Theming
One accent color generates your entire login-page palette — both light and dark, gradients included — so a brand is a single hex value, not a spreadsheet of tokens. You can override individual stops when you need to, tune the corner radius, and drop in a logo. The generator is deterministic and pure: the same input always produces the same theme, which is what lets the preview flow cache and the MCP show you exactly what will ship.
From one accent to a whole theme
Give the generator an accent hex and it builds two 12-step scales (the accent and a neutral gray faintly tinted toward the accent hue), maps them onto the login page's CSS variables for light and dark, and derives the login-background gradient from three accent steps. It also computes a WCAG contrast (AA) report so you see the accessibility result before applying. Destructive/danger red is a fixed semantic color per mode — it stays "danger" regardless of your brand.
Overrides, radius, and logos
Beyond the accent you can pass a few options:
- Per-stop overrides — hand-tune any of the 15 generated tokens (
--background,--foreground,--primary,--accent,--border,--ring, and friends) plus the login gradient, separately for light and dark. Override values are checked and any pair whose contrast an override touches is re-measured and reported. radius— the corner radius, defaulting to shadcn's0.625rem.logo— light and dark logo URLs, rendered as<img src>on the login page.
Overrides, radius, and logos are validated, because every value lands verbatim in a <style> tag or an <img src>. radius must be a single non-negative CSS length — 0, or a number with a px/rem/em/% unit. calc(), var(), or multiple values are rejected. A logo must be an absolute http(s) URL (in production, https) with no whitespace or HTML metacharacters — javascript:, data:, and relative paths are rejected. An override value may not contain < > ; }. These are injection guards, not style opinions; a value outside the grammar throws rather than shipping.
Previewing and applying from the MCP
Theming is a two-step "preview, then persist" loop driven from your AI tool. Nothing is written until you apply — exact params are in the MCP tool reference.
preview_themerenders a live, signed preview URL of your actual login page with the candidate theme, plus both palettes and the contrast report — and stores nothing. The URL expires after a few minutes.tweak_theme_stoplayers one manual token override onto the current theme and re-previews, for fine-tuning a single stop. It still does not save.apply_themepersists the theme as the issuer's brand;apply_client_themepersists a per-client override. These are the only writes. Applying bumpsconfigVersion, so the change is live on the login pages immediately.
The live preview is served by a signed, unauthenticated /theme-preview route on the IDP, gated on the shared THEME_PREVIEW_SECRET. If that secret is unset in an environment, preview links are disabled (the route 404s and the API refuses to sign one) — applying a theme still works, you just do not get the live preview.
Issuer vs. per-client resolution
A theme can live on the issuer (the default brand for every login page under it) or as a per-client override. At render time the IDP resolves in this order:
- The client's theme, if it has a valid one.
- Otherwise the issuer's theme.
- Otherwise the workspace default accent.
So a client override always wins over the issuer brand — useful when one issuer hosts several apps that each want their own look. Clear an issuer theme back to the default by applying null.