Usage

Always use semantic tokens instead of raw Tailwind colors. This ensures your UI automatically adapts to light and dark mode, and that your components remain consistent across themes.

Correct

<div className="bg-kumo-base text-kumo-default border-kumo-line">
  <button className="bg-kumo-brand text-white">Primary</button>
  <button className="bg-kumo-control text-kumo-default">Secondary</button>
</div>

Incorrect

{
  /* Never use raw Tailwind colors */
}
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
  <button className="bg-blue-500">Primary</button>
</div>;

Lint rules enforce this: The no-primitive-colors rule will flag any raw Tailwind colors like bg-blue-500.

Mode

Set data-mode on a parent element to control light/dark mode. Never use Tailwind’s dark: variant — semantic tokens handle dark mode automatically via CSS light-dark().

// Set mode on html or body
<html data-mode="light">  // Light mode
<html data-mode="dark">   // Dark mode

// Components automatically adapt - no dark: variants needed
<div className="bg-kumo-base text-kumo-default" />

Themes

Themes override semantic token values while preserving the same token names. Set data-theme on a parent element to apply a theme.

Available Themes

  • kumo — Default theme (no attribute needed)
  • fedramp — Government compliance styling
// Apply a theme to a section or the whole app
<div data-theme="fedramp">
  {/* All Kumo components inside use fedramp token overrides */}
  <Button>FedRAMP Styled</Button>
</div>

// Themes work with both light and dark mode
<html data-mode="dark" data-theme="fedramp">

Theme Generator

Themes are defined in a centralized config and generated as CSS files. The theme generator ensures consistency across all themes.

# List all tokens and their theme overrides
pnpm --filter @cloudflare/kumo codegen:themes --list

# Generate theme CSS files
pnpm --filter @cloudflare/kumo codegen:themes

# Preview changes without writing files
pnpm --filter @cloudflare/kumo codegen:themes --dry-run

Theme config: packages/kumo/scripts/theme-generator/config.ts

Creating a New Theme

Add theme overrides in the config file. Only override tokens that need to change — all other tokens inherit from the base kumo theme.

// In scripts/theme-generator/config.ts
export const THEME_CONFIG: ThemeConfig = {
  color: {
    "kumo-base": {
      newName: "",
      theme: {
        kumo: {
          light: "var(--color-white, #fff)",
          dark: "var(--color-black, #000)",
        },
        // Add your theme override
        myTheme: {
          light: "#f0f4f8",
          dark: "#1a1f2e",
        },
      },
    },
    // ... other tokens
  },
};

// Add to available themes
export const AVAILABLE_THEMES = ["kumo", "fedramp", "myTheme"] as const;

Then run pnpm codegen:themes to generate the CSS.

Semantic Tokens

Tokens are grouped by purpose. Use the token that matches the role of the element, not the color you want to achieve.

Semantic tokens are named by role, not by hue. A token like bg-kumo-danger communicates intent — it doesn’t imply a specific shade of red, and its exact value can change per theme or color mode without touching your component code.

Surface Hierarchy

Surfaces establish depth and layering in the UI. Use them in order from the outermost background inward.

TokenPurpose
bg-kumo-canvasThe outermost page background — sits behind everything
bg-kumo-baseDefault component background
bg-kumo-elevatedSlightly elevated surface, e.g. LayerCard.Secondary
bg-kumo-recessedRecessed surface with a subtly darker fill, e.g. segmented Tabs background
bg-kumo-tintSubtle tinted background for tables or hover states
bg-kumo-contrastHigh-contrast, inverted background

Brand

TokenPurpose
bg-kumo-brandPrimary brand background
bg-kumo-brand-hoverHover state for brand backgrounds

Semantic Status Colors

Each status color comes in two variants: a solid color for icons and indicators, and a -tint variant for background fills behind content.

TokenPurpose
bg-kumo-infoInfo indicator (icon, dot, bar)
bg-kumo-successSuccess indicator
bg-kumo-warningWarning indicator
bg-kumo-dangerError/destructive indicator

Use the solid token on icons, status dots, and progress fills. Banners and badges use the solid fills with a reduced opacity.

