Colors

Semantic color tokens used throughout the design system.

Color defines the visual tone of a design system. Ink UI uses a palette built around warm gray and orange, expanded into full scales in the OKLCH color space for consistent luminance and accessibility across light and dark modes.

The gray carries a subtle warm bias — warmer than neutral, but less pronounced than Tailwind's Stone — providing a balanced foundation. Orange serves as the primary accent, adding energy and warmth that complements the overall palette.

Gray

A warm gray scale for text, backgrounds, borders, and subtle fills across light and dark modes.

50
100
200
300
400
500
600
700
800
900
950

Red

A red scale for destructive actions, errors, and warning states.

50
100
200
300
400
500
600
700
800
900
950

Orange

The primary accent color for focus rings, active states, and blockquote accents.

50
100
200
300
400
500
600
700
800
900
950

Semantic tokens

These CSS variables map to the scales above and adapt between light and dark modes via the data-theme attribute.

Backgrounds

backgroundPage background
foregroundPrimary text color
cardCard surface background
card-foregroundText on card surfaces
popoverPopover surface background
popover-foregroundText inside popovers
dialogDialog surface background
dialog-foregroundText inside dialogs

Actions

primaryPrimary button / action background
primary-foregroundText on primary actions
secondarySecondary button / muted action background
secondary-foregroundText on secondary actions

States

mutedMuted / subtle background
muted-foregroundText on muted backgrounds
accentAccent highlight background
accent-foregroundText on accent highlights
destructiveDestructive action background
destructive-foregroundText on destructive actions

Borders & focus

borderDefault border color
inputForm input border color
ringFocus ring color

Common Tailwind utilities like text-gray-500, bg-gray-100, border-gray-200, and dark: variants map to the underlying Ink UI theme tokens.

Interactive states

Every surface token ships a -hover and -active companion for interactive elements. Rather than fading a base color with opacity — which composites unpredictably against whatever sits behind it and muddies stacked surfaces — each state is a fixed, opaque step along the palette scale. The values are deliberately tuned per token: light surfaces darken toward the content, dark surfaces brighten, and solid fills move toward the mid-scale so the state always reads as a real change regardless of background.

Use them directly as Tailwind utilities — hover:bg-primary-hover, data-popup-open:bg-secondary-active, and so on — so the light/dark logic lives in the theme instead of being restated in every component.

backgroundBase
background-hoverHover
background-activeActive
cardBase
card-hoverHover
card-activeActive
popoverBase
popover-hoverHover
popover-activeActive
dialogBase
dialog-hoverHover
dialog-activeActive
primaryBase
primary-hoverHover
primary-activeActive
secondaryBase (overlay tint)
secondary-hoverHover
secondary-activeActive
mutedBase
muted-hoverHover
muted-activeActive
accentBase
accent-hoverHover
accent-activeActive
destructiveBase
destructive-hoverHover
destructive-activeActive

secondary (and the ghost / outline surfaces built on it) stays alpha-based on purpose: it's an overlay tint meant to composite over varying backgrounds, so its -hover and -active steps deepen the alpha rather than swapping to a solid color.

Installation

Copy the CSS code into your global stylesheet, wrapped in a selector that applies the appropriate color scheme based on the data-theme attribute.

css
@theme {
  /* gray */
  --color-gray-50: oklch(0.98559 0.002 48.697);
  --color-gray-100: oklch(0.95 0.001 48.697);
  --color-gray-200: oklch(0.925 0.002 48.697);
  --color-gray-300: oklch(0.84598 0.002 48.697);
  --color-gray-400: oklch(0.7374 0.002 48.697);
  --color-gray-500: oklch(0.585 0.003 48.697);
  --color-gray-600: oklch(0.46 0.002 48.697);
  --color-gray-700: oklch(0.37 0.002 48.697);
  --color-gray-800: oklch(0.31 0.001 48.697);
  --color-gray-900: oklch(0.265 0.001 48.697);
  --color-gray-950: oklch(0.21 0.002 48.697);

  /* red */
  --color-red-50: oklch(0.96453 0.015 24);
  --color-red-100: oklch(0.95036 0.03 24);
  --color-red-200: oklch(0.92601 0.045 24);
  --color-red-300: oklch(0.8455 0.07 24);
  --color-red-400: oklch(0.73813 0.135 24);
  --color-red-500: oklch(0.63497 0.18 24);
  --color-red-600: oklch(0.50144 0.15 24);
  --color-red-700: oklch(0.38998 0.125 24);
  --color-red-800: oklch(0.30985 0.095 24);
  --color-red-900: oklch(0.26461 0.06 24);
  --color-red-950: oklch(0.21016 0.04 24);

  /* orange */
  --color-orange-50: oklch(0.96453 0.02 41.5);
  --color-orange-100: oklch(0.95036 0.025 41.5);
  --color-orange-200: oklch(0.92601 0.03 41.5);
  --color-orange-300: oklch(0.8455 0.07 41.5);
  --color-orange-400: oklch(0.73813 0.14 41.5);
  --color-orange-500: oklch(0.63497 0.17 41.5);
  --color-orange-600: oklch(0.50144 0.135 41.5);
  --color-orange-700: oklch(0.38998 0.08 41.5);
  --color-orange-800: oklch(0.30985 0.065 41.5);
  --color-orange-900: oklch(0.26461 0.055 41.5);
  --color-orange-950: oklch(0.21016 0.04 41.5);
}

