chroma-harmonization
Skill3 starsChanged 8 days agoMITView on GitHub
Use when constructing or auditing a multi-hue palette where all hues should appear equally saturated at each contrast stop. Names the per-stop chroma cap rule (take the minimum achievable chroma across hues at the stop), the gamut bottleneck pattern (blue at mid-L is the classic limiter in sRGB; Display P3 — UDTS's default compose gamut — reshapes it), and the relationship to UDTS's default `harmony` palette variant. Generalizes Evil Martians' Harmony algorithm.
In this repository66 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- clud-bug-collaborationSkill
- critical-issues-onlySkill
- evidence-based-reviewSkill
- respect-existing-conventionsSkill
- skill-frontmatter-qualitySkill
- apca-contrastSkill
- api-contract-enforcementSkill
- brand-voice-reviewSkill
- clud-bug-collaborationSkill
- component-sizing-principlesSkill
- component-sizingSkill
- composing-a-screenSkill
- consuming-a-design-systemSkill
- conventions-templateSkill
- critical-issues-onlySkill
- curating-a-skill-catalogSkill
- designing-a-design-systemSkill
- designing-elite-uiSkill
- design-system-consistencySkill
- design-token-namingSkill
- dtcg-formatSkill
- empirical-design-principlesSkill
- evidence-based-reviewSkill
- finding-a-catalog-skillSkill
- frontend-a11ySkill
- guarding-a-regressionSkill
- ios-safari-surfacesSkill
- line-height-gridSkill
- logmindSkill
- motion-continuitySkill
- oklch-color-spaceSkill
- orchestrating-agent-delegationSkill
- orchestrating-a-multi-agent-runSkill
- orchestrating-elite-agent-qaSkill
- palette-relationshipsSkill
- pii-and-complianceSkill
- proving-an-absenceSkill
- respect-existing-conventionsSkill
- retiring-a-superseded-decisionSkill
- reviewing-design-workSkill
- semver-design-tokensSkill
- session-heartbeatSkill
- skillforgeSkill
- skill-frontmatter-qualitySkill
- spacing-systemSkill
- technical-writingSkill
- test-disciplineSkill
- thrill-designSkill
- token-frugal-toolingSkill
- token-naming-conventionsSkill
- type-scaleSkill
- udts-component-sizing-laddersSkill
- udts-dtcg-extensionsSkill
- udts-linter-rulesSkill
- udts-naming-conventionSkill
- udts-reviewSkill
- udts-semver-defaultsSkill
- udts-spacing-defaultsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 70
After harmonizing a stop:
- Very specific: line 72
Gamut check: every emitted oklch(L, c_ceiling, h) is displayable() in the target color space.
- Distinctive: line 76
If any hue clips at the ceiling or visually "pops," recompute with a tighter chroma cap.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Chroma harmonization
Multi-hue palettes look unbalanced when one hue is more saturated than the rest at the same brightness stop — a neon outlier in a calm set, a washed-out blue next to a vibrant red. Chroma harmonization is the fix: at each stop, cap every hue at the lowest-achievable chroma across the set so the palette reads as a coherent family.
When to use
- Constructing a multi-hue palette (3+ hues at the same set of contrast stops).
- Auditing an existing palette where one hue looks "off" against the others.
- Generating UDTS's default
harmonypalette variant. - Code review: flag per-hue chroma values picked independently — propose the cross-hue cap.
When NOT to use
- Single-hue palettes (no other hues to harmonize against — chroma is unconstrained by the procedure).
- Brand-spot or accent palettes where one hue should dominate. Use the
maxpalette variant instead — see oklch-color-space. - Palettes constrained to legacy sRGB-only output, where the narrower gamut shifts the bottleneck calculus back toward the classic blue-at-~220° limiter (the procedure still applies — Display P3 is the default target space; substitute the sRGB boundary deliberately for legacy-only catalogs).
The algorithm
For each contrast stop in the palette (each L value):
- For each hue in the set, find the maximum in-gamut chroma at that (L, H) in the target color space (Display P3 by default — UDTS composes end-to-end in P3 via apcach's
colorSpace: 'p3'; sRGB only when a catalog is deliberately constrained to legacy-only output). - Take the minimum of those per-hue maxima. This is the stop's chroma ceiling.
- Recompose, don't just clamp: at the shared ceiling, re-solve L per hue (apcach's constant-chroma branch) so each hue still hits the stop's target Lc against its background. Clamping chroma while leaving L untouched lets achieved Lc drift off target by up to ~5 across hues — the invariant is chroma constant AND Lc constant; hue and lightness are what's allowed to drift.
- Emit every hue at the stop using the recomposed (L, c_ceiling, h).
for each stop L_target:
c_max_per_hue = { h: max_chroma_in_gamut(L_target, h) for h in hues }
c_ceiling = min(c_max_per_hue.values())
for each h in hues:
l_solved = apcach_constant_chroma_solve(target_lc, h, c_ceiling, bg)
emit oklch(l_solved, c_ceiling, h)Known counterexample, not a template: UDTS's own harmonizeChroma does not recompose — it clamps chroma and leaves the original L untouched ({ l: original.l, c: min(original.c, ceiling), h: original.h }). That's exactly the Lc drift this rule warns about. If you're reading the shipped code as a reference, its chroma-clamp-without-recompose is the bug to fix, not the pattern to copy.
The bottleneck pattern: at moderate lightness (L ≈ 0.5), blue (~220°) is the classic limiter in sRGB — its gamut is narrowest there. In Display P3 (the default compose gamut) the boundary is wider and reshaped, so blue is not guaranteed to be the tightest hue — treat the bottleneck as empirical per palette rather than assuming ~220° holds. At the extremes (L near 0 or 1) the gamut shrinks for all hues in either space, so the ceiling collapses naturally.
Recompose can't fix the bottleneck hue's own residual. Recomposing corrects the other hues' Lc drift once they're capped to the bottleneck hue's ceiling — it can't correct the bottleneck hue itself, whose compose/measure residual (see apca-contrast's meter-vs-composer gamut mismatch) is a property of that one hue's own gamut edge. Only a matched meter — scoring on the same gamut the composer targeted — closes that residual; recompose alone can't.
Why the minimum, not the average
Average would push half the hues out of gamut at the bottleneck. Maximum would push the bottleneck hue out of gamut. Minimum is the only value where every hue can hit the target chroma in-gamut — which is the constraint that has to hold for emission.
Headroom rule
Stay ≥ 10% below the computed ceiling. This headroom is for the emission edge, not the harmonization math itself: rounding when materializing the sRGB hex fallback (the compatibility export for legacy consumers — see oklch-color-space) and display-driver quirks can push a borderline-in-gamut P3 value out of gamut once flattened to 8-bit sRGB. A headroom margin prevents those fallback-materialization failures; it does not change the P3-native chroma math above.
Relationship to UDTS palette variants
harmony(UDTS default): this algorithm exactly. Every hue capped to the cross-hue minimum.max: each hue uncapped at its own gamut ceiling. Useful for brand or accent palettes where consistency yields to vibrancy.muted: harmony or max with an additional user-supplied flat chroma cap (e.g. C ≤ 0.06) — produces tonal greys like sand, stone, slate.flat: a single chroma across all stops for one hue (no cross-hue constraint).grey: zero chroma, always generated.
A UDTS catalog can ship multiple variants per hue and bind theme roles to specific (hue, variant) pairs — see the UDTS theme docs.
Verification
After harmonizing a stop:
- Gamut check: every emitted
oklch(L, c_ceiling, h)isdisplayable()in the target color space. - Visual check: at the same stop, all hues should report identical (or near-identical) saturation in a side-by-side swatch. Use oklch.com or a culori-based tool.
- Headroom check: the actual ceiling sits ≥ 10% below the computed Display P3-gamut maximum at every (L, H) in the set (verify the sRGB-fallback headroom separately, at emission).
If any hue clips at the ceiling or visually "pops," recompute with a tighter chroma cap.
Cross-references
- Routed here by: designing-a-design-system — the L1 dispatcher for building or extending a system.
- REQUIRED BACKGROUND: oklch-color-space — the OKLCH primitive, the gamut-mapping rule.
- For the contrast targets at each stop: apca-contrast — the Lc targets that fix each L value.
- For starting-hue palette construction before harmonization: palette-relationships.
Sources
- Evil Martians' Harmony — the original algorithm + interactive demo.
- Evil Martians' Harmonizer — Figma plugin implementation.
- UDTS's
harmonypalette variant — the canonical productized form of this algorithm.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