// Banner with tinted background and solid icon
<div className="bg-kumo-danger-tint text-kumo-danger">
  <AlertIcon className="text-kumo-danger" />
  <p>Something went wrong.</p>
</div>

Text Colors

TokenPurpose
text-kumo-defaultPrimary body text
text-kumo-strongSecondary text with slightly less contrast than default
text-kumo-subtleMuted text for descriptions, captions, or secondary labels
text-kumo-inactiveDisabled or inactive text
text-kumo-placeholderPlaceholder text in inputs
text-kumo-inverseText intended for use on high-contrast or inverted backgrounds
text-kumo-linkLink text
text-kumo-infoInfo-colored text
text-kumo-successSuccess-colored text
text-kumo-warningWarning-colored text
text-kumo-dangerError/destructive text

Borders & Rings

TokenPurpose
kumo-hairlineNewA border/ring color to distinguish between flat surfaces where no shadow is present (i.e. LayerCard).
kumo-lineA thicker border/ring color that defines the edge of an elevated surface alongside a shadow.

The distinction between line and fill reflects intent: line is for layout separators, fill is for element outlines. Mixing them can make UI feel inconsistent.

Token Reference

Toggle the theme in the header to see how tokens adapt. Tokens marked as “global” are explicit opt-in classes available regardless of theme.

Colors

Displaying 61 tokens

Text Colors (12)

--text-color-kumo-default
Lightvar(--color-neutral-900, oklch(21% 0.006 285.885))
Darkvar(--color-neutral-100, oklch(97% 0 0))
--text-color-kumo-inverse
Lightvar(--color-neutral-100, oklch(97% 0 0))
Darkvar(--color-neutral-900, oklch(20.5% 0 0))
--text-color-kumo-strong
Lightvar(--color-neutral-600, oklch(43.9% 0 0))
Darkvar(--color-neutral-400, oklch(70.8% 0 0))
--text-color-kumo-subtle
Lightvar(--color-neutral-500, oklch(55.6% 0 0))
Darkvar(--color-kumo-neutral-50, oklch(97.5% 0 0))
--text-color-kumo-inactive
Lightvar(--color-neutral-400, oklch(70.8% 0 0))
Darkvar(--color-neutral-600, oklch(70.8% 0 0))
--text-color-kumo-placeholder
Lightvar(--color-neutral-400, oklch(70.8% 0 0))
Darkvar(--color-neutral-500, oklch(55.6% 0 0))
--text-color-kumo-brand
Light#f6821f
Dark#f6821f
--text-color-kumo-link
Lightvar(--color-blue-800, oklch(42.4% 0.199 265.638))
Darkvar(--color-blue-400, oklch(70.7% 0.165 254.624))
--text-color-kumo-info
Lightvar(--color-blue-700, oklch(48.8% 0.243 264.376))
Darkvar(--color-blue-400, oklch(70.7% 0.165 254.624))
--text-color-kumo-success
Lightvar(--color-green-500, oklch(72.3% 0.219 149.579))
Darkvar(--color-green-400, oklch(79.2% 0.209 151.711))
--text-color-kumo-danger
Lightvar(--color-red-700, oklch(50.5% 0.213 27.518))
Darkvar(--color-red-400, oklch(70.4% 0.191 22.216))
--text-color-kumo-warning
Lightvar(--color-yellow-800, oklch(47.6% 0.114 61.907))
Darkvar(--color-yellow-400, oklch(85.2% 0.199 91.936))

Surface, State & Theme Colors (27)

