Theme Tokens
Kitsune is themed entirely through CSS custom properties. There is no styling runtime. Components consume var(--kit-*); the cascade applies whatever you set.
pnpm add @atheory-ai/kitsune-themeDefault token set
Section titled “Default token set”defaultTheme from @atheory-ai/kitsune-theme:
| Token | Default | Used by |
|---|---|---|
--kit-color-accent | oklch(57% 0.18 248) | Buttons, focus, accents |
--kit-color-accent-contrast | white | Text/icon on accent surfaces |
--kit-color-surface | Canvas | Component backgrounds |
--kit-color-text | CanvasText | Body text |
--kit-color-border | (system) | Component borders |
--kit-color-muted | GrayText | Descriptions, secondary text |
--kit-color-error | oklch(58% 0.18 25) | Errors, destructive actions |
--kit-color-success | oklch(60% 0.16 150) | Success toasts, confirms |
--kit-focus-ring | Highlight | Focus outlines |
--kit-radius-sm | 0.375rem | Buttons, inputs, small surfaces |
--kit-radius-md | 0.5rem | Cards, dialogs |
--kit-space-1 | 0.25rem | Smallest spacing |
--kit-space-2 | 0.5rem | Small spacing |
--kit-space-3 | 0.75rem | Default spacing |
--kit-space-4 | 1rem | Larger spacing |
--kit-button-bg | var(--kit-color-accent) | <kit-button> background |
--kit-button-color | var(--kit-color-accent-contrast) | <kit-button> foreground |
--kit-button-border | var(--kit-color-accent) | <kit-button> border |
Components use system colors (Canvas, CanvasText, Highlight) as fallbacks so high-contrast and forced-color modes adapt automatically.
Apply the default tokens
Section titled “Apply the default tokens”The default theme is a value, not a stylesheet. Convert it to CSS at build time:
import { defaultTheme, themeToCss } from '@atheory-ai/kitsune-theme'
console.log(themeToCss(defaultTheme))// :root {// --kit-color-accent: oklch(57% 0.18 248);// ...// }Or apply imperatively at runtime:
import { applyTheme, defaultTheme } from '@atheory-ai/kitsune-theme'
applyTheme(defaultTheme)applyTheme sets each token as an inline style on document.documentElement (or any element you pass).
Define a brand theme
Section titled “Define a brand theme”import { applyTheme, defineTheme } from '@atheory-ai/kitsune-theme'
const brand = defineTheme({ name: 'kitsune-dark', tokens: { '--kit-color-accent': 'oklch(72% 0.18 285)', '--kit-color-surface': 'oklch(20% 0 250)', '--kit-color-text': 'oklch(96% 0 0)', '--kit-color-border': 'oklch(35% 0 250)', '--kit-radius-sm': '0.5rem', },})
applyTheme(brand)defineTheme is a type-checked constructor — it ensures every token name starts with --kit-.
Switching themes at runtime
Section titled “Switching themes at runtime”Swap themes with another applyTheme. Tokens replace. Anything not present in the new theme falls back to the previous values or the component’s hard-coded defaults.
const button = document.querySelector('button')
document.querySelector('#brand-toggle').addEventListener('click', () => { applyTheme(currentTheme.name === 'kitsune-dark' ? defaultTheme : brand)})For per-section themes, scope applyTheme to a specific element instead of the document root:
applyTheme(brand, document.getElementById('marketing-section'))Cascade layers (recommended)
Section titled “Cascade layers (recommended)”Kitsune does not currently ship a layered stylesheet, but the convention is:
@layer kit.reset, kit.tokens, kit.base, kit.components, kit.utilities, app.overrides;
@layer kit.tokens { :root { /* tokens */ } }@layer kit.components { /* component defaults */ }@layer app.overrides { /* your overrides win */ }Layers let your app code override Kitsune defaults without specificity wars and without !important.
Component theming patterns
Section titled “Component theming patterns”Most components expose their theme as --kit-<component>-<property>:
:host { background: var(--kit-button-bg, var(--kit-color-accent)); color: var(--kit-button-color, var(--kit-color-accent-contrast)); border: 1px solid var(--kit-button-border, var(--kit-color-accent));}To override a single button:
.danger { --kit-button-bg: var(--kit-color-error); --kit-button-color: white; --kit-button-border: var(--kit-color-error);}<kit-button class="danger">Delete</kit-button>You don’t need to pierce shadow DOM. The custom properties cascade through the shadow boundary.
Per-element overrides
Section titled “Per-element overrides”Custom properties on the host become available inside the shadow DOM:
<kit-button style="--kit-button-bg: oklch(50% 0.2 25)">Crimson</kit-button>Use this sparingly — most theming should live in your stylesheet, not inline.
Light/dark with light-dark()
Section titled “Light/dark with light-dark()”The simplest dark-mode strategy doesn’t need a theme switcher at all:
:root { color-scheme: light dark; --kit-color-surface: light-dark(white, oklch(20% 0 250)); --kit-color-text: light-dark(oklch(20% 0 250), oklch(96% 0 0));}The browser picks based on prefers-color-scheme. Works in every browser as of 2024.
For manual override:
[data-theme="dark"] { color-scheme: dark; --kit-color-surface: oklch(20% 0 250); /* ... */}Read more
Section titled “Read more”- Tutorial: Chapter 1 — sets up Quill’s theme
- The Web Caught Up: CSS — broader CSS context