diff --git a/README.md b/README.md index 680f549..35c2be6 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,15 @@ deploy secrets. 5. Users of the Traefik dashboard need **Email verified** switched on, because oauth2-proxy refuses unverified addresses. +6. **Branding (optional):** the sign-in and sign-out pages, theme `petrolyst` in `keycloak/themes/`, for the whole realm, both clients. + - Build on the Mac: `keycloak/themes/build.sh `, normally `petrolyst-platform/web/src/fonts/satoshi`. + The font goes only into `dist/petrolyst-theme.jar`, never into Git, because its licence forbids distributing it: so build it yourself and do not hand the file to outsiders. + - Install on Keycloak's server, either way. **No restart:** put the jar's `theme/petrolyst/` folder into Keycloak's `themes/` folder, readable by Keycloak's + user. Keycloak listed and used it without a restart on a test 26.4 with theme caching on; not tried on the real one. **With a restart,** which drops sign-in for every + realm for a moment: the jar into `providers/`, and `kc.sh build` if it runs an optimised build. + - Realm `petrolyst` → Realm settings → Themes → Login theme → `petrolyst` → Save. Other realms keep their look. To undo, set it back. + - `keycloak/themes/check-brand.sh` says whether the theme's copies of the brand tokens still match `Petrolyst/design/`. + ### 3. Server (as root, `ssh petrolyst`) The server needs only the two host scripts; it never gets Gitea credentials. diff --git a/keycloak/themes/.gitignore b/keycloak/themes/.gitignore new file mode 100644 index 0000000..8476e74 --- /dev/null +++ b/keycloak/themes/.gitignore @@ -0,0 +1,4 @@ +# Built by build.sh +dist/ +# Satoshi: licence forbids committing it (build.sh) +petrolyst/login/resources/fonts/Satoshi-* diff --git a/keycloak/themes/build.sh b/keycloak/themes/build.sh new file mode 100755 index 0000000..6b59b15 --- /dev/null +++ b/keycloak/themes/build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Build dist/petrolyst-theme.jar, ready for Keycloak's providers/ folder. Pass the folder holding Satoshi-Variable.woff2 (normally +# petrolyst-platform/web/src/fonts/satoshi, fetched there from Fontshare). The font goes only into the built file, never into Git: +# its licence forbids distributing it. Without it, the pages fall back to Inter. +set -euo pipefail +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SATOSHI="${1:-}" +WORK="$(mktemp -d "${TMPDIR:-/tmp}/petrolyst-theme.XXXXXX")"; trap 'rm -rf "$WORK"' EXIT +mkdir -p "$WORK/META-INF" "$WORK/theme" +cp -R "$HERE/petrolyst" "$WORK/theme/petrolyst" +find "$WORK" -name .DS_Store -delete +if [ -n "$SATOSHI" ]; then cp "$SATOSHI/Satoshi-Variable.woff2" "$WORK/theme/petrolyst/login/resources/fonts/"; else echo "no Satoshi folder given: headings fall back to Inter"; fi +printf '{"themes":[{"name":"petrolyst","types":["login"]}]}\n' > "$WORK/META-INF/keycloak-themes.json" +mkdir -p "$HERE/dist"; rm -f "$HERE/dist/petrolyst-theme.jar" +(cd "$WORK" && zip -qr "$HERE/dist/petrolyst-theme.jar" META-INF theme) +echo "built $HERE/dist/petrolyst-theme.jar" diff --git a/keycloak/themes/check-brand.sh b/keycloak/themes/check-brand.sh new file mode 100755 index 0000000..a34c1de --- /dev/null +++ b/keycloak/themes/check-brand.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# The theme's copies of the brand tokens match their home, Petrolyst/design/. Says "not run" in a lone clone of this repository. +set -u +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE="$HERE/../../../design" +COPIES="$HERE/petrolyst/login/resources/css/brand" +[ -d "$SOURCE" ] || { echo "not run: no Petrolyst/design/ next to this repository"; exit 0; } +status=0 +for pair in brand/07_UI_Tokens/petrolyst_tokens.css identity_layer/02_Tokens/product_identity_layer.css; do + name="$(basename "$pair")" + if cmp -s "$SOURCE/$pair" "$COPIES/$name"; then echo "same $name"; else echo "DIFFERS $name"; status=1; fi +done +exit $status diff --git a/keycloak/themes/petrolyst/login/info.ftl b/keycloak/themes/petrolyst/login/info.ftl new file mode 100644 index 0000000..0f1b4a2 --- /dev/null +++ b/keycloak/themes/petrolyst/login/info.ftl @@ -0,0 +1,24 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayMessage=false; section> + <#if section = "header"> + <#if messageHeader??> + ${kcSanitize(msg("${messageHeader}"))?no_esc} + <#else> + ${message.summary} + + <#elseif section = "form"> +
+

<#if messageHeader??>${message.summary}<#if requiredActions??><#list requiredActions>: <#items as reqActionItem>${kcSanitize(msg("requiredAction.${reqActionItem}"))?no_esc}<#sep>, <#else>

+ <#if skipLink??> + <#else> + <#if pageRedirectUri?has_content> +

${kcSanitize(msg("backToApplication"))?no_esc}

+ <#elseif actionUri?has_content> +

${kcSanitize(msg("proceedWithAction"))?no_esc}

+ <#elseif (client.baseUrl)?has_content> +

${kcSanitize(msg("backToApplication"))?no_esc}

+ + +
+ + \ No newline at end of file diff --git a/keycloak/themes/petrolyst/login/messages/messages_en.properties b/keycloak/themes/petrolyst/login/messages/messages_en.properties new file mode 100644 index 0000000..77f39cb --- /dev/null +++ b/keycloak/themes/petrolyst/login/messages/messages_en.properties @@ -0,0 +1,7 @@ +loginTitle=Sign in to Petrolyst +loginTitleHtml= +loginAccountTitle=Sign in to Petrolyst +doLogIn=Sign in +logoutConfirmTitle=Log out of Petrolyst +logoutConfirmHeader=Do you want to log out? +doLogout=Log out diff --git a/keycloak/themes/petrolyst/login/resources/css/brand/petrolyst_tokens.css b/keycloak/themes/petrolyst/login/resources/css/brand/petrolyst_tokens.css new file mode 100644 index 0000000..3a51c7f --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/css/brand/petrolyst_tokens.css @@ -0,0 +1,190 @@ +:root { + --brand-navy: #0B1D33; + --brand-teal: #009688; + --brand-cyan: #00B6E6; + --brand-sand: #F2EBDD; + --brand-graphite: #3F4752; + /* PD-26: the Etihad-inspired restyle. Pearl carries tags and avatars; gold is an editorial + accent only, never on controls and never carrying state. */ + --brand-gold: #AA8951; + --brand-pearl: #EBE2D2; + + /* The chrome: the dark slate shell (header, side menus, hero bands). The same in light and + dark mode, like the reference design; only the content below it follows the device. */ + --surface-chrome: #2A3945; + --surface-chrome-deep: #1F2A33; + --surface-chrome-active: #334654; + --border-chrome: #4D5562; + --text-on-chrome: #F8FAFC; + --text-on-chrome-muted: #B7C1C9; + + --surface-page: #FFFFFF; + --surface-subtle: #F9FAFB; + --surface-card: #FFFFFF; + --text-primary: #0B1D33; + --text-secondary: #475569; + --text-muted: #64748B; + --border-default: #E3E7EA; + --border-strong: #C9D0D6; + + /* Actions wear the logo's green (PD-26, revised on the owner's word: the Y's colour is the + brand's one strong colour, so the controls echo it). Gold stays an editorial accent only. */ + --action-primary: #00695C; + --action-primary-foreground: #FFFFFF; + --action-primary-hover: #005B50; + --action-primary-active: #004D44; + --action-link: #00695C; + + --focus-ring-inner: #FFFFFF; + --focus-ring-outer: #0B1D33; + --focus-ring-inner-width: 2px; + --focus-ring-outer-width: 2px; + --focus-ring-offset: 2px; + + --status-normal: #0F7B6C; + --status-normal-foreground: #FFFFFF; + --status-live: #007C91; + --status-live-foreground: #FFFFFF; + --status-warning: #B45309; + --status-warning-foreground: #FFFFFF; + --status-critical: #B3261E; + --status-critical-foreground: #FFFFFF; + --status-offline: #52606D; + --status-offline-foreground: #FFFFFF; + --status-stale: #7C8796; + --status-stale-foreground: #0B1D33; + --status-info: #2563EB; + --status-info-foreground: #FFFFFF; + + --font-display: "Satoshi", "Inter", system-ui, sans-serif; + --font-ui: "Inter", system-ui, sans-serif; + + --text-display-xl: 64px; + --text-h1: 48px; + --text-h2: 36px; + --text-h3: 28px; + --text-body-lg: 20px; + --text-body: 16px; + --text-ui: 14px; + --text-caption: 12px; + + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-6: 24px; + --space-8: 32px; + --space-12: 48px; + --space-16: 64px; + --space-24: 96px; + + /* PD-26 shapes: large soft cards, small chips, pill buttons. */ + --radius-sm: 8px; + --radius-md: 16px; + --radius-lg: 20px; + --radius-pill: 999px; + + --shadow-card: 0 2px 4px rgb(11 29 51 / 0.04), 0 12px 28px -8px rgb(11 29 51 / 0.12); + --shadow-pop: 0 6px 16px rgb(11 29 51 / 0.12), 0 20px 48px -12px rgb(11 29 51 / 0.28); +} + +[data-theme="dark"] { + --surface-page: #1F2A33; + --surface-subtle: #243038; + --surface-card: #2A3945; + --text-primary: #F8FAFC; + --text-secondary: #C7CFD6; + --text-muted: #9AA6AF; + --border-default: #3C4854; + --border-strong: #76828E; + + --action-primary: #35C5B6; + --action-primary-foreground: #0B1D33; + --action-primary-hover: #4CD3C4; + --action-primary-active: #2DB4A6; + --action-link: #35C5B6; + + --focus-ring-inner: #1F2A33; + --focus-ring-outer: #F8FAFC; + + --status-normal: #35C5B6; + --status-normal-foreground: #0B1D33; + --status-live: #48CFF2; + --status-live-foreground: #0B1D33; + --status-warning: #F59E0B; + --status-warning-foreground: #0B1D33; + --status-critical: #FF6B6B; + --status-critical-foreground: #0B1D33; + --status-offline: #94A3B8; + --status-offline-foreground: #0B1D33; + --status-stale: #CBD5E1; + --status-stale-foreground: #0B1D33; + --status-info: #60A5FA; + --status-info-foreground: #0B1D33; + + --shadow-card: 0 2px 4px rgb(0 0 0 / 0.2), 0 12px 28px -8px rgb(0 0 0 / 0.4); + --shadow-pop: 0 6px 16px rgb(0 0 0 / 0.35), 0 20px 48px -12px rgb(0 0 0 / 0.55); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --surface-page: #1F2A33; + --surface-subtle: #243038; + --surface-card: #2A3945; + --text-primary: #F8FAFC; + --text-secondary: #C7CFD6; + --text-muted: #9AA6AF; + --border-default: #3C4854; + --border-strong: #76828E; + + --action-primary: #35C5B6; + --action-primary-foreground: #0B1D33; + --action-primary-hover: #4CD3C4; + --action-primary-active: #2DB4A6; + --action-link: #35C5B6; + + --focus-ring-inner: #1F2A33; + --focus-ring-outer: #F8FAFC; + + --status-normal: #35C5B6; + --status-normal-foreground: #0B1D33; + --status-live: #48CFF2; + --status-live-foreground: #0B1D33; + --status-warning: #F59E0B; + --status-warning-foreground: #0B1D33; + --status-critical: #FF6B6B; + --status-critical-foreground: #0B1D33; + --status-offline: #94A3B8; + --status-offline-foreground: #0B1D33; + --status-stale: #CBD5E1; + --status-stale-foreground: #0B1D33; + --status-info: #60A5FA; + --status-info-foreground: #0B1D33; + + --shadow-card: 0 2px 4px rgb(0 0 0 / 0.2), 0 12px 28px -8px rgb(0 0 0 / 0.4); + --shadow-pop: 0 6px 16px rgb(0 0 0 / 0.35), 0 20px 48px -12px rgb(0 0 0 / 0.55); + } +} + +/* Recommended primary action implementation */ +.petrolyst-action-primary { + background: var(--action-primary); + color: var(--action-primary-foreground); + border: 1px solid var(--action-primary); +} + +.petrolyst-action-primary:hover { + background: var(--action-primary-hover); + border-color: var(--action-primary-hover); +} + +.petrolyst-action-primary:active { + background: var(--action-primary-active); + border-color: var(--action-primary-active); +} + +.petrolyst-action-primary:focus-visible { + outline: var(--focus-ring-outer-width) solid var(--focus-ring-outer); + outline-offset: calc(var(--focus-ring-offset) + var(--focus-ring-inner-width)); + box-shadow: 0 0 0 var(--focus-ring-inner-width) var(--focus-ring-inner); +} diff --git a/keycloak/themes/petrolyst/login/resources/css/brand/product_identity_layer.css b/keycloak/themes/petrolyst/login/resources/css/brand/product_identity_layer.css new file mode 100644 index 0000000..d616ea5 --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/css/brand/product_identity_layer.css @@ -0,0 +1,112 @@ +:root { + --petroleum-50:#F7F9FA; --petroleum-100:#EEF2F4; --petroleum-200:#DDE5E9; + --petroleum-300:#C3D0D7; --petroleum-400:#96A9B4; --petroleum-500:#6C818E; + --petroleum-600:#506572; --petroleum-700:#394E5C; --petroleum-800:#233746; + --petroleum-900:#132636; --petroleum-950:#0A1825; + + --surface-live:#FFFFFF; + --surface-live-alt:var(--petroleum-50); + --surface-context:#F2EBDD; + --surface-report:#F7F1E8; + --surface-empty:#F5EFE4; + + /* Active semantic override of the base Brand System */ + --surface-subtle:var(--petroleum-50); + --text-secondary:var(--petroleum-600); + --text-muted:var(--petroleum-600); + --border-default:var(--petroleum-200); + --border-subtle:var(--petroleum-200); + --border-control:var(--petroleum-500); + --border-strong:var(--petroleum-500); + + --numeric-family:"Inter",system-ui,sans-serif; + --numeric-weight:600; + --numeric-unit-weight:500; + --numeric-unit-color:var(--petroleum-600); + --numeric-value-unit-ratio:.46; + + --product-icon-color:#0B1D33; + --product-icon-live:#007C91; + --product-icon-action:#009688; + --product-icon-warning:#B45309; + --product-icon-critical:#B3261E; + --product-icon-offline:#52606D; + --product-icon-info:#2563EB; + + --y-branch-angle-from-vertical:35deg; + --y-divider-angle:12deg; +} + +[data-theme="dark"] { + --surface-live:#2A3945; + --surface-live-alt:#243038; + --surface-context:#242A2F; + --surface-report:#20272D; + --surface-empty:#1C242B; + + --surface-subtle:#1F2A33; + --text-secondary:#C7CFD6; + --text-muted:#9AA6AF; + --border-default:#3C4854; + --border-subtle:#3C4854; + --border-control:#76828E; + --border-strong:#8B96A1; + + --numeric-unit-color:#C7CFD6; + + --product-icon-color:#F8FAFC; + --product-icon-live:#48CFF2; + --product-icon-action:#35C5B6; + --product-icon-warning:#F59E0B; + --product-icon-critical:#FF6B6B; + --product-icon-offline:#94A3B8; + --product-icon-info:#60A5FA; +} + +@media (prefers-color-scheme:dark) { + :root:not([data-theme="light"]) { + --surface-live:#2A3945; + --surface-live-alt:#243038; + --surface-context:#242A2F; + --surface-report:#20272D; + --surface-empty:#1C242B; + --surface-subtle:#1F2A33; + --text-secondary:#C7CFD6; + --text-muted:#9AA6AF; + --border-default:#3C4854; + --border-subtle:#3C4854; + --border-control:#76828E; + --border-strong:#8B96A1; + --numeric-unit-color:#C7CFD6; + --product-icon-color:#F8FAFC; + --product-icon-live:#48CFF2; + --product-icon-action:#35C5B6; + --product-icon-warning:#F59E0B; + --product-icon-critical:#FF6B6B; + --product-icon-offline:#94A3B8; + --product-icon-info:#60A5FA; + } +} + +.petrolyst-reading { + display:inline-flex; align-items:baseline; gap:.14em; + font-family:var(--numeric-family); + font-variant-numeric:tabular-nums slashed-zero; + font-feature-settings:"tnum" 1,"zero" 1; +} +.petrolyst-reading__value {font-weight:var(--numeric-weight);letter-spacing:-.025em;line-height:.95;} +.petrolyst-reading__unit { + font-size:calc(1em * var(--numeric-value-unit-ratio)); + font-weight:var(--numeric-unit-weight); color:var(--numeric-unit-color); +} +.petrolyst-surface--context {background:var(--surface-context);} +.petrolyst-surface--live {background:var(--surface-live);} +.petrolyst-divider--y {transform:skewY(var(--y-divider-angle)); transform-origin:left center;} + +/* Functional controls require stronger non-text contrast than decorative dividers. */ +.petrolyst-control { + border: 1px solid var(--border-control); +} +.petrolyst-divider { + border-color: var(--border-subtle); +} diff --git a/keycloak/themes/petrolyst/login/resources/css/petrolyst.css b/keycloak/themes/petrolyst/login/resources/css/petrolyst.css new file mode 100644 index 0000000..f29acc8 --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/css/petrolyst.css @@ -0,0 +1,237 @@ +/* Petrolyst's sign-in pages. Colours and type come from the brand tokens (css/brand/), light and dark; the form sits on a soft card + beside the dark slate shell, which is the same in both (PD-26), as on the website and in the app. */ + +@font-face { + font-family: "Inter"; + src: url("../fonts/inter-latin-wght-normal.woff2") format("woff2"); + font-weight: 100 900; + font-display: swap; +} +/* Fetched at build time and never committed: Satoshi's licence forbids distributing the files (build.sh). Without it, Inter takes over. */ +@font-face { + font-family: "Satoshi"; + src: url("../fonts/Satoshi-Variable.woff2") format("woff2"); + font-weight: 300 900; + font-display: swap; +} + +* { box-sizing: border-box; } + +html { color-scheme: light; } + +body { + margin: 0; + min-height: 100vh; + background: var(--surface-subtle); + color: var(--text-primary); + font-family: var(--font-ui); + font-size: var(--text-body); + line-height: 1.5; +} + +.pl-page { + min-height: 100vh; + display: flex; + flex-direction: column; + width: 100%; + max-width: 448px; + margin: 0 auto; + padding: 0 var(--space-6, 24px) var(--space-8, 32px); +} + +/* The logo; the realm's name is left out (messages: loginTitleHtml). On a phone a slate band, like the website's header; wide, the panel holds it. */ +.pl-header { + display: flex; + align-items: center; + height: 64px; + margin: 0 calc(var(--space-6, 24px) * -1) var(--space-8, 32px); + padding: 0 var(--space-6, 24px); + background: var(--surface-chrome); + border-bottom: 1px solid var(--border-chrome); +} +#kc-header-wrapper { + width: 160px; + height: 22px; + background: url("../img/petrolyst_wordmark_white_y.svg") left center / contain no-repeat; +} + +.pl-card { + padding: var(--space-6, 24px); + background: var(--surface-card); + border-radius: var(--radius-md); + box-shadow: var(--shadow-card); +} + +#kc-page-title { + margin: 0 0 var(--space-6, 24px); + font-family: var(--font-display); + font-size: var(--text-h3); + font-weight: 400; + line-height: 1.2; +} + +/* --- The form --- */ +.pl-group { margin-bottom: var(--space-5, 20px); } +.pl-label, +#kc-content label { + display: block; + margin-bottom: var(--space-2, 8px); + font-size: var(--text-ui); + font-weight: 600; +} +.pl-input, +#kc-content input[type="text"], +#kc-content input[type="password"], +#kc-content input[type="email"] { + width: 100%; + min-height: 44px; + padding: 10px 14px; + border: 1px solid var(--border-control); + border-radius: var(--radius-sm); + background: var(--surface-card); + color: var(--text-primary); + font: inherit; +} +.pl-input:focus, +#kc-content input:focus-visible, +.pl-eye:focus-visible, +.pl-btn:focus-visible, +#kc-content a:focus-visible { + outline: 2px solid var(--action-primary); + outline-offset: 2px; +} +.pl-input[aria-invalid="true"] { border-color: var(--status-critical); } + +.pl-input-group { position: relative; } +.pl-input-group .pl-input { padding-right: 48px; } +.pl-eye { + position: absolute; + top: 0; + right: 0; + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + border: 0; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text-secondary); + cursor: pointer; +} +.pl-eye:hover { color: var(--text-primary); } + +.pl-icon { + display: inline-block; + width: 18px; + height: 18px; + background: currentColor; + -webkit-mask: var(--pl-icon) center / contain no-repeat; + mask: var(--pl-icon) center / contain no-repeat; +} +.pl-icon-show { --pl-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E"); } +.pl-icon-hide { --pl-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.9 4.2A10 10 0 0 1 12 4c6.5 0 10 8 10 8a17 17 0 0 1-2.2 3.3M6.6 6.6A17 17 0 0 0 2 12s3.5 8 10 8a10 10 0 0 0 5.4-1.6M2 2l20 20M9.9 9.9a3 3 0 0 0 4.2 4.2'/%3E%3C/svg%3E"); } +.pl-icon-restart { --pl-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12a9 9 0 1 0 3-6.7L3 8'/%3E%3Cpath d='M3 3v5h5'/%3E%3C/svg%3E"); } + +.pl-settings { margin-bottom: 0; font-size: var(--text-ui); } +#kc-form-buttons { margin: var(--space-5, 20px) 0 0; } +.pl-settings a, +#kc-content a { + color: var(--action-primary); + font-weight: 600; + text-decoration: none; +} +#kc-content a:hover { text-decoration: underline; } +#kc-form-options .checkbox label { display: inline-flex; align-items: center; gap: var(--space-2, 8px); font-weight: 400; margin: 0; } + +.pl-btn, +#kc-content input[type="submit"] { + min-height: 44px; + padding: 10px 24px; + border: 1px solid var(--border-control); + border-radius: var(--radius-pill); + background: transparent; + color: var(--text-primary); + font: inherit; + font-size: var(--text-ui); + font-weight: 600; + cursor: pointer; +} +.pl-btn-primary, +#kc-content input[type="submit"].pl-btn-primary { + border-color: var(--action-primary); + background: var(--action-primary); + color: var(--action-primary-foreground); +} +.pl-btn-primary:hover { background: var(--action-primary-hover); border-color: var(--action-primary-hover); } +.pl-btn-primary:active { background: var(--action-primary-active); border-color: var(--action-primary-active); } +.pl-btn-primary:disabled { opacity: 0.6; cursor: default; } +.pl-btn-block { width: 100%; } + +/* --- Messages --- */ +.pl-alert { + display: flex; + gap: var(--space-2, 8px); + margin-bottom: var(--space-5, 20px); + padding: 12px 14px; + border: 1px solid var(--border-default); + border-left-width: 3px; + border-radius: var(--radius-sm); + background: var(--surface-subtle); + font-size: var(--text-ui); +} +.pl-alert.alert-error { border-left-color: var(--status-critical); } +.pl-alert.alert-warning { border-left-color: var(--status-warning, var(--border-strong)); } +.pl-alert.alert-success { border-left-color: var(--action-primary); } +.pl-alert.alert-info { border-left-color: var(--action-primary); } +.pl-field-error { + display: block; + margin-top: var(--space-2, 8px); + color: var(--status-critical); + font-size: var(--text-ui); +} +.instruction, +#kc-content p { color: var(--text-secondary); margin: 0 0 var(--space-5, 20px); } +#kc-content p:last-child { margin-bottom: 0; } +#kc-content ol, #kc-content ul { color: var(--text-secondary); padding-left: 20px; } +#kc-content li { margin-bottom: var(--space-3, 12px); } +#kc-content ul ul { margin-top: var(--space-2, 8px); } + +/* The user's name on later steps, with the way back to the start. */ +#kc-username { display: flex; align-items: center; justify-content: space-between; margin: 0 0 var(--space-5, 20px); font-size: var(--text-ui); font-weight: 600; } +#kc-attempted-username { margin: 0; } +#reset-login { display: inline-flex; color: var(--text-secondary); } +.kc-tooltip-text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } +#kc-info { margin-top: var(--space-5, 20px); font-size: var(--text-ui); color: var(--text-secondary); } +.subtitle { font-size: var(--text-caption); color: var(--text-muted); } +.required { color: var(--status-critical); } +.clearfix { clear: both; } + +/* --- Dark --- */ +@media (prefers-color-scheme: dark) { + html { color-scheme: dark; } +} + +/* --- Wide: the slate panel with the logo, the form on its card beside it. The logo appears once, so the form's own is hidden. --- */ +@media (min-width: 64rem) { + body::before { + content: ""; + position: fixed; + inset: 0 50% 0 0; + background: + url("../img/petrolyst_logo_white_y.svg") center / clamp(300px, 30vw, 480px) auto no-repeat, + radial-gradient(ellipse 34vw 40vw at 50% 50%, color-mix(in srgb, var(--petroleum-500) 45%, transparent) 0%, color-mix(in srgb, var(--petroleum-500) 20%, transparent) 45%, transparent 100%), + var(--surface-chrome); + } + .pl-page { + position: relative; + z-index: 1; + justify-content: center; + width: 50%; + max-width: none; + margin: 0 0 0 50%; + padding: var(--space-8, 32px) clamp(48px, 7vw, 128px); + } + .pl-header { display: none; } + .pl-card { padding: var(--space-8, 32px); } +} diff --git a/keycloak/themes/petrolyst/login/resources/fonts/LICENSE-Inter.txt b/keycloak/themes/petrolyst/login/resources/fonts/LICENSE-Inter.txt new file mode 100644 index 0000000..40589da --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/fonts/LICENSE-Inter.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) Inter-Italic[opsz,wght].ttf: Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/keycloak/themes/petrolyst/login/resources/fonts/inter-latin-wght-normal.woff2 b/keycloak/themes/petrolyst/login/resources/fonts/inter-latin-wght-normal.woff2 new file mode 100644 index 0000000..d15208d Binary files /dev/null and b/keycloak/themes/petrolyst/login/resources/fonts/inter-latin-wght-normal.woff2 differ diff --git a/keycloak/themes/petrolyst/login/resources/img/favicon.ico b/keycloak/themes/petrolyst/login/resources/img/favicon.ico new file mode 100644 index 0000000..403f809 Binary files /dev/null and b/keycloak/themes/petrolyst/login/resources/img/favicon.ico differ diff --git a/keycloak/themes/petrolyst/login/resources/img/petrolyst_logo_standard_white_no_tagline.svg b/keycloak/themes/petrolyst/login/resources/img/petrolyst_logo_standard_white_no_tagline.svg new file mode 100644 index 0000000..f6a8321 --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/img/petrolyst_logo_standard_white_no_tagline.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/keycloak/themes/petrolyst/login/resources/img/petrolyst_logo_white_y.svg b/keycloak/themes/petrolyst/login/resources/img/petrolyst_logo_white_y.svg new file mode 100644 index 0000000..43c0200 --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/img/petrolyst_logo_white_y.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/keycloak/themes/petrolyst/login/resources/img/petrolyst_wordmark_white_y.svg b/keycloak/themes/petrolyst/login/resources/img/petrolyst_wordmark_white_y.svg new file mode 100644 index 0000000..926fdf8 --- /dev/null +++ b/keycloak/themes/petrolyst/login/resources/img/petrolyst_wordmark_white_y.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/keycloak/themes/petrolyst/login/theme.properties b/keycloak/themes/petrolyst/login/theme.properties new file mode 100644 index 0000000..01bdc66 --- /dev/null +++ b/keycloak/themes/petrolyst/login/theme.properties @@ -0,0 +1,26 @@ +# Petrolyst's sign-in and sign-out pages, on Keycloak's unstyled base theme: only our own stylesheet, so a Keycloak upgrade cannot +# restyle them. The brand's home is Petrolyst/design/; css/brand/ holds copies (../check-brand.sh). +parent=base +locales=en +meta=viewport==width=device-width,initial-scale=1 +styles=css/brand/petrolyst_tokens.css css/brand/product_identity_layer.css css/petrolyst.css + +kcLoginClass=pl-page +kcHeaderClass=pl-header +kcFormCardClass=pl-card +kcFormGroupClass=pl-group +kcLabelClass=pl-label +kcInputClass=pl-input +kcInputGroup=pl-input-group +kcInputErrorMessageClass=pl-field-error +kcAlertClass=pl-alert +kcAlertTitleClass=pl-alert-text +kcButtonClass=pl-btn +kcButtonPrimaryClass=pl-btn-primary +kcButtonDefaultClass=pl-btn-secondary +kcButtonBlockClass=pl-btn-block +kcFormPasswordVisibilityButtonClass=pl-eye +kcFormPasswordVisibilityIconShow=pl-icon pl-icon-show +kcFormPasswordVisibilityIconHide=pl-icon pl-icon-hide +kcResetFlowIcon=pl-icon pl-icon-restart +kcFormSettingClass=pl-settings