Styling Tokens
Jul 11, 2023122 min read

Styling Tokens: Deep Dive #66

MC
Malik Chen

Component Systems Strategist

This post follows the plan’s “Styling Tokens” focus and the site’s global cross-linking requirements:

Some pages on this site use conceptual or illustrative APIs. This post uses standard, real-world React and Next.js patterns (CSS variables + Tailwind or plain CSS) so the code is directly applicable.

Styling tokens are easiest to scale when they’re treated as route-adjacent state:

  • layouts define the baseline token set (shared across nested routes),
  • component routes (tabs/panels) may apply scoped overrides,
  • and every override is visible and reversible.

Even if your “routing” is just UI state, the rule holds: put your token contract in a shell that’s stable across navigation.

What Counts as a Token (Practical Definition)

In production apps, tokens are the small set of values you reference everywhere:

  • colors (bg/fg/border)
  • spacing scale
  • radii and shadows
  • typography sizes/line-heights

Tokens are not “a theme” — they’re the contract that makes themes possible.

Worked Example: Route-Shell Tokens With CSS Variables

We’ll build a token system that supports:

  • a layout-level baseline (:root variables),
  • per-section overrides (e.g. docs vs blog),
  • and “component route” overrides (e.g. a panel that needs a denser spacing scale).

Step 1: Define Tokens as CSS Variables

Create a small, stable set. Don’t put every color in tokens — only the ones used as system primitives.

/* app/tokens.css */
:root {
  --color-bg: #0b0d12;
  --color-fg: #e7eaf0;
  --color-muted: #9aa3b2;
  --color-border: rgba(231, 234, 240, 0.12);

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --radius-2: 8px;
  --radius-3: 12px;

  --font-sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
}

Step 2: Apply Tokens in the App Shell (Layout)

If you’re using app/ routing, this lives naturally in your root layout (stable across route segments).

// app/layout.tsx
import "./tokens.css";
import "./globals.css";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body data-shell="root">{children}</body>
    </html>
  );
}

Step 3: Use Tokens in Components (No Magic)

type CardProps = {
  title: string;
  children: React.ReactNode;
};

export function Card({ title, children }: CardProps) {
  return (
    <section
      style={{
        border: "1px solid var(--color-border)",
        borderRadius: "var(--radius-3)",
        padding: "var(--space-4)",
        background: "var(--color-bg)",
        color: "var(--color-fg)",
      }}
    >
      <h2 style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-lg)" }}>{title}</h2>
      <div style={{ marginTop: "var(--space-2)", color: "var(--color-muted)" }}>{children}</div>
    </section>
  );
}

Step 4: Route-Level Overrides (Docs vs Blog)

Override tokens by applying a wrapper with different variable values.

/* app/docs/docs-theme.css */
[data-theme="docs"] {
  --color-bg: #0a101a;
  --color-fg: #eaf2ff;
  --color-muted: #9fb1cf;
}

/* app/blog/blog-theme.css */
[data-theme="blog"] {
  --color-bg: #0f0f10;
  --color-fg: #f2f2f2;
  --color-muted: #b9b9b9;
}
// app/docs/layout.tsx
import "./docs-theme.css";

export default function DocsLayout({ children }: { children: React.ReactNode }) {
  return <div data-theme="docs">{children}</div>;
}
// app/blog/layout.tsx
import "./blog-theme.css";

export default function BlogLayout({ children }: { children: React.ReactNode }) {
  return <div data-theme="blog">{children}</div>;
}

Step 5: Component-Route Overrides (Density Toggle)

For panels/tabs inside a route, you can apply scoped overrides on a container element.

export function DensityPanel({
  density,
  children,
}: {
  density: "comfortable" | "dense";
  children: React.ReactNode;
}) {
  const style =
    density === "dense"
      ? ({ ["--space-2" as any]: "6px", ["--space-4" as any]: "12px" } as React.CSSProperties)
      : undefined;

  return (
    <div data-density={density} style={style}>
      {children}
    </div>
  );
}

Security Note: Tokens and User-Generated Content

Token overrides should not accept arbitrary user-provided values without validation (e.g. themes stored in user profiles) because CSS variables can be abused to create deceptive UI states or reduce contrast. If you allow user themes:

  • whitelist allowed token keys,
  • sanitize token values,
  • and keep minimum contrast constraints.

See: Security and Safety.

Token Checklist

  • Define a small baseline token set in :root.
  • Apply route-level overrides in nested layouts.
  • Apply scoped overrides for component-route “panels”.
  • Avoid user-provided raw CSS variable values unless sanitized.