Tips & Tricks (Updated: 6/2/2026)

CSS Variables With Claude Code: Practical Theme Tokens

Use Claude Code to implement CSS variables, var() fallbacks, theme tokens, dark mode, and safer UI reviews.

CSS Variables With Claude Code: Practical Theme Tokens

CSS Variables Are A System Boundary

CSS variables, formally called CSS custom properties, let you define values such as --color-accent and reuse them with var(--color-accent). In plain terms, they are named storage slots for design decisions: color, spacing, radius, shadows, type scale, and state-specific overrides.

That matters when you use Claude Code. If you ask for “a nice card UI”, the generated CSS may hard-code colors into every component. If you first ask for custom properties and theme tokens, Claude Code has a stable vocabulary for future edits. A later dark-mode change or CTA color test becomes a token edit instead of a hunt through unrelated selectors.

Pair this article with the dark mode implementation guide, the design system guide, and the Flexbox patterns guide. The mental model is simple: palette tokens hold raw values, semantic tokens describe product meaning, component tokens adapt one UI part, and state tokens override values for dark, danger, compact, or brand-specific variants.

Prompt Claude Code For Tokens First

Give Claude Code a precise brief before asking it to style a component. This keeps the output reviewable and reduces one-off values.

{
  "task": "Create CSS custom properties for an article UI and implement a copy-paste demo.",
  "mustInclude": [
    "palette tokens",
    "semantic theme tokens",
    "component-level tokens",
    "var() fallbacks",
    "dark mode with data-theme and prefers-color-scheme",
    "vanilla JavaScript for theme switching"
  ],
  "constraints": [
    "Use custom properties only for values, not selectors or property names.",
    "Keep contrast readable in light and dark modes.",
    "Add comments for tokens that product designers may edit."
  ]
}

Copy-Paste CSS Theme Tokens

The second argument in var(--surface, #ffffff) is the fallback. MDN’s var() reference is the official place to confirm how fallback resolution works.

:root {
  color-scheme: light;

  /* Palette tokens: raw values */
  --blue-50: #eff6ff;
  --blue-400: #60a5fa;
  --blue-600: #2563eb;
  --blue-700: #1d4ed8;
  --slate-50: #f8fafc;
  --slate-100: #f1f5f9;
  --slate-300: #cbd5e1;
  --slate-700: #334155;
  --slate-900: #0f172a;
  --red-600: #dc2626;

  /* Semantic theme tokens: values used by the app */
  --surface: var(--slate-50);
  --surface-raised: #ffffff;
  --text: var(--slate-900);
  --text-muted: var(--slate-700);
  --border: var(--slate-300);
  --color-accent: var(--blue-600);
  --color-danger: var(--red-600);

  /* Typography and spacing */
  --font-body: Inter, "Noto Sans", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", Consolas, monospace;
  --step-0: 1rem;
  --step-1: 1.125rem;
  --step-2: 1.5rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --shadow-card: 0 12px 30px rgb(15 23 42 / 0.12);
}

[data-theme="dark"] {
  color-scheme: dark;
  --surface: var(--slate-900);
  --surface-raised: #111827;
  --text: var(--slate-50);
  --text-muted: var(--slate-300);
  --border: #334155;
  --color-accent: var(--blue-400);
  --shadow-card: 0 18px 42px rgb(0 0 0 / 0.35);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface: var(--slate-900);
    --surface-raised: #111827;
    --text: var(--slate-50);
    --text-muted: var(--slate-300);
    --border: #334155;
    --color-accent: var(--blue-400);
  }
}

