You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/arts/color
dev 7389fb6386
refactor(arts): audit fixes — connection barrel + close dedup, http inline diag, color doc
4 months ago
..
README.md docs(color): sync arts/color README + COLOR_ENGINE_RFC with implemented API 4 months ago
apca.ts feat(color): uix.color isomorphic color-math engine (RFC Phase 0) 4 months ago
color.test.ts feat(color): temper() — perceptual-temperature match for intents (keeps hue) 4 months ago
convert.ts feat(color): APCA-driven on-solid contrast pick in render-css (RFC Phase 1) 4 months ago
generate.ts feat(color): uix.color isomorphic color-math engine (RFC Phase 0) 4 months ago
index.ts refactor(arts): audit fixes — connection barrel + close dedup, http inline diag, color doc 4 months ago
scheme.ts feat(color): temper() — perceptual-temperature match for intents (keeps hue) 4 months ago
types.ts feat(color): uix.color isomorphic color-math engine (RFC Phase 0) 4 months ago

README.md

color (uix.color)

Isomorphic color math engine. Pure, deterministic, DOM-free — the same functions run at build (eidos render-css emits static CSS) and at runtime (ActiveEidos.applyColorScheme for live / white-label theming). Because every decision is computed in JS before a value is written, introspection (APCA on-solid pick) and alpha fidelity are preserved in every mode.

Status — consumed (COLOR_ENGINE_RFC.md Phases 0/1/2/4). eidos's render-css consumes it at build (APCA on-solid pick + OKLCH-native palette output); ActiveEidos.applyColorScheme consumes it at runtime (the live theme builder). Wide-gamut output (oklch() + hex fallback) is default-on.

Why an art (and not an eidos lib)

The color math has two consumers — eidos's build-time generator (render-css) and its runtime (ActiveEidos). Living in arts/ (below the UIX layers), like uix.motion, lets both consume it with no cross-layer dependency. Per the arts convention it imports no other art and touches no DOM.

Pipeline

hex ⇄ gamma sRGB ⇄ linear sRGB ⇄ OKLab ⇄ OKLCH        (convert.ts)
                                              │
APCA Lc / WCAG2 ratio                         │        (apca.ts)
seed + template → 12 OKLCH steps (morph)      │        (generate.ts)
APCA on-solid pick · compositing-inverse alpha┘

OKLCH is the source / authoring space; wide gamut is preserved there. The sRGB hex is a gamut-mapped fallback (chroma reduction, never channel clip — clipping shifts the hue). The oklch(...) string keeps the full gamut for the browser.

API

Function Purpose
parseColor(hex | 'oklch(...)' | [l,c,h]) → Oklch
oklchToHex / oklchToCss / oklchToGammaRgb OKLCH → sRGB fallback / oklch() string / gamma sRGB
isInSrgbGamut does an OKLCH fit sRGB without chroma reduction?
apcaLc(text, bg) / wcagContrastRatio(a, b) contrast (APCA Lc · WCAG2 ratio) — gamma sRGB inputs
scaleToTemplate(hex[12]) hand-authored scale → OKLCH template (curve donor)
pickNearestTemplate(seed, templates) choose the donor whose solid is closest (L-weighted)
generateScale(seed, template, { solidStep? }) seed → 12 OKLCH steps (morph; solid anchored to seed)
pickOnSolid(solid, { onSolid, onSolidContrast }, floor?) APCA pick of on-solid text + passes
alphaOverBackground(solid, background) translucent fill (rgb, alpha) that over background = solid
deriveScheme(seed, variant?) brand seed → hierarchy role seeds (Material 3 CorePalette)
temper(color, reference, amount?) match a reference's L+C keeping hue (cohere intents)
harmonize(color, toward, amount?) rotate hue toward a reference (M3 blend; brand accents)

The generator (template morph)

Radix's generateRadixColors spirit: a hand-tuned scale donates its perceptual L-curve and chroma profile; the seed re-hues it and rescales chroma so the solid step lands on the seed exactly. Beats a naive l − k linear ramp (constant chroma → muddy mids) because the donor's curve is already perceptually placed. The 31 Radix scales eidos already ships become the template library.

Theme builder (scheme derivation)

deriveScheme(seed, variant?) ports Material 3's HCT CorePalette to OKLCH: one brand seed → the hierarchy role seeds (primary / secondary / tertiary / neutral / neutralVariant).

  • secondary = same hue, low chroma · tertiary = hue + 60°, moderate chroma · neutral = same hue, near-zero chroma. (Structure is exact; chroma is recalibrated HCT→OKLCH. Tone→contrast is NOT ported — APCA decides contrast.)
  • Variants (SchemeVariant): tonal (default), vibrant, monochrome.
  • The 6 canonical intents are NOT derived (an error is always red). To cohere them with a brand WITHOUT losing meaning, temper(color, reference, amount?) matches the brand's perceptual temperature (L + C) while keeping the hue (red stays red) — the right tool for intents. harmonize (rotates hue) also exists, but for brand ACCENTS, not semantic intents (rotating an intent's hue erodes its meaning).

Composition + apply: buildScheme(seed, opts) (in eidos/lib, pure) composes deriveScheme + generateScale + APCA + alpha into the --primitive-{role}-* token map; ActiveEidos.applyColorScheme(seed, opts) writes it as a managed style block (hex fallback + oklch() wide-gamut), follows light/dark, and returns the scheme for introspection. Same math at build or runtime; only VALUES behind the frozen --color-{role}-{slot} contract, so it touches no component. opts: variant (tonal/vibrant/monochrome) + temper (intent cohesion) + per-role overrides. Full spec: ../../uix/eidos/COLOR_ENGINE_RFC.md §6.2 + THEMING.md §26.

Notes

  • APCA is a WCAG 3 draft, not legal conformance. pickOnSolid decides by APCA but onSolidWcagRatio is available as a documented cross-check / safety floor.
  • Wide-gamut output is live + default-on (RFC §7 strategy A): the palette emits a hex fallback + an oklch() sibling that wins where supported → wide-gamut on P3 with no @media. Explicit color(display-p3 …) (strategy B) stays deferred — only if an app needs hand-tuned P3 values. NOTE: an sRGB-authored palette renders identically in oklch() (no P3 data to recover); real P3 needs a wide-gamut SOURCE (a vivid seed / OKLCH-authored theme), which the generator preserves (raw OKLCH, never gamut-clamped).

Powered by TurnKey Linux.