Theming
Twenty-three tokens, twenty-two CSS variables, and the isolation rule that makes them the only way in.
Pass a partial theme to any widget, or once to StableGenieProvider:
import { StableGenieExchange } from "@glacislabs/stablegenie-zerodelta";
<StableGenieExchange
theme={{ mode: "dark", accent: "#8c9bff", accentText: "#0b0d12", radius: "16px" }}
/>resolveTheme() picks darkTheme when mode === "dark" and lightTheme otherwise, then spreads your overrides on top. You only name what you want to change.
The
themeprop is the only way in. Tokens are emitted as CSS custom properties written inline on the widget's own root element, never on:root. Page CSS cannot leak into the widget and the widget cannot leak out. A host stylesheet targeting the widget's internal class names is unsupported and will break without warning.
Tokens
StableGenieTheme is flat — 23 fields, all optional when you pass a partial.
Colour
accent
--sg-accent
#4358e1
#4358e1
accentText
--sg-accent-text
#ffffff
#ffffff
bg
--sg-bg
#ffffff
#0e1115
surface
--sg-surface
#f7f8fe
#1b1d22
border
--sg-border
#e8ebfb
#343541
text
--sg-text
#2c2d34
#ffffff
textMuted
--sg-text-muted
#959699
#87888a
info
--sg-info
same as accent
#8c9bff
danger
--sg-danger
#df1b41
#ff5470
success
--sg-success
#2abd7d
#2abd7d
What each colour paints:
bg— the card, and the controls that sit on a tinted pane (the token button, the percent chips, the swap button). In dark mode those controls aresurfacelifted 8 % towardinfo, so they still read as raised.surface— the tinted You pay / You receive panes and the From / To tiles.border— the ring around the detail rows (Compliance, Quote, the rate box) and the wallet chip.text— every heading and value. Labels and the greyed decimals of an amount are tone steps of this one colour (90, 80, 60 and 35 %), so a host names it once.textMuted— secondary lines: the chain under a ticker, the dollar line, the balance label.info— the Quote row's icon and its Best return pill. Omitted, it followsaccent. The dark palette's default is a lighter lavender that reads on the dark card; a dark theme that brings its ownaccentand noinfogets its accent. Set a neutral here when your accent could be read as a verdict — the Emerald and Sand presets use#292a30.success— the compliance shield and its Passed pill, and the connected-wallet dot.
Shape and type
radius
--sg-radius
24px
the outer card, the pill's dialog
radiusControl
--sg-radius-control
24px
panes, tiles, the detail rows, token button
radiusCta
--sg-radius-cta
24px
the primary button and the pill face
font
--sg-font
'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif
everything except the amounts
fontDisplay
--sg-font-display
Benzin, followed by font
the amounts
The small pieces follow the control radius: the percent chips at half of it, the wallet chip and the verdict pills at two thirds.
fontDisplaynames Benzin, but the widget never loads it. Benzin is the ZeroDelta display face; the widget ships no font file and makes no font request. On a page that already loads Benzin the amounts use it, and everywhere else they fall back tofont. If you setfontand leavefontDisplayunset, the amounts follow yourfont— a re-branded card is never left with one foreign face.
Motion
Identical in light and dark — motion does not theme. Every rule that uses these is prefers-reduced-motion guarded, so a user who asks for less motion gets it regardless of what you set.
motionFast
--sg-motion-fast
160ms
small state flips
motionBase
--sg-motion-base
220ms
expand / collapse, the modal
motionSlow
--sg-motion-slow
320ms
the drawer, the receipt
motionDeliberate
--sg-motion-deliberate
480ms
the success sweep
easeOutSoft
--sg-ease-out-soft
cubic-bezier(0.22,0.61,0.36,1)
entrances
easeInOut
--sg-ease-in-out
cubic-bezier(0.4,0,0.2,1)
reversible morphs
easeSpring
--sg-ease-spring
cubic-bezier(0.34,1.56,0.64,1)
the ZeroDelta Link pop
mode
mode is the only field with no CSS variable. It selects which base palette the other twenty-two are spread over.
Dark mode is yours to decide
The widget does not read prefers-color-scheme itself — it renders the mode you give it, so it always matches your page rather than fighting it. Follow the OS setting like this:
Presets
The five palettes the widget playground offers. Copy the object you want.
Measured contrast of the CTA label on its accent: light and dark 5.6:1, Black/White 14.3:1, Sand 8.5:1 — all WCAG AA. Emerald measures 3.9:1, which meets AA for large text only; darken the accent if your CTA copy must meet AA at 14 px.
Corners
The three radii move together. The playground drives all three from one slider:
0 gives a square card; 24 is the default.
Picking accentText for a brand colour
If you set accent to your own brand colour, set accentText to whichever of black or white is legible on it. This is the helper the playground uses:
Sizing
The compact card is 380 px wide at most and centres in its container. The wide layout is a 360 px card beside a 280 px You receive card, and folds to one column when its container is narrower than 660 px. Set heightPx to fix the height and scroll the content inside — useful in a fixed checkout panel. Omit it for fit-content.
What you cannot theme
Layout, copy, the ZeroDelta brand mark, the ordering of funding routes, and the success visual — which is accent-tinted by design and never a loud green surface. These are fixed so that a payment surface looks and behaves the same wherever it is embedded.
See every token live, with a copyable snippet, in the widget playground.