body {
  margin: 0;
  font-family: var(--font-body, system-ui, sans-serif);
  background: var(--surface, #ffffff);
  color: var(--text, #111827);
}

.pricing-card {
  --card-padding: var(--space-6);
  --card-border: var(--border);

  max-width: 34rem;
  margin: var(--space-6) auto;
  padding: var(--card-padding);
  background: var(--surface-raised, #ffffff);
  border: 1px solid var(--card-border, #d1d5db);
  border-radius: var(--radius-lg, 0.75rem);
  box-shadow: var(--shadow-card, none);
}

.pricing-card h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--step-2, 1.5rem);
}

.pricing-card p {
  color: var(--text-muted, #4b5563);
  line-height: 1.75;
}

.button {
  --button-bg: var(--color-accent);
  --button-text: #ffffff;
  --button-border: transparent;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: 0 var(--space-4);
  border: 1px solid var(--button-border);
  border-radius: var(--radius-md);
  background: var(--button-bg, #2563eb);
  color: var(--button-text, #ffffff);
  font-weight: 700;
  cursor: pointer;
}

.button[data-variant="secondary"] {
  --button-bg: transparent;
  --button-text: var(--color-accent);
  --button-border: var(--color-accent);
}

HTML And JavaScript Demo

Drop this markup onto a page that loads the CSS above. The JavaScript uses data-theme for a manual override and falls back to prefers-color-scheme.

<section class="pricing-card">
  <h2>Claude Code UI Token Demo</h2>
  <p>
    This card uses CSS custom properties for surface, text, border,
    shadow, and button colors.
  </p>
  <button class="button" type="button" data-theme-toggle>
    Toggle theme
  </button>
  <button class="button" type="button" data-variant="secondary">
    Secondary action
  </button>
  <label>
    Accent color
    <input type="color" value="#2563eb" data-accent-picker>
  </label>
</section>
const root = document.documentElement;
const themeKey = "claude-code-css-variable-theme";
const savedTheme = localStorage.getItem(themeKey);
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;

function applyTheme(theme) {
  root.setAttribute("data-theme", theme);
  localStorage.setItem(themeKey, theme);
}

function toggleTheme() {
  const current = root.getAttribute("data-theme") || "light";
  applyTheme(current === "dark" ? "light" : "dark");
}

function setAccentColor(color) {
  if (CSS.supports("color", color)) {
    root.style.setProperty("--color-accent", color);
  }
}

applyTheme(savedTheme || (prefersDark ? "dark" : "light"));

document.querySelector("[data-theme-toggle]")?.addEventListener("click", toggleTheme);
document.querySelector("[data-accent-picker]")?.addEventListener("input", (event) => {
  setAccentColor(event.target.value);
});

MDN documents prefers-color-scheme and color-scheme. Use both: one detects user preference, the other tells the browser which built-in form and scrollbar colors are expected.

Practical Use Cases

Use case 1: article CTAs and landing pages. A site can test the CTA color by changing --color-accent once instead of editing every button, link, and card.

Use case 2: SaaS dashboards. Shared tokens keep spacing, borders, surfaces, and danger states consistent even when Claude Code generates multiple screens in separate sessions.

Use case 3: white-label branding. Add [data-brand="client-a"] and override only semantic tokens. The component CSS stays the same.

Use case 4: accessibility variants. Larger spacing, stronger contrast, and calmer motion can be expressed as state tokens, then reviewed alongside the accessibility guide.

Pitfalls To Review Critically

Undefined variables without fallbacks are the common failure. Prefer color: var(--text, #111827); for public pages.

Custom property names are case-sensitive. MDN’s custom properties reference is clear on this point, so keep token names lowercase and hyphenated.

Do not use custom properties for selectors, property names, or media query conditions. They are for values.

Validate JavaScript updates. If user input reaches setProperty(), check it with CSS.supports() first.

Review shadows and borders in dark mode, not only background and text colors. This is where many generated themes still look unfinished.

CTA And Verification

Ask Claude Code to review tokens explicitly:

{
  "reviewTarget": "CSS custom properties and theme implementation",
  "checks": [
    "undefined custom properties without fallback",
    "light and dark contrast issues",
    "component variants that bypass tokens",
    "duplicated raw colors outside :root",
    "JavaScript setProperty calls without validation",
    "mobile overflow caused by fixed spacing tokens"
  ],
  "output": "List findings with file name, selector, risk, and suggested fix."
}

For official product behavior, start with the Claude Code documentation. For repeatable daily work, keep the free Claude Code cheatsheet open. If you want reusable review and implementation prompts, use 50 Prompt Templates. For team setup, permissions, hooks, and CLAUDE.md structure, use the Setup Guide or Claude Code training and consultation.

After trying this workflow on article CTA cards, the most useful rule was simple: raw colors live in :root, components consume semantic tokens, and Claude Code reviews any exception. That made theme changes smaller and the generated diffs easier to trust.

Extra Production Review

Before publishing, ask Claude Code to compare the implementation against mobile width, keyboard use, long labels, ad slots, code blocks, and the main conversion path. The change is only successful when it improves maintainability without hiding the business action the page is supposed to drive.

#Claude Code #CSS variables #custom properties #design tokens #theming
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.