How DESIGN.md Handles Hex, RGB, Oklch, and Named Colors in Its CSS Parser

DESIGN.md normalizes every CSS color format—from legacy hex codes to modern Oklch values—into a canonical sRGB representation using the parseCssColor function, enabling consistent accessibility linting and token validation across the design system.

The DESIGN.md linter processes color values through a dedicated parser that supports the full CSS Color Module Level 4 specification. Located in packages/cli/src/linter/model/color-parser.ts, this utility converts hex, RGB, Oklch, named colors, and functional notations into a standardized ParsedColorResult structure. This normalization allows downstream lint rules to analyze contrast ratios and color relationships using a unified interface regardless of the original input format.

The Entry Point: parseCssColor

The parser exposes a single entry function that dispatches to specialized handlers based on the input string's format.

export function parseCssColor(colorStr: string, depth = 0): ParsedColorResult | null

The function first normalizes the input by trimming whitespace and converting to lowercase. It then rejects values that exceed the maximum recursion depth of 32, which prevents infinite loops when parsing nested color-mix expressions. Based on the leading characters, the parser routes the string to one of several format-specific handlers.

Supported Color Format Dispatch

The parser recognizes color types through the following detection patterns:

Detected form Handler
#… Hex (parseHex)
Named string Named-color lookup (CSS_NAMED_COLORS)
rgb(…), rgba(…) RGB / RGBA
hsl(…), hsla(…) HSL / HSLA
hwb(…) HWB
lab(…) CIE Lab
lch(…) CIE Lch
oklab(…) Oklab
oklch(…) Oklch
color‑mix(…) Recursive color blending

If no pattern matches, the function returns null, signaling an invalid color value to the caller.

Hex Value Handling

The parseHex implementation in packages/cli/src/linter/model/color-parser.ts supports all four CSS hex forms: #RGB, #RGBA, #RRGGBB, and #RRGGBBAA. Short forms expand to 6- or 8-digit hex before parsing.

The parser extracts red, green, blue, and optional alpha components using parseInt(..., 16), then constructs the result object. For example, a 3-digit hex code expands to 6 digits by duplicating each character.

import { parseCssColor } from './color-parser';

const result = parseCssColor('#0f8');
console.log(result?.hex); // "#00ff88"

This expansion occurs in the parseHex function within lines 66-84 of the color parser source file.

Named Color Support

The parser consults a static map called CSS_NAMED_COLORS that enumerates every CSS Level 4 named color. When the lower-cased input matches a key in this map, the parser reuses parseHex on the stored hex string.

const result = parseCssColor('rebeccapurple');
console.log(result?.hex); // "#663399"

