Skip to content
Kinetic

Docs

Theming

Components never hard-code a colour. They read Tailwind theme tokens, so they inherit whatever your project already defines — installing one into a light, brand-coloured app does not drag this site’s near-black palette along with it.

The two-layer token system

Raw values live on :root as --k-* custom properties. A @theme inlineblock then republishes them under Tailwind’s namespaces, which is what turns them into real utilities like bg-bg, text-muted and border-line.

The inline keyword is load-bearing. Without it Tailwind resolves each token to its :root value at build time, and any theme override you write later never lands.

:root {
  --k-bg: oklch(0.145 0 0);
  --k-fg: oklch(0.949 0 0);
  --k-line: oklch(1 0 0 / 8%);
  --k-accent: oklch(0.75 0.16 250);
}

@theme inline {
  --color-bg: var(--k-bg);
  --color-fg: var(--k-fg);
  --color-line: var(--k-line);
  --color-accent: var(--k-accent);
}

Light and dark

Dark is the :root default here because the product is motion — a shader or particle field reads correctly against black and washes out against white. Only the light theme needs a class, which halves the work the blocking theme script has to do before first paint.

Your project almost certainly does the opposite. That is fine: the components care only that the token names resolve, not which theme is the default.

Accent by category

Any subtree tagged with data-category reassigns --k-accentto that category’s hue. A card, its badge, its motif poster and its hover rule then agree on one colour with no props threaded through them, and they cannot drift apart.

[data-category='scroll'] { --k-accent: var(--k-cat-scroll); }
[data-category='webgl'] { --k-accent: var(--k-cat-webgl); }
[data-category='cursor'] { --k-accent: var(--k-cat-cursor); }

Retheming an installed component

Two options, both ordinary CSS. Either define the token names the component uses in your own theme layer, or wrap it in an element that overrides them locally:

<div style={{ ['--k-accent' as string]: 'oklch(0.7 0.19 20)' }}>
  <MagneticButton>Get in touch</MagneticButton>
</div>

Motion tokens

Site chrome uses a deliberately narrow easing vocabulary — --ease-out-quart, --ease-out-expo, --ease-in-out-quart. Components do not use these; each one brings the timing its technique actually requires, and those values are documented on its page with the reason they hold.

Reduced motion

The site’s own chrome collapses every transition to near-zero under prefers-reduced-motion: reduce. Components deliberately do not rely on that rule — for them “no motion” means rendering a different, still-correct state, not running the same animation instantly. Each component reads the preference itself and its page states exactly what it does.