Theming
The token system the styled components paint against, and the CSS to copy.
The styled components paint against a small set of semantic tokens. Copy the token CSS into your global stylesheet once and every component reads it. The headless package has no styling of its own, so this only matters if you use the styled components.
The token model
Four seeds drive everything:
:root {
--bg: #ffffff; /* canvas */
--fg: #1a1a1a; /* ink */
--acc: #0169cc; /* accent */
--con: 0.3; /* contrast — how far surfaces and states step from bg */
}Every other token derives from those with oklch(from … calc(…)): the surface
tiers (--primary, --tertiary, --base), their -hover/-active/-border
states, the accent chain, and the ink tiers. Because the derivations reference
the seeds, dark mode is just a different set of seeds — the .dark block
re-seeds --bg/--fg/--acc and flips the state-delta signs, and every derived
value recomputes. Tune the whole system by changing four values.
Copy the tokens
Paste this into your globals.css (after @import "tailwindcss"). It defines
the seeds, the derived tokens, the @theme inline mappings, and the .dark
overrides:
@custom-variant dark (&:is(.dark *));
:root {
font-feature-settings:
"liga" 1,
"calt" 1;
--bg: #ffffff;
--fg: #1a1a1a;
--acc: #0169cc;
--con: 0.35;
--mix-hover: calc(16% * var(--con));
--mix-active: calc(20% * var(--con));
--mix-border: calc(24% * var(--con));
/* Border states stack from the border token so a hovered border keeps its
separation from the hovered background. */
--base-bg: color-mix(in oklch, var(--bg), var(--fg) calc(10% * var(--con)));
--base-bg-hover: color-mix(in oklch, var(--base-bg), var(--fg) var(--mix-hover));
--base-bg-active: color-mix(in oklch, var(--base-bg), var(--fg) var(--mix-active));
--base-border: color-mix(in oklch, var(--base-bg), var(--fg) var(--mix-border));
--base-border-hover: color-mix(in oklch, var(--base-border), var(--fg) var(--mix-hover));
--base-border-active: color-mix(in oklch, var(--base-border), var(--fg) var(--mix-active));
--secondary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(4% * var(--con)));
--secondary-bg-hover: color-mix(in oklch, var(--secondary-bg), var(--fg) var(--mix-hover));
--secondary-bg-active: color-mix(in oklch, var(--secondary-bg), var(--fg) var(--mix-active));
--secondary-border: color-mix(in oklch, var(--secondary-bg), var(--fg) var(--mix-border));
--secondary-border-hover: color-mix(in oklch, var(--secondary-border), var(--fg) var(--mix-hover));
--secondary-border-active: color-mix(
in oklch,
var(--secondary-border),
var(--fg) var(--mix-active)
);
--primary-bg: var(--bg);
--primary-bg-hover: color-mix(in oklch, var(--primary-bg), var(--fg) var(--mix-hover));
--primary-bg-active: color-mix(in oklch, var(--primary-bg), var(--fg) var(--mix-active));
--primary-border: color-mix(in oklch, var(--primary-bg), var(--fg) var(--mix-border));
--primary-border-hover: color-mix(in oklch, var(--primary-border), var(--fg) var(--mix-hover));
--primary-border-active: color-mix(in oklch, var(--primary-border), var(--fg) var(--mix-active));
--tertiary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(2% * var(--con)));
--tertiary-bg-hover: color-mix(in oklch, var(--tertiary-bg), var(--fg) var(--mix-hover));
--tertiary-bg-active: color-mix(in oklch, var(--tertiary-bg), var(--fg) var(--mix-active));
--tertiary-border: color-mix(in oklch, var(--tertiary-bg), var(--fg) var(--mix-border));
--tertiary-border-hover: color-mix(in oklch, var(--tertiary-border), var(--fg) var(--mix-hover));
--tertiary-border-active: color-mix(in oklch, var(--tertiary-border), var(--fg) var(--mix-active));
--quaternary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(24% * var(--con)));
--quaternary-bg-hover: color-mix(in oklch, var(--quaternary-bg), var(--fg) var(--mix-hover));
--quaternary-bg-active: color-mix(in oklch, var(--quaternary-bg), var(--fg) var(--mix-active));
--quaternary-border: color-mix(in oklch, var(--quaternary-bg), var(--fg) var(--mix-border));
--quaternary-border-hover: color-mix(
in oklch,
var(--quaternary-border),
var(--fg) var(--mix-hover)
);
--quaternary-border-active: color-mix(
in oklch,
var(--quaternary-border),
var(--fg) var(--mix-active)
);
--accent-bg: color-mix(in oklch, var(--acc), var(--fg) calc(28% * var(--con)));
--accent-bg-hover: color-mix(in oklch, var(--acc), var(--fg) var(--mix-hover));
--accent-bg-active: color-mix(in oklch, var(--acc), var(--fg) var(--mix-active));
--accent-border: color-mix(in oklch, var(--acc), var(--fg) var(--mix-border));
--accent-border-hover: color-mix(in oklch, var(--accent-border), var(--fg) var(--mix-hover));
--accent-border-active: color-mix(in oklch, var(--accent-border), var(--fg) var(--mix-active));
--text-primary: color-mix(in oklch, var(--bg), var(--fg) 85%);
--text-secondary: color-mix(in oklch, var(--bg), var(--fg) 58%);
--text-tertiary: color-mix(in oklch, var(--bg), var(--fg) 40%);
--radius: 8px;
--sidebar-width: 224px;
--thread-width: 672px;
--artifacts-panel-width: 512px;
}
.dark {
--bg: #111111;
--fg: #fcfcfc;
--acc: #4a9eed;
--base-bg: var(--bg);
--primary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(12% * var(--con)));
--secondary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(3% * var(--con)));
--tertiary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(20% * var(--con)));
--quaternary-bg: color-mix(in oklch, var(--bg), var(--fg) calc(56% * var(--con)));
--mix-hover: calc(16% * var(--con));
--mix-active: calc(18% * var(--con));
--mix-border: calc(20% * var(--con));
}
@theme inline {
--color-base-bg: var(--base-bg);
--color-base-bg-hover: var(--base-bg-hover);
--color-base-bg-active: var(--base-bg-active);
--color-base-border: var(--base-border);
--color-base-border-hover: var(--base-border-hover);
--color-base-border-active: var(--base-border-active);
--color-secondary-bg: var(--secondary-bg);
--color-secondary-bg-hover: var(--secondary-bg-hover);
--color-secondary-bg-active: var(--secondary-bg-active);
--color-secondary-border: var(--secondary-border);
--color-secondary-border-hover: var(--secondary-border-hover);
--color-secondary-border-active: var(--secondary-border-active);
--color-primary-bg: var(--primary-bg);
--color-primary-bg-hover: var(--primary-bg-hover);
--color-primary-bg-active: var(--primary-bg-active);
--color-primary-border: var(--primary-border);
--color-primary-border-hover: var(--primary-border-hover);
--color-primary-border-active: var(--primary-border-active);
--color-tertiary-bg: var(--tertiary-bg);
--color-tertiary-bg-hover: var(--tertiary-bg-hover);
--color-tertiary-bg-active: var(--tertiary-bg-active);
--color-tertiary-border: var(--tertiary-border);
--color-tertiary-border-hover: var(--tertiary-border-hover);
--color-tertiary-border-active: var(--tertiary-border-active);
--color-quaternary-bg: var(--quaternary-bg);
--color-quaternary-bg-hover: var(--quaternary-bg-hover);
--color-quaternary-bg-active: var(--quaternary-bg-active);
--color-quaternary-border: var(--quaternary-border);
--color-quaternary-border-hover: var(--quaternary-border-hover);
--color-quaternary-border-active: var(--quaternary-border-active);
--color-accent-bg: var(--accent-bg);
--color-accent-bg-hover: var(--accent-bg-hover);
--color-accent-bg-active: var(--accent-bg-active);
--color-accent-border: var(--accent-border);
--color-accent-border-hover: var(--accent-border-hover);
--color-accent-border-active: var(--accent-border-active);
--color-ink-primary: var(--text-primary);
--color-ink-secondary: var(--text-secondary);
--color-ink-tertiary: var(--text-tertiary);
--radius-xs: calc(var(--radius) - 4px);
--radius-sm: calc(var(--radius) - 2px);
--radius-md: var(--radius);
--radius-lg: calc(var(--radius) + 2px);
--radius-xl: calc(var(--radius) + 4px);
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
--text-2xs: 11px;
--text-xs: 12px;
--text-sm: 13px;
--text-md: 14px;
--text-lg: 15px;
--text-xl: 16px;
}The @source line keeps Tailwind scanning the streamdown package for classes;
drop it if you don't render markdown. Adjust the four seeds to rebrand.