Search Kaotypr Design System

Search tokens, pages and quick actions

Cascade

Why your override isn't working

Most reskins go wrong in one of four places. This page is the debug checklist when --primary refuses to change, .dark doesn't apply, or Tailwind seems to ignore your token. Read once, save the bookmark.

The cascade, in order

  1. 1

    Tailwind base reset

    @import 'tailwindcss' — applies preflight, normalize, and the default --spacing scale. This is the floor; nothing above it cares about your tokens yet.

  2. 2

    Kaotypr theme

    @import '@kaotypr-ui/theme' — declares every token under :root (light values) and .dark (dark values). This is the canonical theme.

  3. 3

    Your :root overrides

    The block in your project's globals.css that re-binds brand-tier tokens (--primary, --background, --radius-card, etc.). Must come AFTER the @import — order in the file matters.

  4. 4

    Your .dark overrides

    The .dark { } block that re-binds the dark-mode values for the same tokens. next-themes flips the .dark class on <html> before paint.

  5. 5

    Component classNames

    When a component reads bg-card or rounded-button, Tailwind v4 resolves the var(--card) / var(--radius-button) reference at use-time — so your override is already baked in.

The four debug scenarios

I overrode --primary but nothing changed.
  1. Is your :root block AFTER @import '@kaotypr-ui/theme' in source order? Move it below.
  2. Are you setting --primary or --color-primary? In Tailwind v4 the @theme inline mapping is var(--primary) → --color-primary; both work, but be consistent — pick one and stop.
  3. Are you targeting :root and not html? :root has higher specificity than html. Either works, but never mix.
  4. Did you put the override inside a media query (@media (min-width: ...)) or layer? Layered tokens lose to layer-less ones.
  5. Is the value valid OKLCH? Run getComputedStyle(document.documentElement).getPropertyValue('--primary') in the console — if it returns empty, the value parsed wrong.
.dark mode isn't picking up my override.
  1. Is the override under .dark { } and AFTER your :root block? .dark must follow :root in source order so its specificity wins when the class is present.
  2. Is next-themes actually toggling the .dark class? Inspect <html> in devtools — class should flip between '' and 'dark'.
  3. Is suppressHydrationWarning on <html>? Without it next-themes triggers an SSR hydration mismatch that re-renders without the class.
  4. Are you using resolvedTheme (not theme) when reading the mode? theme can be 'system' even when resolvedTheme is 'dark'.
  5. Did you put :root values inside a @media (prefers-color-scheme: dark) block instead of .dark? next-themes uses the class, not the media query — those overrides will never fire.
My component renders the wrong color despite the right token.
  1. Did you split the semantic pair? bg-card without text-card-foreground means the foreground falls back to whatever the parent sets.
  2. Are you using a state-layer token where you wanted a surface? bg-state-hover is a 4–6% layer, NOT a surface — on its own it looks transparent.
  3. Is a parent class overriding via specificity? An ancestor with text-foreground/60 will leak down to children that don't set their own foreground.
  4. Is Tailwind v4 finding the class? Token-arbitrary syntax like text-(--color-foreground) requires the variable to be declared in @theme or :root — confirm with devtools.
  5. Is dark mode flipping the wrong way? Check that --card-foreground in .dark contrasts with --card in .dark, not against the light-mode --card.
A Tailwind utility seems to override my CSS variable.
  1. Tailwind utilities are class selectors (.bg-card) — same specificity as your :root variable (an attribute-like declaration). The class WINS because it's later in the cascade and uses var(--card) at use-time. So the utility didn't 'win'; it read the latest var().
  2. If you're applying bg-zinc-900 directly to a component, that's a Tailwind palette literal and ignores your token entirely. Swap it for bg-card.
  3. If you're using arbitrary values bg-[oklch(...)] you've hardcoded a value that won't respond to your override. Use var(--card) or token-arbitrary syntax bg-(--color-card).
  4. Consumer-side !important on a utility (.bg-card!) breaks the cascade for that property. Remove it; reach for state layers or proper roles instead.

The 60-second debug recipe

// 1. Is the variable declared at all?
getComputedStyle(document.documentElement).getPropertyValue('--primary');
// → "oklch(0.205 0.04 290)" — declared ✓
// → "" — not declared. Check @import order.

// 2. Is the variable resolving to your override or the canonical theme value?
// Inspect element → Computed → search '--primary'. If it's the canonical
// value, your :root block came BEFORE the @import. Move it down.

// 3. Is .dark actually applied?
document.documentElement.classList.contains('dark');
// → true / false. If false in dark mode, next-themes is broken.

// 4. Does the component read your override?
// Right-click the element → Inspect → Computed tab → search 'background'.
// You should see "background-color: oklch(...)" with your value.
// If it shows the inherited / default, your selector targets the wrong layer.

// 5. Is Tailwind purging the class?
// Build prod, search dist/.next/static/css for 'bg-card'. If absent,
// the class isn't being scanned — check content paths in your tailwind config.