Theming & Tokens
kinu exposes all visual styling through CSS Custom Properties declared in the bundled kinu/style.css (sourced from
src/variables.css). The defaults define both light and dark palettes, radii, spacing, and motion tokens. Because components
forward props as attributes, you override the design by setting CSS variables or adding new attribute selectors—no JavaScript
theme provider required.
Changing Tokens
Override tokens at the root or within a scope. Tokens follow the --k-* naming convention:
:root {
--k-radius: 0.625rem;
--k-primary: 221 83% 53%;
--k-primary-foreground: 0 0% 100%;
}
[data-theme='dark'] {
--k-background: 224 71% 4%;
--k-foreground: 210 40% 98%;
}
Because values are stored as space-separated h s l triplets, components call hsl(var(--k-primary)) to render consistent
colors. You can replace tokens with your own palette or plug in design system values.
Spacing Scale
Layout primitives and gap-style props read from a fixed, theme-independent spacing scale. Reach for these tokens instead of
hard-coded lengths so spacing stays consistent across components:
| Token | Value | Pixels |
|---|---|---|
--k-space-0 |
0 |
0 |
--k-space-xs |
0.25rem |
4px |
--k-space-sm |
0.5rem |
8px |
--k-space-md |
1rem |
16px |
--k-space-lg |
1.5rem |
24px |
--k-space-xl |
2.5rem |
40px |
The step names mirror kinu's sm/md/lg size convention, so a gap="md" prop maps to --k-space-md. Override a step at
the root to rescale spacing everywhere at once:
:root {
--k-space-md: 1.25rem; /* roomier default gap */
}
Component Variants via Attributes
Each component maps props to attributes, letting you target them with CSS selectors:
[k="button"][variant="brand"] {
background-color: hsl(var(--brand));
color: hsl(var(--brand-foreground));
}
Keep selectors minimal—apply only the delta from the base style to preserve small bundle size. For boolean props, target the
presence of the attribute: [k="button"][loading] { opacity: 0.6; }.
Dark Mode
kinu ships light and dark palettes. Dark mode follows the OS by default
(@media (prefers-color-scheme: dark)); to force a scheme, set
data-color-scheme on <html> or any container:
document.documentElement.dataset.colorScheme = 'dark'; // or 'light'
Because tokens cascade, you can theme individual subtrees simply by setting data-color-scheme on a wrapping element.
Animations & Motion
Animation curves live in --k-ease and related tokens. Adjust them globally or per component:
:root {
--k-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}
[k="accordion"][open] {
transition-duration: 200ms;
}
When you need reduced motion support, respect the prefers-reduced-motion media query and reduce durations or disable
animations in your overrides.
Token Contract
The --k-* custom properties are kinu's public theming API — a stable surface you can rely on. Restyle the whole library
by overriding tokens; you rarely need to touch component selectors. The groups:
| Group | Tokens |
|---|---|
| Surfaces | --k-background, --k-foreground, --k-card, --k-popover, --k-sidebar, --k-muted, --k-accent (+ -foreground) |
| Brand | --k-primary, --k-primary-foreground, --k-primary-hover, --k-primary-soft, --k-secondary (+ states) |
| Semantic | --k-success, --k-warning, --k-info, --k-destructive (+ -foreground) |
| Lines & focus | --k-border, --k-input, --k-ring |
| Shape & spacing | --k-radius, --k-space-0…xl |
| Motion | --k-ease, --k-ease-out, --k-ease-spring, --k-ease-elastic |
Colors are stored as space-separated h s l triplets (not finished colors) so components can apply alpha:
hsl(var(--k-primary) / 0.15). Keep that format when overriding, and derive related shades from a base where it helps —
e.g. --k-primary-soft: var(--k-primary) / 0.15.
Preset Themes
Two ready-made brand palettes ship as standalone stylesheets. Import one after the base styles to recolor the whole library (light and dark both covered):
import 'kinu/style.css';
import 'kinu/themes/violet.css'; // or 'kinu/themes/emerald.css'
Each preset overrides only the brand tokens (--k-primary*, --k-ring), so everything else — surfaces, semantic colors,
spacing — stays consistent. Copy one as a starting point for your own brand: it is just a handful of token declarations across
the light, system-dark, and explicit-dark scopes.
Density, RTL & High Contrast
Density. The --k-density scalar multiplies control heights and padding. The default is 1; lower it for compact
dashboards or raise it for touch:
[k="dashboard"] {
--k-density: 0.875; /* tighter controls in this subtree */
}
Button is the reference implementation; the same calc(... * var(--k-density)) pattern applies to any control.
RTL. Components use CSS logical properties (inset-inline, padding-inline, margin-inline-start, …), so they mirror
automatically under dir="rtl" — no separate stylesheet.
Forced colors. Under Windows High Contrast (forced-colors: active), box-shadow focus rings are dropped, so the base
stylesheet restores a system-color outline on focus for interactive components. Keep custom focus styles outline-based (or
add a forced-colors fallback) so keyboard focus stays visible there.