Skip to content

Latest commit

 

History

History
96 lines (67 loc) · 4.89 KB

File metadata and controls

96 lines (67 loc) · 4.89 KB

Color System

Why OKLch

Verdigris uses OKLch as the canonical color space. All color tokens are defined in OKLch; other formats (HSL, hex, RGB) are generated by the build pipeline.

Why not HSL?

HSL is perceptually non-uniform — hsl(60, 100%, 50%) (yellow) appears far brighter than hsl(240, 100%, 50%) (blue) despite identical L values. This makes palette interpolation unpredictable and accessibility auditing unreliable.

OKLch fixes this:

  • L (lightness) is perceptually linear — equal numeric changes produce equal visual changes
  • C (chroma) controls saturation without shifting perceived brightness
  • h (hue) rotates through the color wheel

This means Patina's gradient palette (teal → purple → red → yellow) was generated by rotating the hue while keeping lightness and chroma in controlled ranges. The result is a harmonious palette that "feels" balanced across all hues.

Browser support: OKLch is supported in all evergreen browsers since 2023. The build pipeline generates HSL fallbacks for email clients and legacy contexts.

Brand Palette

The palette is a hue-rotation gradient anchored at four points:

Name OKLch Approx Hex Role
Verdigris (teal) oklch(0.75 0.1286 191.57) #0fc8c3 Primary brand, CTAs, links
Midnight Purple oklch(0.29 0.1506 289.33) ~#1a0a4a Deep accent, dark sections
Pastel Red oklch(0.7 0.1909 24.11) ~#e85d3a Warm accent, sidebar active
Cyber Yellow oklch(0.87 0.1786 92.23) ~#d4c520 Highlight, attention

Between each anchor, three interpolation steps create a smooth 16-color chart palette. See tokens/color/base.json for all values.

Gradient Logic

Teal (191°) → step1 (216°) → step2 (240°) → step3 (265°)
  → Purple (289°) → step1 (313°) → step2 (337°) → step3 (0°)
  → Red (24°) → step1 (41°) → step2 (58°) → step3 (75°)
  → Yellow (92°) → step1 (113°) → step2 (137°) → step3 (164°)
  → (back to Teal)

This creates a full-spectrum palette from a single generative rule — add or remove steps by interpolating between the anchors.

Neutral Scale

The neutral scale is zinc-tinted (hue ~286°) rather than pure gray. This gives surfaces a subtle warmth that complements the teal brand color. Extracted from Patina's production CSS.

Token OKLch Role
neutral.50 oklch(0.985 0 0) Near-white
neutral.100 oklch(0.967 0.001 286.375) Secondary/muted bg
neutral.200 oklch(0.92 0.004 286.32) Borders, inputs
neutral.400 oklch(0.705 0.015 286.067) Ring, muted-fg (dark)
neutral.500 oklch(0.552 0.016 285.938) Muted-fg (light), ring (dark)
neutral.800 oklch(0.274 0.006 286.033) Dark mode secondary
neutral.900 oklch(0.21 0.006 285.885) Light primary, dark card
neutral.950 oklch(0.141 0.005 285.823) Light foreground, dark bg

Dark Mode Strategy

Both codebases use the same mechanism: CSS custom properties toggled via a .dark class on the HTML element. The semantic tokens (background, foreground, primary, etc.) swap values between light and dark.

Key dark mode choices:

  • Background swaps from white to neutral.950 (near-black)
  • Primary inverts from near-black to light gray — ensuring contrast in both modes
  • Borders become semi-transparent white (oklch(1 0 0 / 10%)) for a subtle glass effect
  • Sidebar primary stays pastel-red in both modes — brand consistency

See tokens/color/semantic-dark.json for all overrides.

WCAG Contrast

The www site darkened the brand teal from hsl(178, 86%, 42%) to hsl(178, 86%, 28%) for WCAG AA text contrast on white backgrounds (~4.9:1 ratio). This is documented as legacy.www-primary-light in the token set.

Recommendation: Use the original bright teal (brand.verdigris) for decorative/non-text use (backgrounds, illustrations, data viz). Use neutral.900 (Patina's approach) or the darkened teal (www's approach) for text/interactive elements where contrast matters.

Multi-Format Output

The build pipeline generates:

Format File Consumer
OKLch CSS vars css/oklch.css Patina, modern browsers
HSL CSS vars css/hsl.css www (until OKLch migration)
Hex JSON hex/colors.json Email templates, Figma, print
Tailwind preset tailwind/preset.js Both codebases via config

Known Discrepancies (www vs Patina)

Token www Patina Resolution
Color space HSL OKLch www should migrate to OKLch
Primary (light) Darkened teal hsl(178,86%,28%) Near-black oklch(0.21...) Different strategies — both valid
Ring/focus Green-shifted hsl(153,67%,38%) Neutral oklch(0.705...) www should adopt Patina's neutral ring
Brand teal hsl(178,86%,42%) oklch(0.75,0.1286,191.57) Equivalent values — confirmed