@layer base {
  :root {
    color-scheme: light;
    --background: oklch(0.98559 0.002 48.697);
    --foreground: oklch(0.265 0.001 48.697);
    --card: oklch(0.95 0.001 48.697);
    --card-foreground: oklch(0.265 0.001 48.697);
    --popover: oklch(1 0 0);
    --popover-foreground: oklch(0.265 0.001 48.697);
    --dialog: oklch(0.98559 0.002 48.697);
    --dialog-foreground: oklch(0.265 0.001 48.697);
    --primary: oklch(0.265 0.001 48.697);
    --primary-foreground: oklch(1 0 0);
    --secondary: oklch(0.21 0.002 48.697 / 0.08);
    --secondary-foreground: oklch(0.265 0.001 48.697);
    --muted: oklch(0.95 0.001 48.697);
    --muted-foreground: oklch(0.585 0.003 48.697);
    --accent: oklch(0.63497 0.17 41.5);
    --accent-foreground: oklch(1 0 0);
    --destructive: oklch(0.63497 0.18 24);
    --destructive-foreground: oklch(1 0 0);
    --border: oklch(0.21 0.002 48.697 / 0.1);
    --input: oklch(0.21 0.002 48.697 / 0.1);
    --ring: oklch(0.63497 0.17 41.5 / 0.3);

    /* surface state variants — stepped down the palette scale */
    --background-hover: var(--color-gray-100);
    --background-active: var(--color-gray-200);
    --card-hover: var(--color-gray-200);
    --card-active: var(--color-gray-300);
    --popover-hover: var(--color-gray-50);
    --popover-active: var(--color-gray-100);
    --dialog-hover: var(--color-gray-100);
    --dialog-active: var(--color-gray-200);
    --primary-hover: var(--color-gray-700);
    --primary-active: var(--color-gray-600);
    --secondary-hover: oklch(0.21 0.002 48.697 / 0.16);
    --secondary-active: oklch(0.21 0.002 48.697 / 0.22);
    --muted-hover: var(--color-gray-200);
    --muted-active: var(--color-gray-300);
    --accent-hover: var(--color-orange-600);
    --accent-active: var(--color-orange-700);
    --destructive-hover: var(--color-red-600);
    --destructive-active: var(--color-red-700);

    --chart-1: var(--orange-500, oklch(0.63497 0.17 41.5));
    --chart-2: var(--red-500, oklch(0.63497 0.18 24));
    --chart-3: oklch(0.585 0.003 48.697);
    --chart-4: oklch(0.73813 0.14 41.5);
    --chart-5: oklch(0.8455 0.07 24);
    --sidebar: var(--card);
    --sidebar-foreground: var(--foreground);
    --sidebar-primary: var(--primary);
    --sidebar-primary-foreground: var(--primary-foreground);
    --sidebar-accent: var(--secondary);
    --sidebar-accent-foreground: var(--secondary-foreground);
    --sidebar-border: var(--border);
    --sidebar-ring: var(--ring);
  }

  :root[data-theme="dark"],
  .dark {
    color-scheme: dark;
    --background: oklch(0.21 0.002 48.697);
    --foreground: oklch(0.95 0.001 48.697);
    --card: oklch(0.21 0.002 48.697);
    --card-foreground: oklch(0.95 0.001 48.697);
    --popover: oklch(0.265 0.001 48.697);
    --popover-foreground: oklch(0.95 0.001 48.697);
    --dialog: oklch(0.265 0.001 48.697);
    --dialog-foreground: oklch(0.95 0.001 48.697);
    --primary: oklch(0.95 0.001 48.697);
    --primary-foreground: oklch(0.265 0.001 48.697);
    --secondary: oklch(0.98559 0.002 48.697 / 0.08);
    --secondary-foreground: oklch(0.95 0.001 48.697);
    --muted: oklch(0.31 0.001 48.697);
    --muted-foreground: oklch(0.7374 0.002 48.697);
    --accent: oklch(0.50144 0.135 41.5);
    --accent-foreground: oklch(1 0 0);
    --destructive: oklch(0.63497 0.18 24);
    --destructive-foreground: oklch(1 0 0);
    --border: oklch(0.98559 0.002 48.697 / 0.1);
    --input: oklch(0.98559 0.002 48.697 / 0.1);
    --ring: oklch(0.63497 0.17 41.5 / 0.3);

    /* surface state variants — stepped toward the lighter end of the palette */
    --background-hover: var(--color-gray-900);
    --background-active: var(--color-gray-800);
    --card-hover: var(--color-gray-900);
    --card-active: var(--color-gray-800);
    --popover-hover: var(--color-gray-800);
    --popover-active: var(--color-gray-700);
    --dialog-hover: var(--color-gray-800);
    --dialog-active: var(--color-gray-700);
    --primary-hover: var(--color-gray-300);
    --primary-active: var(--color-gray-400);
    --secondary-hover: oklch(0.98559 0.002 48.697 / 0.16);
    --secondary-active: oklch(0.98559 0.002 48.697 / 0.22);
    --muted-hover: var(--color-gray-700);
    --muted-active: var(--color-gray-600);
    --accent-hover: var(--color-orange-500);
    --accent-active: var(--color-orange-400);
    --destructive-hover: var(--color-red-400);
    --destructive-active: var(--color-red-300);

    --chart-1: oklch(0.50144 0.135 41.5);
    --chart-2: oklch(0.63497 0.18 24);
    --chart-3: oklch(0.7374 0.002 48.697);
    --chart-4: oklch(0.38998 0.08 41.5);
    --chart-5: oklch(0.8455 0.07 24);
    --sidebar: var(--card);
    --sidebar-foreground: var(--foreground);
    --sidebar-primary: var(--primary);
    --sidebar-primary-foreground: var(--primary-foreground);
    --sidebar-accent: var(--secondary);
    --sidebar-accent-foreground: var(--secondary-foreground);
    --sidebar-border: var(--border);
    --sidebar-ring: var(--ring);
  }

  * {
    border-color: var(--border);
    outline-color: color-mix(in oklab, var(--ring) 50%, transparent);
  }

  body {
    color: var(--foreground);
    background: var(--background);
  }
}

