Search Raihin Design System

Search tokens, pages and quick actions

Cascade

How the stylesheet layers compose

A reference for how @import order, :root, .dark, Tailwind layers, and component classes interact — and a debug checklist for when a token value isn't resolving the way you expect.

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 tokens yet.

  2. 2

    tw-animate-css

    @import 'tw-animate-css' — adds the data-state animation utilities that Radix-based components use for enter/exit transitions.

  3. 3

    @theme inline

    The @theme inline block maps raw token names (--background, --primary, …) into Tailwind's design system so utilities like bg-background and text-primary compile.

  4. 4

    :root values

    The :root block declares every light-mode token value: surfaces, foregrounds, brand colors, borders, status, charts, sizing, motion, z-index, and so on.

  5. 5

    .dark values

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

  6. 6

    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 the right value lands wherever the component is rendered.

The four debug scenarios

I changed --primary but nothing updated.
  1. Is the value inside the :root block in app/globals.css and after the @theme inline declaration? Earlier blocks lose to later ones.
  2. Are you binding --primary or --color-primary? In Tailwind v4 the @theme inline mapping is var(--primary) → --color-primary; both work, but stay consistent within a file.
  3. Are you targeting :root and not html? :root has higher specificity than html. Either works, but don't mix them in the same stylesheet.
  4. Did you place the value inside a media query or @layer block? Layered tokens lose to layer-less ones; declarations inside @media are mode-scoped.
  5. Is the value valid OKLCH? Run getComputedStyle(document.documentElement).getPropertyValue('--primary') in the console — an empty result means the value didn't parse.
Dark mode isn't picking up the new value.
  1. Is the value inside .dark { } and after the :root block? .dark must follow :root so its specificity wins when the class is present.
  2. Is next-themes actually toggling the .dark class? Inspect <html> in devtools — the class should flip between '' and 'dark'.
  3. Is suppressHydrationWarning set on <html>? Without it, next-themes triggers an SSR hydration mismatch that re-renders without the class.
  4. Are you reading resolvedTheme (not theme) when you need to know the current mode? theme can be 'system' even when resolvedTheme is 'dark'.
  5. Did you put dark values inside a @media (prefers-color-scheme: dark) block instead of .dark? next-themes uses the class, not the media query, so those values will never apply.
A component renders the wrong color despite using the right token.
  1. Did the semantic pair get split? bg-card without text-card-foreground means the foreground falls back to whatever the parent sets.
  2. Is the className using a state-layer token where a surface was expected? bg-state-hover is a 4–6% layer — 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 win over the CSS variable.
  1. Tailwind utilities are class selectors (.bg-card) — same specificity as a :root variable declaration. The class appears to 'win' because it reads var(--card) at use-time, so it always reflects the latest value.
  2. If a component applies bg-zinc-900 directly, that's a Tailwind palette literal and ignores the token entirely. Swap it for bg-card.
  3. If a component uses an arbitrary value like bg-[oklch(...)], it has hardcoded a value that won't respond to token changes. Use var(--card) or the token-arbitrary syntax bg-(--color-card).
  4. An !important on a utility (bg-card!) breaks the cascade for that property. Remove it; reach for state layers or the right role instead.

The 60-second debug recipe

// 1. Is the variable declared at all?
getComputedStyle(document.documentElement).getPropertyValue('--primary');
// → "oklch(0.228 0.013 107.4)" — declared ✓
// → "" — not declared. Check the source order in app/globals.css.

// 2. Is the variable resolving to the value you expect?
// Inspect element → Computed → search '--primary'. If it shows a stale
// value, an earlier declaration is shadowing the one you intended.

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

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

// 5. Is Tailwind compiling the class?
// Build for production, search dist/.next/static/css for 'bg-card'. If
// absent, the class isn't being scanned — check content paths.