Skip to content
← ReactDynamic Theming
import kleur from "@driangle/kleur";
import { useMemo } from "react";

function useTheme(seed, mode) {
  return useMemo(() => {
    const base = kleur(seed);
    const isDark = mode === "dark";
    const neutral = base.desaturate(0.85);

    const surface = isDark
      ? neutral.withLightness(8)
      : neutral.withLightness(96);
    const onSurface = isDark
      ? neutral.withLightness(91)
      : neutral.withLightness(10);

    return {
      "--primary": base.toHex(),
      "--primary-light": base.lighten(0.3).toHex(),
      "--primary-dark": base.darken(0.3).toHex(),
      "--surface": surface.toHex(),
      "--surface-dim": isDark
        ? neutral.withLightness(12).toHex()
        : neutral.withLightness(91).toHex(),
      "--on-surface": onSurface.toHex(),
      "--on-primary": kleur.isLight(base)
        ? base.darken(0.8).toHex()
        : base.lighten(0.9).toHex(),
    };
  }, [seed, mode]);
}

Card Heading

Styled entirely from tokens derived from a single seed color using lighten(), darken(), and contrast().

Primary
Secondary
Surface contrast: 14.93:1WCAG AA
Derived Tokens
--primary#3a6bd5
--primary-light#7597e2
--primary-dark#21489c
--surface#121416
--surface-dim#1c1d22
--surface-high#272a30
--on-surface#e6e7ea
--on-primary#0a152d