System · Harbor

Color

OKLCH roles, brand sparingly, contrast.

color.md

Meridian — color.md

Color system. Ranking: influences.md.
Primary absorb: Fluent neutrals + brand sparingly + semantic status · Polaris assign meaning · Linear perceptual theme knobs · Meridian OKLCH-only + WCAG AA.

1. Laws

  1. OKLCH only — tokens are oklch(...) or var(--token); never hex/rgb/hsl in components.
  2. One accent--brand drives primary, ring, selected, success.
  3. Status ≠ branddestructive / warning keep fixed meaning (Polaris, Fluent semantic).
  4. Soft neutrals — never pure white/black.
  5. Brand sparingly — CTA + selected wash; not large fills (Fluent brand).
  6. Theater lifts branddata-tone="theater" for AA on dock.
  7. Activity pastels are semantic — agent doing, not accent fashion.

2. Palettes (Fluent → Meridian)

Fluent paletteMeridian
Neutral--background, --foreground, --muted, --card, --border, --dock*
Brand--brand, --brand-foreground, --brand-muted
Shared / semantic--destructive, --warning, --success(=brand)
(Meridian fixed)--activity-*, --chart-2…5

3. Theme knobs (Linear-shaped, OKLCH-strict)

Minimum knobs an agent may re-skin:

--brand
--brand-foreground
--brand-muted
--background
--foreground
--radius

Linear reduced theme definition to base + accent + contrast (Linear redesign). Meridian keeps explicit roles but one accent variable owns brand-linked aliases.

4. Neutral hierarchy

Fluent: lighter neutrals highlight focus areas (Color).

LayerToken intent
Pagebg-background
Inset chromebg-muted/15–30
Card / raised contentbg-card
Floating / dockbg-dock + text-dock-foreground
Selectedbg-brand/10
Hoverbg-muted/30–40

Limit accent tint in chrome calculations (Linear lesson).

5. Contrast contract

Audited pairs live in lib/design/contrast.ts. Gate: npm run check:contrast.

SurfaceForeground rules
Papertext-foreground / text-muted-foreground
Theater / docktext-dock-foreground / text-dock-muted; brand lifted
Nevertext-*/70 opacity cheats on body

Body/UI ≥ 4.5:1; large/display ≥ 3:1.

6. Interaction coloring

StateColor behavior
HoverNeutral darken/lighten — not brand flood
SelectedBrand wash 10%
FocusRing = brand; stroke weight up
Destructive hoverDestructive wash

7. Charts & activity

FamilyRule
--chart-1= brand
--chart-2…5Fixed ramp — not brand-linked
--activity-*thinking/search/read/edit only

8. Anti-patterns

raw hex · arbitrary color utilities · second accent · neon · cream-terracotta cliché · pure white/black · status-as-decoration · influence brand labels

9. Implementation map

FileRole
app/globals.cssToken values
lib/design/grammar.tscolorRoles
lib/design/contrast.tsPairs
scripts/check-contrast.mjsGate

Sources

Raw