@theme inline {
  --color-background: var(--background);
  --color-background-hover: var(--background-hover);
  --color-background-active: var(--background-active);
  --color-foreground: var(--foreground);
  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);
  --color-card-hover: var(--card-hover);
  --color-card-active: var(--card-active);
  --color-popover: var(--popover);
  --color-popover-foreground: var(--popover-foreground);
  --color-popover-hover: var(--popover-hover);
  --color-popover-active: var(--popover-active);
  --color-dialog: var(--dialog);
  --color-dialog-foreground: var(--dialog-foreground);
  --color-dialog-hover: var(--dialog-hover);
  --color-dialog-active: var(--dialog-active);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-primary-hover: var(--primary-hover);
  --color-primary-active: var(--primary-active);
  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-secondary-hover: var(--secondary-hover);
  --color-secondary-active: var(--secondary-active);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);
  --color-muted-hover: var(--muted-hover);
  --color-muted-active: var(--muted-active);
  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);
  --color-accent-hover: var(--accent-hover);
  --color-accent-active: var(--accent-active);
  --color-destructive: var(--destructive);
  --color-destructive-foreground: var(--destructive-foreground);
  --color-destructive-hover: var(--destructive-hover);
  --color-destructive-active: var(--destructive-active);
  --color-chart-1: var(--chart-1);
  --color-chart-2: var(--chart-2);
  --color-chart-3: var(--chart-3);
  --color-chart-4: var(--chart-4);
  --color-chart-5: var(--chart-5);
  --color-sidebar: var(--sidebar);
  --color-sidebar-foreground: var(--sidebar-foreground);
  --color-sidebar-primary: var(--sidebar-primary);
  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
  --color-sidebar-accent: var(--sidebar-accent);
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
  --color-sidebar-border: var(--sidebar-border);
  --color-sidebar-ring: var(--sidebar-ring);
  --color-border: var(--border);
  --color-input: var(--input);
  --color-ring: var(--ring);
}