The complete mapping—from standard colors like red to transparent (mapped to #00000000)—resides in lines 24-56 of color-parser.ts.

RGB and RGBA Functional Notations

Functional notations utilize a tokenizeFunc helper that separates the function name from its arguments while respecting nested parentheses and the optional / alpha separator. For RGB and RGBA values, the parsePercentOrNumber helper converts arguments to 0-255 values, while parseAlpha handles the optional opacity channel.

const result = parseCssColor('rgb(100% 0% 0% / 0.5)');
/*
{
  hex: '#ff000080',  // 8-digit because alpha < 1
  r: 255,
  g: 0,
  b: 0,
  a: 0.5,
  luminance: …
}
*/

Oklch and Oklab (Perceptual Color Spaces)

The parser handles modern perceptual color spaces through dedicated conversion functions. For Oklch, the parser normalizes the hue component using parseHue (accepting deg, grad, rad, turn, or bare numbers) before converting to sRGB via oklchToRgb.

const result = parseCssColor('oklch(0.7 0.15 120deg)');
/*
{
  hex: '#7ac57c',
  r: 122,
  g: 197,
  b: 124,
  luminance: …
}
*/

Similarly, Oklab values process through oklabToRgb to produce the canonical sRGB result.

Additional Functional Notations

The parser supports several other CSS color functions through specific conversion pipelines:

  • HSL / HSLA: Hue normalization via parseHue, saturation and lightness parsed as percentages (0-1 range), then converted through hslToRgb.
  • HWB: Hue, whiteness, and blackness values convert via hwbToRgb.
  • Lab / Lch: CIE Lab values process through labToRgb, while CIE Lch uses lchToRgb.

All functional branches terminate by calling makeResult, which constructs the final ParsedColorResult object with normalized hex, RGB components, and calculated luminance.

Color-Mix for Recursive Blending

The color-mix notation enables recursive blending of two colors. The parser evaluates this by recursively calling parseCssColor on each constituent color, then blending them using premultiplied alpha while respecting optional weight percentages.

const result = parseCssColor('color-mix(in srgb, #ff0000 30%, oklch(0.5 0.2 240deg) 70%)');
/* Returns a blended sRGB color, e.g. hex "#c44d9b" */

This recursive evaluation respects the 32-level depth limit to prevent stack overflow.

Normalized Result Structure

Every color format ultimately resolves to a ParsedColorResult interface defined in the model layer:

export interface ParsedColorResult {
  hex: string;       // lower-case hex, 6-digit (or 8-digit if alpha < 1)
  r: number;         // 0-255
  g: number;
  b: number;
  a?: number;        // 0-1 (omitted if 1)
  luminance: number; // WCAG relative luminance
}

The luminance field stores the WCAG relative luminance calculated from the sRGB values, enabling contrast ratio calculations without re-parsing the original color string.

Integration with the Linter Model

The ModelSpec class in packages/cli/src/linter/model/spec.ts orchestrates the parser integration. When processing a DESIGN.md document, the model walker identifies tokens with type 'color' and invokes parseCssColor. Valid colors populate the DesignSystemState.colors map and the flattened symbolTable for fast lookups.

The validation helper isValidColor in spec.ts (lines 66-71) simply forwards to parseCssColor, returning a boolean indicating whether the string represents a valid CSS color.

Lint rules access the resolved color objects directly—querying color.hex, color.r, color.g, color.b, and color.luminance—to perform accessibility checks and token consistency validation.

Summary

  • DESIGN.md handles color formats through a centralized parser in packages/cli/src/linter/model/color-parser.ts that converts all inputs to canonical sRGB.
  • The parseCssColor function supports hex (3, 4, 6, 8-digit), named colors, RGB/RGBA, HSL/HSLA, HWB, Lab, Lch, Oklab, Oklch, and color-mix.
  • All colors normalize to a ParsedColorResult containing hex, RGB components, optional alpha, and WCAG luminance.
  • The ModelSpec class integrates the parser into the linting pipeline, storing results in the design system state for downstream rules.

Frequently Asked Questions

What happens if DESIGN.md encounters an invalid color format?

The parseCssColor function returns null when the input string does not match any recognized color pattern or exceeds the recursion depth limit for color-mix expressions. The isValidColor wrapper in spec.ts converts this to a boolean false, allowing lint rules to flag invalid color tokens without throwing exceptions.

Does the parser support alpha transparency?

Yes, the parser handles alpha channels across all supported formats. For hex codes, it processes 8-digit #RRGGBBAA and 4-digit #RGBA notations. For functional notations, it parses the optional / alpha separator. When alpha is less than 1, the resulting ParsedColorResult includes an a property (0-1 range) and produces an 8-digit hex string.

How does DESIGN.md handle perceptually uniform color spaces like Oklch?

The parser implements dedicated conversion functions oklchToRgb and oklabToRgb that transform these modern color spaces into sRGB values. Unlike legacy RGB, these spaces provide uniform lightness perception, and the parser normalizes hue angles (supporting deg, grad, rad, turn units) before conversion to ensure accurate results.

Can I use CSS color-mix() expressions in DESIGN.md?

Yes, the parser supports color-mix(in srgb, color1, color2) syntax with optional percentage weights. It recursively evaluates the component colors using parseCssColor with a depth counter, blends them using premultiplied alpha, and returns a single ParsedColorResult representing the mixed color.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →