Skip to main content
There is no CSS file to import. The screens inject their own stylesheet once, and everything they draw resolves from --kerne-* custom properties — so you restyle by setting those, not by fighting selectors.
There is no styles.css you can import or override, on purpose — theming is exclusively appearance and the --kerne-* variables below. Under a strict CSP style-src that omits 'unsafe-inline', this runtime injection is blocked and the screens render unstyled. There is no workaround today.

Your colors

Set the properties anywhere in your own CSS to change every screen at once:
Or per instance, with appearance:
primaryColor is usually the only one you need. From it the screens also derive the label on the primary button, its hover, and the accent used for focus rings and links — so a light brand color gets a dark label instead of an unreadable white one, and a branded button no longer turns grey under the cursor. Override any of those with primaryTextColor and accentColor when you want something else. appearance accepts primaryColor, primaryTextColor, accentColor, textColor, borderColor, panelColor, background, surfaceColor, surface, fontFamily, radius, controlHeight, and theme. Color derivation reads #rgb, #rrggbb and rgb(). Anything else — a named color, hsl(), a var() reference — is used as-is and nothing is derived from it, so set primaryTextColor yourself in that case.

Light and dark

The screens are light unless you say otherwise. They do not follow the visitor’s system setting by default: your product decides what it looks like, and a light-only app should not render a dark sign-in form for half its users.
If your app has its own dark mode, pass whichever one is active:
Use 'system' to opt into following the visitor’s OS setting instead.

Standing on their own

The screens ship flat — no card, no border — because they are built to drop into a page you already designed. When the component is the page (a dedicated /login route, a hosted portal), surface: 'card' gives it a container of its own:
Tell it about its background with background whenever the component owns the viewport — that is what lets its text and its ground resolve together instead of independently.

What the screens do on their own

You do not wire any of this — it is what the prebuilt forms already handle:
  • Password fields carry a show/hide toggle, warn when Caps Lock is on, and — where a password is being set — show the tenant’s requirements as a checklist (length by default, plus uppercase/number/symbol if the tenant turned them on) until every one is met, then switch to an advisory strength meter. Only the checklist gates the submit button, and it mirrors the server’s policy exactly; the meter never does, so a password it scores low can’t be rejected client-side when the server would accept it.
  • Errors land on the field they belong to rather than in one banner above the form, so there is nothing to guess at when a submit fails.
  • Buttons show a spinner while a request is in flight, instead of only swapping their label.
  • Outcomes are announced to screen readers — failures assertively, confirmations politely.
  • The first field is focused only where there is a real pointer, so arriving on a phone does not throw the keyboard over the screen.

Language

Every string the screens render — including the error messages — comes from one dictionary. Override what you need on the provider; anything you leave out stays in English.

Error messages

Errors are keyed by the API’s error code, so you control exactly what a user reads when something fails:
Codes you do not map fall back to generic. The raw API message is never shown — it is written for you, the developer, not for your users. See Error codes for the full list.

French

A complete French pack ships as fr — pass it directly, or layer overrides on top of it with mergeLocalization:
No other language ships yet — everything else falls back to English unless you override it yourself.

Starting from the defaults

defaultLocalization is exported if you want to see every key, or build a translation on top of it: