Repository navigation
Phase 3: build the xore/theme-based Keycloak theme to match the current auth-backend #91
Description
Activity
Progress, 2026-08-09: Account Console theme built and merged (#111). `themes/apiary/account/` now exists (previously only `login/` did) -- masthead, sidebar nav, forms, buttons, alerts, tables, and data lists (device activity, applications) all restyled with Xore/theme tokens, verified with real screenshots in both light and dark mode against a disposable Keycloak 26.7.1 instance.
Remaining against this issue's acceptance criteria:
- Email templates (`themes/apiary/email/` doesn't exist yet)
- Upgrade-compatibility check (login theme has one via `.github/scripts/verify-keycloak-compat.sh` against `keycloak.v2`; account console's `keycloak.v3` isn't covered by anything yet)
- Dedicated automated test coverage for the account theme (currently CSS + manual screenshot verification only, not a `login.spec.ts`-depth regression suite)
- APIARY brand mark for the account console masthead (currently still Keycloak's own logo -- login theme sidesteps this via realm displayName text branding, which account.v3 has no equivalent slot for)
Queued as parallel work (2026-08-09) -- splitting the remaining 0.1.0 gate items across a few agents working concurrently. Claiming the remaining 4 items from the last status comment, no action taken yet:
themes/apiary/email/templates (doesn't exist yet).- Upgrade-compatibility check for the account console's
keycloak.v3base -- the login theme has one via.github/scripts/verify-keycloak-compat.shagainstkeycloak.v2, account console has no equivalent. - Automated regression coverage for the account theme (currently CSS + manual screenshot verification only).
- APIARY brand mark for the account console masthead -- currently still shows Keycloak's own logo; the login theme's realm-
displayNametext-branding trick has no equivalent slot inaccount.v3.
Will post real evidence back to this issue as each lands. Flagging here so anyone else picking up 0.1.0 gate work doesn't duplicate this specific slice.
Closing out #91's last remaining scope (the account console + email theme surfaces from "Theme surfaces" above, both conditional -- "if part of the approved product surface" / "where branding is required" -- and both now real):
Account console theme (themes/apiary/account/, PR #112): keycloak.v3's account console is a compiled React SPA (PatternFly v5), not FreeMarker like login -- CSS-only, plus a masthead brand-mark swap via theme.properties' logo=. Found and fixed while building test coverage: a nested .pf-v5-c-toolbar painting solid black over the masthead regardless of theme, invisible white user-menu text in light mode, a dead #root selector (real mount point is #app), a dead .pf-v5-c-table rule block (this Keycloak version never renders a real Table here), and a real realm-JSON-import gap where imported users got zero role mappings (breaking every Account Console API call with 401s; production is unaffected since it doesn't create users via import). test/specs/account.spec.ts (13 tests) covers Personal info/Account security/Applications/masthead/mobile nav plus a DOM-hook selector-coverage scan standing in for #101's file-hash approach, which doesn't apply to a compiled SPA with no individually-hashable templates.
Email theme (themes/apiary/email/, this session): overrides base/email's own html/template.ftl -- confirmed it's completely bare upstream (no header, no branding, no styling at all) for this release. Table-based layout, inline styles (email clients don't reliably support linked/<style>-block CSS), real APIARY lockup mark via url.resourcesUrl, guarded for the real case where url is absent (ContextNotActiveException for emails sent outside an active request). keycloak.lock/verify-keycloak-compat.sh extended with [email_upstream_files] pinning that file's hash. test/specs/email.spec.ts triggers a real email through the disposable Keycloak + mailhog stack and inspects the actual sent MIME message.
Also found and fixed getting these two PRs' CI actually green (it hadn't been checked before this): account.spec.ts's tests were only ever written/verified against the desktop-1440 project, but npx playwright test with no filter runs every spec across all 6 viewport projects -- most of account.spec.ts doesn't need that (only "Personal info" opens its own explicit-viewport contexts), and running it anyway hit real narrow-viewport gaps (nav collapses behind a hamburger these tests didn't drive, plus a genuine button-name a11y violation at mobile-390/iphone-393) that blew the regression job's 15-minute CI budget via 30s timeouts. Scoped both account.spec.ts and the new email.spec.ts to desktop-1440; filed #113 for the real narrow-viewport coverage as its own deliberate piece of work.
Acceptance criteria checklist above updated. Two items left honestly unchecked rather than rubber-stamped:
- "Approved screenshot comparisons" -- baselines exist and are diffed in CI across all 6 tiers, but "approved" is a human design sign-off, not something this session can self-certify.
- "hover/disabled/loading" states in the DOM regression coverage item -- default/validation/error/success are covered thoroughly; hover/disabled/loading are not yet exercised. Filed as Theme: add hover/focus/disabled/loading state regression coverage (#91 follow-up) #114 rather than silently left off the list.
All of #98-#107 are closed/merged. Full local regression suite (login + account + email, all 6 projects, fresh disposable Keycloak instance): 114 passed, 90 skipped (documented, not silent), 0 failed.
Closing this epic; #113 and #114 track the two real, scoped gaps found while closing it out.
Scope
Reproduce the current auth-backend look and feel as a supported Keycloak theme built from the vendored
xore/themedesign system. The Keycloak experience should feel like the same product even though the authentication runtime changes completely.The theme is the only bespoke user-facing authentication implementation retained from this repository.
xore/themeremains the source of truth for tokens, components, assets, and styling; do not add a parallel custom CSS system outside it.Visual source of truth
xore/theme; add reusable theme capabilities there when a required primitive is missing.Theme surfaces
Constraints
xore/theme; no standalone dashboard/auth CSS fork or copied one-off design tokens.Acceptance criteria
xore/theme; no parallel custom CSS system is introduced.Depends on Phases 0 and 2.
Research decomposition (2026-08-09)
The official Keycloak guidance and current upstream theme sources support a CSS-first child theme that extends
keycloak.v2, keeps inherited FreeMarker templates, and overrides templates only when the required semantic structure cannot be achieved safely with styles. The Xore auth example is the visual source of truth, not a functional form implementation.Important findings:
stylesdeclaration replaces the parent list. The parentcss/styles.cssmust therefore be explicitly loaded before Xore and APIARY adapters.main.Implementation children:
keycloak.v2functional stylesheet before Xore overridesResearch sources:
Full-fidelity interaction amendment (2026-08-09)
Custom local JavaScript and targeted FreeMarker overrides are explicitly allowed and expected when required to reproduce the Xore authentication experience. The implementation must not stop at recoloring inherited PatternFly markup.
Fidelity requirements include staged identity-to-credential presentation, Xore light/dark/system behavior, focus and keyboard handling, ARIA state, reduced-motion-aware transitions, submitting/disabled feedback, alternate passkey/IdP actions, and visual continuity across server-rendered required actions.
CSS-first remains an upgrade-risk preference, not a product-fidelity ceiling. Override templates when semantic markup or stable JavaScript hooks require it, while preserving all Keycloak form actions, hidden fields, built-in scripts, WebAuthn hooks, server messages, and security behavior. JavaScript must be local, pinned, CSP-compatible, progressively enhanced, and unable to change authentication decisions.
Historical production reference (2026-08-09)
The authoritative visual and interaction reference is now the old production login at commit
3c2779a8ca03f9d6df6ba5bba0a263131a514c2b, not the generic split-screen Xore example:The old page's centered brand, compact 384px card, accent primary actions, fixed theme toggle, staged identity/credential interaction, SSO/passkey/recovery ordering, and quiet footer define parity. Current Xore tokens and approved APIARY branding may update colors/assets/content, but must not replace that composition with the generic artwork split layout.
The retired Go form token, honeypot, passkey endpoints, trusted-device policy, and inline credential ceremony are behavioral history only and must not be reimplemented. Keycloak remains authoritative for every authentication and protocol decision.