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 field | Typical partner token |
|---|---|
background | Page background |
foreground | Default text |
primary | Brand color / primary button |
secondary | Secondary surface |
muted | Subdued surface |
mutedForeground | Subdued text |
accent | Hover/active surface |
destructive | Error red |
warning | Warning yellow/orange |
success | Success green |
border | Default border |
input | Form input border |
radius | Default border-radius |
buttonRadius | Pill vs. rounded-rect on buttons |
cardRadius | Container border-radius |
fontSans | Body 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.