PageWeave/Design

Skills / Color Systems

11 Skill skillcoloroklchpalettecontrastdark-modethemesemantic .md

Color Systems

OKLCH palettes, semantic roles, and contrast-safe themes

Author

saifyxpro

Year

2026

Install as SKILL.md

curl -o skills/color-systems/SKILL.md /skills/color-systems.md

Build color systems that are perceptually uniform, accessible, and themeable. Work in OKLCH — it matches how humans see lightness and color, unlike hex or HSL.

Why OKLCH

  • Perceptual uniformity: equal lightness steps look equally different. L 0.9 to L 0.8 feels the same jump as L 0.5 to L 0.4.
  • Polar coordinates: hue is a degree (0–360), chroma is saturation (0–0.4 typical), lightness is 0–1.
  • No gamut clipping: unlike HSL, OKLCH colors that look the same brightness actually ARE the same brightness.
  • Better contrast prediction: if two colors have ≥ 0.4 lightness difference, they almost certainly pass WCAG AA.

The Primitive Palette

Build a scale from 50 to 950 for each hue. Keep chroma consistent within a hue family.

/* Neutral scale (chroma 0.01–0.02, hue toward brand) */
--gray-50:  oklch(0.98 0.005 240);
--gray-100: oklch(0.96 0.008 240);
--gray-200: oklch(0.92 0.01 240);
--gray-300: oklch(0.87 0.012 240);
--gray-400: oklch(0.71 0.015 240);
--gray-500: oklch(0.55 0.02 240);
--gray-600: oklch(0.45 0.02 240);
--gray-700: oklch(0.37 0.02 240);
--gray-800: oklch(0.27 0.015 240);
--gray-900: oklch(0.2 0.01 240);
--gray-950: oklch(0.14 0.008 240);

/* Brand scale (chroma 0.12–0.2, consistent hue) */
--blue-50:  oklch(0.97 0.02 240);
--blue-100: oklch(0.93 0.04 240);
--blue-200: oklch(0.88 0.08 240);
--blue-300: oklch(0.8 0.12 240);
--blue-400: oklch(0.7 0.16 240);
--blue-500: oklch(0.55 0.2 240);   /* primary */
--blue-600: oklch(0.48 0.2 240);
--blue-700: oklch(0.4 0.18 240);
--blue-800: oklch(0.32 0.14 240);
--blue-900: oklch(0.25 0.1 240);

Rules:

  • Keep hue consistent within a family (±5° max).
  • Increase chroma toward the middle of the scale (300–500), decrease at extremes.
  • Neutral scales: chroma 0.005–0.02. Tint toward brand hue, not toward warmth-by-default.

The Semantic Layer

Map primitives to roles. This layer changes for dark mode; primitives stay.

/* Foreground hierarchy */
--fg-primary: var(--gray-900);      /* headings, body text */
--fg-secondary: var(--gray-600);    /* descriptions, labels */
--fg-tertiary: var(--gray-400);     /* hints, placeholders */
--fg-muted: var(--gray-300);        /* disabled, decorative */

/* Background elevation */
--bg-base: var(--gray-50);          /* page background */
--bg-raised: var(--white);          /* cards, modals */
--bg-sunken: var(--gray-100);       /* inputs, code blocks */
--bg-overlay: oklch(0 0 0 / 0.5);  /* modal backdrop */

/* Border progression */
--border-subtle: var(--gray-200);   /* card borders, dividers */
--border-default: var(--gray-300);  /* input borders */
--border-strong: var(--gray-400);   /* focus rings */

/* Brand */
--color-primary: var(--blue-500);
--color-primary-hover: var(--blue-600);
--color-primary-active: var(--blue-700);
--color-primary-content: var(--white);

/* Semantic feedback */
--color-success: oklch(0.6 0.17 155);
--color-warning: oklch(0.75 0.16 85);
--color-error: oklch(0.6 0.2 25);
--color-info: oklch(0.6 0.15 240);

Dark Mode

Swap semantics, keep primitives:

[data-theme="dark"] {
  --fg-primary: var(--gray-100);
  --fg-secondary: var(--gray-400);
  --fg-tertiary: var(--gray-500);
  --bg-base: var(--gray-950);
  --bg-raised: var(--gray-900);
  --bg-sunken: var(--gray-800);
  --border-subtle: var(--gray-800);
  --border-default: var(--gray-700);
  --color-primary: var(--blue-400);
  --color-primary-hover: var(--blue-300);
  --color-primary-content: var(--gray-950);
}

Rules:

  • Dark mode primary is often 1–2 steps lighter than light mode (better contrast on dark bg).
  • Semantic colors (success/warning/error) often need desaturation in dark mode to avoid vibrating.
  • color-scheme: dark; is mandatory for PageWeave dark themes.

Contrast Verification

The fast OKLCH contrast check: lightness difference ≥ 0.4 almost always passes WCAG AA (4.5:1).

For precise checks:

  • Body text (normal): ≥ 4.5:1 against background.
  • Large text (≥ 18px or bold ≥ 14px): ≥ 3:1.
  • UI components: ≥ 3:1 against adjacent colors.
  • Focus indicators: ≥ 3:1 against both background and adjacent colors.

Every *-content token must pass contrast against its parent:

  • --color-primary-content against --color-primary.
  • --fg-primary against --bg-base and --bg-raised.
  • --fg-secondary against --bg-base.

Color Usage Rules

  1. One accent color, used with intention. One primary color carries action. Secondary and accent are for rare emphasis.
  2. Semantic colors are functional. Success/warning/error appear in feedback contexts only — never decoration.
  3. Opacity modifiers over new colors. text-base-content/60 gives you a dimmed version that survives theming. Don't create --text-dim when opacity works.
  4. Never use color alone to convey information. Pair with text, icon, or pattern.
  5. Test the full chain. primary → primary-hover → primary-active → primary-content. Each step should be visibly distinct.

Palette Anti-Patterns

  • Purple-blue gradient: the most common AI color scheme. Avoid.
  • Single-hue palettes: everything blue, or everything green. Use neutrals for structure, color for emphasis.
  • Rainbow palettes: 6+ distinct hues in one interface. Stick to 1 brand hue + neutrals + semantic colors.
  • Hardcoded hex in components: bg-[#6366f1] doesn't survive theming. Use bg-primary.
  • Warm-by-default neutrals: tinting neutrals toward warmth "because the brand feels warm" is the monoculture move. Tint toward the brand's actual hue.
  • Similar lightness for text and background: OKLCH L difference < 0.3 will fail contrast. Check before shipping.

PageWeave Workflow

  1. Define primitives and semantics in OKLCH.
  2. Pass bare variables to update_theme(website, config: { css }) — no [data-theme] wrapper.
  3. Dark themes: include color-scheme: dark;.
  4. Test: screenshot a page with buttons, badges, inputs, tables, and an alert — all five exercise semantic colors.
  5. Verify contrast for every text/background pair.
  6. Check .md view: code blocks inherit theme colors.