--color-kumo-canvas
Lightvar(--color-kumo-neutral-25, oklch(98.75% 0 0))
Darkvar(--color-kumo-neutral-1000, oklch(10% 0 0))
--color-kumo-elevated
Lightvar(--color-kumo-neutral-75, oklch(98% 0 0))
Darkvar(--color-kumo-neutral-975, oklch(12% 0 0))
--color-kumo-recessed
Lightvar(--color-kumo-neutral-125, oklch(96% 0 0))
Darkvar(--color-kumo-neutral-950, oklch(15% 0 0))
--color-kumo-base
Lightvar(--color-white, #fff)
Darkvar(--color-kumo-neutral-925, oklch(17% 0 0))
--color-kumo-tint
Lightvar(--color-neutral-100, oklch(97% 0 0))
Darkvar(--color-kumo-neutral-800, oklch(26.9% 0 0))
--color-kumo-contrast
Lightvar(--color-kumo-neutral-975, oklch(8.5% 0 0))
Darkvar(--color-kumo-neutral-25, oklch(98.5% 0 0))
--color-kumo-overlay
Lightvar(--color-kumo-neutral-50, oklch(97.5% 0 0))
Darkvar(--color-neutral-800, oklch(26.9% 0 0))
--color-kumo-control
Lightvar(--color-white, #fff)
Darkvar(--color-neutral-900, oklch(21% 0.006 285.885))
--color-kumo-interact
Lightvar(--color-neutral-300, oklch(87% 0 0))
Darkvar(--color-neutral-700, oklch(37.1% 0 0))
--color-kumo-fill
Lightvar(--color-neutral-200, oklch(92.2% 0 0))
Darkvar(--color-neutral-800, oklch(26.9% 0 0))
--color-kumo-fill-hover
Lightvar(--color-kumo-neutral-125, oklch(96.5% 0 0))
Darkvar(--color-neutral-800, oklch(37.1% 0 0))
--color-kumo-brand
Lightoklch(0.5772 0.2324 260)
Darkoklch(0.5772 0.2324 260)
--color-kumo-brand-hover
Lightvar(--color-blue-700, oklch(48.8% 0.243 264.376))
Darkvar(--color-blue-700, oklch(48.8% 0.243 264.376))
--color-kumo-line
Lightoklch(14.5% 0 0 / 0.1)
Darkvar(--color-neutral-800, oklch(26.9% 0 0))
--color-kumo-hairline
Lightvar(--color-kumo-neutral-150, oklch(93.5% 0 0))
Darkvar(--color-neutral-700, oklch(37.1% 0 0))
--color-kumo-shadow-edge
Lightoklch(0% 0 0 / 0.12)
Darkoklch(100% 0 0 / 0.1)
--color-kumo-shadow-drop
Lightoklch(0% 0 0 / 0.08)
Darkoklch(0% 0 0 / 0.3)
--color-kumo-tip-shadow
Lightvar(--color-gray-200, oklch(92.8% 0.006 264.531))
Darktransparent
--color-kumo-tip-stroke
Lighttransparent
Darkvar(--color-neutral-800, oklch(26.9% 0 0))
--color-kumo-info
Lightvar(--color-blue-500, oklch(62.3% 0.214 259.815))
Darkvar(--color-blue-400, oklch(70.7% 0.165 254.624))
--color-kumo-info-tint
Lightvar(--color-blue-300, oklch(80.9% 0.105 251.813))
Darkvar(--color-blue-900, oklch(37.9% 0.146 265.522))
--color-kumo-warning
Lightvar(--color-yellow-500, oklch(79.5% 0.184 86.047))
Darkvar(--color-yellow-700, oklch(55.4% 0.135 66.442))
--color-kumo-warning-tint
Lightvar(--color-yellow-300, oklch(90.5% 0.182 98.111))
Darkvar(--color-yellow-900, oklch(42.1% 0.095 57.708))
--color-kumo-danger
Lightvar(--color-red-500, oklch(63.7% 0.237 25.331))
Darkvar(--color-red-700, oklch(50.5% 0.213 27.518))
--color-kumo-danger-tint
Lightvar(--color-red-300, oklch(80.8% 0.114 19.571))
Darkvar(--color-red-900, oklch(39.6% 0.141 25.723))
--color-kumo-success
Lightvar(--color-green-500, oklch(72.3% 0.219 149.579))
Darkvar(--color-green-700, oklch(52.7% 0.154 150.069))
--color-kumo-success-tint
Lightvar(--color-green-300, oklch(87.1% 0.15 154.449))
Darkvar(--color-green-900, oklch(39.3% 0.095 152.535))

Component Colors (22)

Badge (22)

--text-color-kumo-badge-red-subtle
Lightvar(--color-red-800, oklch(44.4% 0.177 26.899))
Darkvar(--color-red-200, oklch(88.5% 0.062 18.334))
--text-color-kumo-badge-orange-subtle
Lightvar(--color-orange-800, oklch(47% 0.157 37.304))
Darkvar(--color-orange-200, oklch(90.1% 0.076 70.697))
--text-color-kumo-badge-yellow-subtle
Lightvar(--color-yellow-800, oklch(47.6% 0.114 61.907))
Darkvar(--color-yellow-200, oklch(94.5% 0.129 101.54))
--text-color-kumo-badge-green-subtle
Lightvar(--color-emerald-800, oklch(43.2% 0.095 166.913))
Darkvar(--color-emerald-200, oklch(90.5% 0.093 164.15))
--text-color-kumo-badge-teal-subtle
Lightvar(--color-teal-800, oklch(43.7% 0.078 188.216))
Darkvar(--color-teal-200, oklch(91% 0.096 180.426))
--text-color-kumo-badge-blue-subtle
Lightvar(--color-blue-800, oklch(42.4% 0.199 265.638))
Darkvar(--color-blue-200, oklch(88.2% 0.059 254.128))
--text-color-kumo-badge-neutral-subtle
Lightvar(--color-neutral-800, oklch(26.9% 0 0))
Darkvar(--color-neutral-200, oklch(92.2% 0 0))
--text-color-kumo-badge-inverted
Lightvar(--color-white, #fff)
Darkvar(--color-black, #000)
--color-kumo-badge-red
Lightvar(--color-red-600, oklch(57.7% 0.245 27.325))
Darkvar(--color-red-700, oklch(50.5% 0.213 27.518))
--color-kumo-badge-red-subtle
Lightvar(--color-red-100, oklch(93.6% 0.032 17.717))
Darkvar(--color-red-900, oklch(39.6% 0.141 25.723))
--color-kumo-badge-orange
Lightvar(--color-orange-600, oklch(64.6% 0.222 41.116))
Darkvar(--color-orange-700, oklch(55.3% 0.195 38.402))
--color-kumo-badge-orange-subtle
Lightvar(--color-orange-100, oklch(95.4% 0.038 75.164))
Darkvar(--color-orange-900, oklch(40.8% 0.123 38.172))
--color-kumo-badge-yellow
Lightvar(--color-yellow-600, oklch(68.1% 0.162 75.834))
Darkvar(--color-yellow-700, oklch(55.4% 0.135 66.442))
--color-kumo-badge-yellow-subtle
Lightvar(--color-yellow-100, oklch(97.3% 0.071 103.193))
Darkvar(--color-yellow-900, oklch(42.1% 0.095 57.708))
--color-kumo-badge-green
Lightvar(--color-emerald-600, oklch(59.6% 0.145 163.225))
Darkvar(--color-emerald-700, oklch(50.8% 0.118 165.612))
--color-kumo-badge-green-subtle
Lightvar(--color-emerald-100, oklch(95% 0.052 163.051))
Darkvar(--color-emerald-900, oklch(37.8% 0.077 168.94))
--color-kumo-badge-teal
Lightvar(--color-teal-600, oklch(60% 0.118 184.704))
Darkvar(--color-teal-700, oklch(51.1% 0.096 186.391))
--color-kumo-badge-teal-subtle
Lightvar(--color-teal-100, oklch(95.3% 0.051 180.801))
Darkvar(--color-teal-900, oklch(38.6% 0.063 188.416))
--color-kumo-badge-blue
Lightvar(--color-blue-600, oklch(54.6% 0.245 262.881))
Darkvar(--color-blue-700, oklch(48.8% 0.243 264.376))
--color-kumo-badge-blue-subtle
Lightvar(--color-blue-100, oklch(93.2% 0.032 255.585))
Darkvar(--color-blue-900, oklch(37.9% 0.146 265.522))
--color-kumo-badge-neutral
Lightvar(--color-neutral-600, oklch(43.9% 0 0))
Darkvar(--color-neutral-600, oklch(43.9% 0 0))
--color-kumo-badge-inverted
Lightvar(--color-neutral-950, oklch(14.5% 0 0))
Darkvar(--color-white, #fff)