Search Kaotypr Design System

Search tokens, pages and quick actions

Branding

Reskin in ≤10 lines

Consumer projects override at most ten CSS variables per theme block. Token names and structure stay identical to the canonical theme — that is what keeps the system coherent across products.

Worked example: a warm, sunset-toned consumer app
@import "@kaotypr-ui/theme";

:root {
  --primary: oklch(0.205 0.04 290);     /* deep indigo */
  --secondary: oklch(0.86 0.07 50);      /* peach */
  --background: oklch(0.99 0.005 80);    /* warm off-white */
  --foreground: oklch(0.18 0 0);
  --border: oklch(0.92 0.01 80);
  --radius-card: 1.25rem;
  --radius-button: 0.75rem;
  --font-heading: var(--font-inter);
  --color-chart-1: oklch(0.65 0.13 50);  /* anchor charts to peach */
}

.dark {
  --background: oklch(0.13 0.01 290);
  --foreground: oklch(0.96 0 0);
  --primary: oklch(0.92 0 0);
  --secondary: oklch(0.43 0.07 50);
  --border: oklch(0.27 0.01 290);
}
Brand tier — override expected
  • Surfaces: --background, --card, --popover
  • Foreground hierarchy: --foreground, --foreground-subtle
  • Brand: --primary, --secondary, --accent
  • Status lightness/chroma (hue stays locked)
  • Fonts: --font-display, --font-heading, --font-sans
  • Semantic radii: --radius-button, --radius-card, …
  • Chart palette: --color-chart-1..12
Tunable tier — override allowed
  • Shadows: --shadow-card, --shadow-popover
  • Motion: --duration-*, --ease-*
  • Overlay opacity: --overlay
  • Per-control surfaces (switch, slider, scrollbar, skeleton)
  • Link / code / kbd treatments
  • Blur scale: --blur-sm, --blur-md, --blur-lg
Locked tier — never override

These keep the system coherent across every Kaotypr product. Overriding any of them locally is a bug, not a customization.

  • Z-index ladder (cross-product layering)
  • State-layer opacities (4 / 6 / 8 / 12 / 16%)
  • Status hue conventions (red = destructive, green = success, yellow = warning, blue = info)
  • --ring-width = 3px
  • The --size-* ratio scale (control / icon / avatar / touch-target)
  • The opacity scale (--opacity-*)

If you find yourself needing to override one of these, file an issue in the design-system repo. It is almost certainly a system-wide need.

  • Status colors still read as themselves

    Destructive must read as red. Success as green. Warning as yellow/orange. Info as blue. If your reskin pushed --primary into the red space, you must not also let it cascade into status colors.

  • Focus rings are still 3px

    Tab through your interactive surfaces. The ring is uniform across buttons, inputs, links, sidebar items. Width is locked; only color flips with theme.

  • Compact density still tightens

    Toggle data-density='compact' on <html>. Controls and card paddings shrink predictably. Touch targets stay ≥ 44px.

  • Chart series 1 anchors to brand

    The first series in any chart should read as your accent. Override --color-chart-1 to your primary anchor; the rest of the palette derives from there.

  • Light and dark both legible

    Switch between modes. Foreground/background contrast stays ≥ 4.5:1 for body copy in both modes. Status pairs (e.g. bg-success + text-success-foreground) remain readable.

  • No locked tokens in your override

    grep your override for --z-index-, --ring-width, --opacity-, --state-, --size-. If any appear, remove them or open an issue.

Overriding --primary AND status colors so 'destructive' is no longer red

Status hue is a cross-product convention. Users in product A and product B both expect red = destructive. Pick a primary that doesn't collide with red — or accept that status colors stay where they are.

Setting --radius-card and --radius-dialog to wildly different scales

The semantic-radii relationship is part of the system identity. If --radius-card is 1rem, --radius-dialog should land near 1.25rem, not 2.5rem.

Defining new token names like --brand-red or --custom-spacing

The token surface is fixed. New tokens at the consumer layer fragment the system. Compose with existing tokens or use a CSS variable scoped to your component (--my-component-thing).

Heavy shadow-card with low contrast borders

Shadows are a tunable tier — but they only feel right when they pair with the surface contrast they were drawn against. If you crank shadow + drop border-subtle, cards float disconnected.

Overriding inside a component with !important

Reskins live at the :root / .dark level. Overriding inside a component bypasses the cascade and breaks every consumer that builds on top.

Different values for the same token in :root vs .dark that change meaning

If --primary is 'brand indigo' in light, it can't be 'success green' in dark. Light and dark are tonal variants of the same role; the role doesn't change.

:root {
  --background: oklch(0.99 0.005 80);
  --foreground: oklch(0.18 0 0);
  --primary: oklch(0.55 0.16 30);   /* terracotta */
  --secondary: oklch(0.86 0.07 50); /* peach */
  --border: oklch(0.92 0.01 80);
  --color-chart-1: oklch(0.55 0.16 30);
}
.dark {
  --background: oklch(0.16 0.01 30);
  --foreground: oklch(0.96 0 0);
  --primary: oklch(0.78 0.13 30);
  --secondary: oklch(0.50 0.08 50);
}