Skip to main content

Theming

The kit accepts a full set of color and shape tokens via the theme prop. This guide walks through the practical patterns — for the complete token reference see the theme prop reference.

The token system, in one paragraph

The iframe's UI is built on CoinTracker's @cointracker/base-ui package — a component library built on shadcn patterns + Radix UI primitives. If you've worked with a shadcn-based app before, every ThemeContract field name will be familiar: background, foreground, primary, muted / mutedForeground, accent, destructive, border, input, popover / popoverForeground, radius. Same semantics, same role each one plays inside components. The fields prefixed with ct* (ctCardSurface, ctForeground, ctEmphasis, ctSecondary) are CoinTracker-specific surfaces that don't exist in vanilla shadcn — leave them unset for sensible defaults, or set them when you want full fidelity.

Token format: any valid CSS color string works — #0052FF, rgb(0, 82, 255), hsl(216 100% 50%), rgba(0, 82, 255, 0.8). The iframe doesn't validate, so an invalid string just renders as the browser default.

Pass partner-mode-aware tokens

If your product has both light and dark themes, pass both — the kit picks the right one automatically based on theme.mode (or the user's OS preference when mode is omitted).

const theme = useMemo(
() => ({
light: {
background: '#FFFFFF',
foreground: '#0A0B0D',
primary: '#0052FF',
muted: '#F5F8FF',
mutedForeground: '#5B616E',
border: 'rgba(91, 97, 110, 0.2)',
input: 'rgba(91, 97, 110, 0.2)',
ring: '#0052FF',
},
dark: {
background: '#0A0B0D',
foreground: '#F5F8FF',
primary: '#3773F5',
muted: '#1F2226',
mutedForeground: '#A7ADB8',
border: 'rgba(91, 97, 110, 0.2)',
input: 'rgba(91, 97, 110, 0.2)',
ring: '#3773F5',
},
mode: appTheme.mode, // 'light' | 'dark' from your app's theme provider
}),
[appTheme.mode],
);

<TaxKitProvider fetchAccessToken={fetchAccessToken} theme={theme}>
<YourApp />
</TaxKitProvider>

Memoize the theme object — it serializes into the iframe's CONFIG post-robot event, and an unstable reference triggers redundant CONFIG sends on every render.

Map your design tokens

If your product already exposes design tokens (via CSS variables, a theme provider, or a build-time tool), map them to the ThemeContract fields. Most fields have direct equivalents:

ThemeContract fieldTypical partner token
backgroundPage background
foregroundDefault text
primaryBrand color / primary button
secondarySecondary surface
mutedSubdued surface
mutedForegroundSubdued text
accentHover/active surface
destructiveError red
warningWarning yellow/orange
successSuccess green
borderDefault border
inputForm input border
radiusDefault border-radius
buttonRadiusPill vs. rounded-rect on buttons
cardRadiusContainer border-radius
fontSansBody font stack

Shape and typography

In addition to colors, you can tune the kit's shape language:

  • buttonRadius — pill (9999px) vs. soft-rounded (0.5rem) vs. sharp (0.25rem).
  • cardRadius — controls container roundness.
  • fontSans — the kit uses system fonts by default; override with your product's font stack.
theme={{
light: {
fontSans: '"Inter", system-ui, sans-serif',
buttonRadius: '0.5rem',
cardRadius: '1rem',
// …colors
},
}}

Don't theme the mode out from under users

Avoid hard-coding theme.mode. If you must, give users a way to switch back — otherwise the kit will render in a mode that disagrees with their system preference and the partner shell.

The recommended pattern is to drive theme.mode from your app's theme provider (which itself respects user preference), so the kit always matches the rest of your UI.

Inspecting what's applied

While you're tuning, the easiest way to see what's rendering is to open the kit in dev and inspect the iframe in browser devtools. The kit applies tokens as CSS variables on the iframe wrapper — you'll see them as --background, --foreground, etc.