@multiplatform.one/keycloak-theme
v7.4.1
Published
The SHC springboard design language as a Keycloakify login skin
Readme
@multiplatform.one/keycloak-theme
The SHC springboard, as a Keycloak login skin.
Keycloak's stock login page has nothing to do with the launcher and console a user lands on immediately after signing in. This package closes that gap: the same navy wallpaper, the same glass plate, the same per-app glyph, on the page that comes first.
What it is
One stylesheet and the values behind it. It renders no Keycloak page, so it
cannot move a form's action, rename a field, drop a hidden input, or
reorder a tab stop — it hooks the PatternFly class names and ids that
keycloakify's own pages already emit. src/pages.spec.tsx renders those
real pages against it and asserts the flow-critical DOM survives.
| Export | What it gives you |
| ---------------------------------------------------------- | ------------------------------------------------------------------ |
| SpringboardStyles | The skin, as a <style> element. Mount it once, high in the tree. |
| isSpringboardTheme | Whether Keycloak served this page from the springboard theme. |
| buildSpringboardCss | The pure CSS builder behind the component. |
| glyphMark, glyphHue, glyphMonogram | The springboard's per-realm glyph, bit-identical to the console's. |
| wallpaperLayers, plate, icon, caption, accent, … | The transcribed design values. |
Using it
import { SpringboardStyles, isSpringboardTheme } from "@multiplatform.one/keycloak-theme";
export default function KcPage({ kcContext }: { kcContext: KcContext }) {
return (
<>
{isSpringboardTheme(kcContext) && <SpringboardStyles realm={kcContext.realm} />}
{/* your pages, untouched */}
</>
);
}apps/keycloak already does this. Its keycloakify build emits two theme
names from one bundle — platform-keycloak (the house look) and
shc-springboard (this skin) — into the same JAR. A realm picks between
them with one field, loginTheme, and can change its mind without a
redeploy.
Where the values come from
Every constant in src/tokens.ts and src/glyph.ts is transcribed by hand
from the SHC console's springboard package:
selfhosted-cloud/shc packages/springboard-ui/src/wallpaper.ts
selfhosted-cloud/shc packages/springboard-ui/src/launcherGeometry.ts
selfhosted-cloud/shc packages/springboard-ui/src/glyph.tswhich are themselves a second copy of dress() in that repo's Traefik
launcher plugin. This is the third writing of the same numbers, and the
duplication is deliberate at every hop — the launcher is hand-written ES5
interpreted by yaegi and can never import a TypeScript module, and a
Keycloak login page is served by Quarkus from a JAR baked into an image in
another repository on another release cadence.
Nothing enforces the copy across repositories. src/tokens.spec.ts pins
every value as a literal instead, so a change here is a deliberate edit to
a failing test and never a silent drift. When the springboard moves:
change shc, change src/tokens.ts, change src/tokens.spec.ts — in that
order.
Deploying it
The skin reaches an estate through the normal train, not through a mount:
apps/keycloakbuilds the theme JAR (pnpm build→keycloakify build).- The JAR lands in the multiplatform Keycloak bundle image.
selfhosted-cloud/shckeycloak/docker/DockerfileFROMs that image by digest and re-augments, so the theme arrives by inheritance.- A realm names it:
shc config set keycloak.login_theme shc-springboard.
Step 4 is opt-in and has no default, deliberately. Keycloak resolves a theme name when it renders, not when it is stored — a name the running image does not carry is a 500 on the login page for every user of that realm, stamped by a converge that reported success.
Tests
pnpm test # vitest, jsdomFive files, ~94 assertions: the transcribed literals, known-answer vectors for the FNV-1a glyph hash, the emitted stylesheet, the theme-name gate, and the real keycloakify pages rendered against mock Keycloak contexts.
