color
Skill1 starsChanged 3 months agoMITView on GitHub
Build a full palette from one brand hue in OKLCH — tinted neutrals, surface hierarchy, status colors, semantic scales, dark mode. Use when picking colors, building color tokens, fixing a palette that feels assembled from defaults, or converting a palette to dark mode.
8 other projects use a version of this.
Adopted by
Most recently taken up by
- sanmidable/skillstheir own version, first seen Nov 8, 2026
- soilmass/motion-design-agenttheir own version, first seen Nov 8, 2026
- raphaelsalaja/skillstheir own version, first seen Nov 1, 2026
- instrument-org/skillstheir own version, first seen Oct 22, 2026
- IamK77/Skilltheir own version, first seen Oct 12, 2026
- alejo-valencia/copilot-session-colortheir own version, first seen Oct 11, 2026
Other versions8 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 8
- raphaelsalaja/skills★ 62same name
- allierays/agentic-loop★ 38same name
- soilmass/motion-design-agent★ 7same name
- IamK77/Skill★ 2same name
- jangles-byte/atelier★ 2same name
- sanmidable/skills★ 2same name
- alejo-valencia/copilot-session-color★ 1same name
- instrument-org/skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository14 other files it gives its agents.
- accessibilitySkill
- content-designSkill
- design-reviewSkill
- design-systemSkill
- implementation-reviewSkill
- interaction-designSkill
- layoutSkill
- measurementSkill
- motionSkill
- product-evidenceSkill
- product-framingSkill
- typographySkill
- validationSkill
- visual-directionSkill
The file
We split this skill into the rules it gives an agent
7 rules. 0 are common, 6 are distinctive, and 1 is very specific to this project.
0
Common
Most of the 8 edited versions we read keep these.
6
Distinctive
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 27
Use OKLCH (now supported in all major browsers) for perceptual uniformity:
- Distinctive: line 39
When building semantic color scales (danger-100 through danger-900), use OKLCH with fixed L steps.
- Distinctive: line 46
Test color combinations together, not in isolation.
- Distinctive: line 69
Every neutral shares H=250 (the brand hue) with very low chroma — this tints them toward blue so the palette reads as a family, not parts …
- Distinctive: line 191
Verify 500 against your brand: it should read as "the" color.
- Distinctive: line 192
Verify 100 as a tinted background and 900 as near-black text — both should still read as belonging to the hue family.
- Very specific: line 202
If light mode --surface-base is oklch(97% ...), a naive inversion gives oklch(3% ...) — pure black.
“Common” is measured: kept by at least 6 in 10 of the 8 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Color
Color systems are not palettes. They are structured relationships: construct the palette from one hue, then verify it against perceptual properties.
An existing product's palette is the origin — derive missing pieces from its brand hue rather than starting a new palette. Full construction is for new products, or when you are explicitly reopening visual direction.
Palette construction from a single hue
The best palettes are derived, not assembled. Pick one hue. The entire palette grows from it.
- Brand hue — the origin. Define it in OKLCH:
oklch(55% 0.18 250)— a medium-saturated blue. - Neutrals are tinted — do not use pure gray (C=0). Tint the neutrals toward the brand hue at low chroma (C 0.01–0.02). This makes the palette feel cohesive rather than pasted-together.
- Surface hierarchy from the same hue — surfaces share the brand's H value with very low C. Surfaces that share origin hue feel like they belong to the same material.
- Status colors are neighbors, not defaults — derive them by hue-rotating from the brand. Never import a design system's status palette wholesale.
- Accent — one color. The brand hue at high chroma and appropriate lightness for the surface. One accent. No secondary accent. The moment you add a second accent, you have competing CTAs.
- Contrast, checked now — verify every text-on-surface and status-on-surface pair against WCAG AA while choosing values, not after the palette ships. Fixing contrast later means redesigning the palette.
Perceptual uniformity — why HSL is not enough
HSL is not perceptually uniform. hsl(60, 80%, 50%) (yellow) is visually much brighter than hsl(240, 80%, 50%) (blue) at the same lightness value. This matters when constructing multi-color status systems or data viz palettes.
Use OKLCH (now supported in all major browsers) for perceptual uniformity:
/* hsl yellow and blue at same lightness — VERY different visual weight */
color: hsl(60, 80%, 50%); /* appears bright, almost white */
color: hsl(240, 80%, 50%); /* appears much darker */
/* oklch — L is perceptually even */
color: oklch(65% 0.18 85); /* yellow at true L=65 */
color: oklch(65% 0.18 265); /* blue at same visual L */When building semantic color scales (danger-100 through danger-900), use OKLCH with fixed L steps. The result is a scale where every step has genuinely equal perceived contrast.
Simultaneous contrast — what colors do to each other
A neutral gray placed on a warm orange background will appear slightly blue. The same gray on a cool blue background will appear slightly orange. This is simultaneous contrast — adjacent hues shift each other's apparent temperature.
Practical rules:
- Test color combinations together, not in isolation. A token that passes contrast in a white mockup may fail perceptually on a tinted surface.
- Warm accent on a cool neutral background has maximum pop. Warm on warm competes.
- Muted colors on white look desaturated. The same muted color on a slightly dark surface looks rich. Design for the actual background.
- Status green on a dark blue background needs higher chroma than status green on a white background to read as clearly "positive."
Hue, lightness, chroma — three levers, not one
Most LLM-generated palettes only move one lever (lightness) when building a scale. A designed scale moves all three:
- Lightness: primary scale lever — light to dark
- Chroma (saturation): high at mid-lightness, lower at extremes. The purest version of any hue is never at its lightest or darkest. It's at the midpoint. (
oklch(55% 0.22 H)is more vivid thanoklch(90% 0.22 H)) - Hue: slight shift along the scale is acceptable and often desirable — a scale that "twists" slightly toward a neighboring hue at extremes is richer than a pure-hue scale.
Worked example — full palette from one hue
Brand origin: oklch(55% 0.18 250) — a medium-saturated blue.
Why this origin? L=55% sits at the perceptual midpoint where chroma peaks. C=0.18 is vivid but not neon. H=250 is a true blue that avoids the purple lean of H=265+ and the cyan lean of H=230−.
Neutrals
Every neutral shares H=250 (the brand hue) with very low chroma — this tints them toward blue so the palette reads as a family, not parts from different kits.
| Token | OKLCH | Reasoning |
--neutral-950 | oklch(12% 0.015 250) | Near-black. L=12% is dark enough for text on light. C=0.015 gives a subtle blue tint without looking colored. |
--neutral-800 | oklch(25% 0.015 250) | Dark. Secondary text, heavy borders. |
--neutral-600 | oklch(45% 0.012 250) | Mid. Placeholder text, disabled states. C drops slightly — high chroma at mid-L would look like a blue, not a gray. |
--neutral-400 | oklch(65% 0.010 250) | Light. Dividers, subtle borders. |
--neutral-200 | oklch(85% 0.008 250) | Near-white. Hover backgrounds, secondary surfaces. C continues to drop — at high L even small chroma is perceptible. |
--neutral-50 | oklch(97% 0.005 250) | Page background. Nearly white, barely blue. |
Key pattern: as L increases, C decreases. High-L + high-C reads as a pastel blue, not a tinted white.
Surface hierarchy
Surfaces use the same H=250 with minimal chroma. The L values create depth through lightness contrast.
| Token | OKLCH | Role |
--surface-base | oklch(97% 0.005 250) | Default page background. |
--surface-elevated | oklch(99% 0.003 250) | Cards, popovers — floats above base. |
--surface-depressed | oklch(93% 0.008 250) | Input fields, wells — sits below base. Slightly higher C to feel recessed. |
--surface-overlay | oklch(99% 0.003 250 / 0.9) | Modal/dialog backdrop with transparency. |
Status colors — hue-rotated neighbors
Starting from brand H=250, rotate to semantically meaningful hues. Keep L≈55% so all status colors have equal visual weight. Adjust C per hue — some hues (green, yellow) have wider gamut at their peak and can carry more chroma.
| Token | OKLCH | Hue math |
--status-success | oklch(55% 0.17 155) | H=250 → −95° = 155. Green. C=0.17 (green peaks wide, can stay high). |
--status-warning | oklch(55% 0.16 85) | H=250 → −165° = 85. Warm amber. C=0.16 (yellow-range hues are perceptually loud, slightly lower C). |
--status-error | oklch(55% 0.20 25) | H=250 → −225° = 25. Red-orange. C=0.20 (red is the alarm signal, maximum chroma). |
--status-info | oklch(55% 0.14 220) | H=250 → −30° = 220. Slightly cooler blue. C=0.14 (lower than brand — info should not compete with actions). |
Why not H=0 for error? Pure red in OKLCH (H=25–30) reads more urgently than H=0 which leans pink. The +5° shift toward orange increases alarm perception.
Brand accent
| Token | OKLCH | Role |
--accent-action | oklch(55% 0.22 250) | Primary CTA. Same L and H as brand, but C pushed to 0.22 — near the sRGB gamut edge for this hue; verify it renders in-gamut. |
--accent-hover | oklch(48% 0.22 250) | Hover state. Darken by dropping L, keep C. Never brighten on hover — darkening signals "pressing in." |
--accent-subtle | oklch(90% 0.06 250) | Tinted background for selected/active states. High L, low C — a whisper of the brand. |
Copy-ready CSS custom properties
:root {
/* --- Neutrals --- */
--neutral-950: oklch(12% 0.015 250);
--neutral-800: oklch(25% 0.015 250);
--neutral-600: oklch(45% 0.012 250);
--neutral-400: oklch(65% 0.010 250);
--neutral-200: oklch(85% 0.008 250);
--neutral-50: oklch(97% 0.005 250);
/* --- Surfaces --- */
--surface-base: oklch(97% 0.005 250);
--surface-elevated: oklch(99% 0.003 250);
--surface-depressed: oklch(93% 0.008 250);
--surface-overlay: oklch(99% 0.003 250 / 0.9);
/* --- Status --- */
--status-success: oklch(55% 0.17 155);
--status-warning: oklch(55% 0.16 85);
--status-error: oklch(55% 0.20 25);
--status-info: oklch(55% 0.14 220);
/* --- Brand accent --- */
--accent-action: oklch(55% 0.22 250);
--accent-hover: oklch(48% 0.22 250);
--accent-subtle: oklch(90% 0.06 250);
}Semantic color scale generation (100–900)
A 9-step semantic scale encodes severity/weight. The formula uses OKLCH with fixed L steps, a C curve that peaks at 500, and optional H twist.
The formula
For step N (100, 200, ... 900):
L = 95% − (N/100 − 1) × 9.375% → L₁₀₀=95%, L₅₀₀=57.5%, L₉₀₀=20%
C = C_max × bell(N) → peaks at 500, tapers at extremes
H = H_base ± (N − 500)/100 × twist → optional ±5° drift across the scaleWhy bell-curve chroma? At high L (100–200), high C produces pastels that look weak. At low L (800–900), high C looks neon-in-darkness. The strongest expression of any hue is at perceptual midpoint (L≈55%), so C should peak there.
Why H twist? A slight hue shift across a scale adds richness. For danger/red: light steps can lean slightly orange (warmer, less severe), dark steps can lean slightly crimson (cooler, more intense). Keep the twist under ±5° or the scale loses coherence.
Full example: danger/red scale (H=25)
:root {
/* Danger scale — base hue H=25 (red-orange), twist ±3° */
--danger-100: oklch(95.0% 0.04 28); /* faint blush background */
--danger-200: oklch(85.6% 0.08 27); /* subtle highlight */
--danger-300: oklch(76.2% 0.13 26); /* light badge background */
--danger-400: oklch(66.9% 0.17 25); /* approaching full strength */
--danger-500: oklch(57.5% 0.20 25); /* THE danger color — peak C */
--danger-600: oklch(48.1% 0.18 25); /* text on light surfaces */
--danger-700: oklch(38.8% 0.14 24); /* heavy emphasis */
--danger-800: oklch(29.4% 0.09 23); /* dark badge, dark text */
--danger-900: oklch(20.0% 0.05 22); /* near-black danger accent */
}Reading the scale:
- L drops linearly from 95% to 20% — each step is ≈9.4% darker.
- C: 0.04 → peaks at 0.20 (step 500) → 0.05. The exact values are hand-tuned per hue, not computed — verify each step renders in-gamut.
- H: 28 → 22, drifting −3° from warm-side to cool-side. Light danger leans orange (approachable); dark danger leans crimson (severe).
Applying the formula to any hue
To generate a scale for any semantic color:
- Pick the base hue H and peak chroma C_max (test in browser — some hues clip at high C in sRGB).
- Apply the L/C/H formula above.
- Verify 500 against your brand: it should read as "the" color.
- Verify 100 as a tinted background and 900 as near-black text — both should still read as belonging to the hue family.
Dark mode — OKLCH lightness inversion
Dark mode is not "invert lightness." It is a deliberate re-mapping of the L axis with chroma compensation.
Why mechanical inversion fails
If light mode --surface-base is oklch(97% ...), a naive inversion gives oklch(3% ...) — pure black. This is wrong for three reasons:
- Contrast asymmetry: The eye adapts differently to dark backgrounds. Dark surfaces need L=12–18%, not L=3–5%. True black (L<5%) causes halation — bright text on pure black appears to bleed/glow.
- Chroma hunger: Dark surfaces absorb color. A subtle blue tint (C=0.005) that reads clearly at L=97% becomes invisible at L=12%. Boost C by 1.5–2× in dark mode to maintain the same perceived tint.
- Weber's law: Perceived contrast is a ratio, not a difference. Light-on-dark needs wider absolute L gaps between surface levels than dark-on-light.
The transformation rules
DARK MODE TRANSFORM:
L_dark = 100% − L_light → base inversion
Then compress toward center:
If L_dark < 20% → lift to 12–18% (no true blacks)
If L_dark > 90% → cap at 90–93% (no true whites)
C_dark = C_light × 1.5 to 2.0 → compensate chroma hunger
H_dark = H_light → hue stays constantSurface tokens: light → dark
| Token | Light mode | Dark mode | Transformation notes |
--surface-base | oklch(97% 0.005 250) | oklch(14% 0.010 250) | L: 97→14 (not 3). C: doubled to stay visible. |
--surface-elevated | oklch(99% 0.003 250) | oklch(18% 0.008 250) | Elevated is lighter than base in both modes. Gap widens in dark (4% vs 2%) because Weber. |
--surface-depressed | oklch(93% 0.008 250) | oklch(10% 0.014 250) | Depressed is darker than base in both modes. C bumped more — recessed surfaces in dark mode lose tint fast. |
--surface-overlay | oklch(99% 0.003 250 / 0.9) | oklch(22% 0.010 250 / 0.95) | Higher opacity in dark mode — the dark background bleeds through less at 0.9. |
Key insight: elevated surfaces are always lighter than base — in both modes. The absolute L values invert, but the relative hierarchy is preserved.
Neutral tokens: light → dark
| Token | Light mode | Dark mode | Notes |
--neutral-950 | oklch(12% 0.015 250) | oklch(93% 0.008 250) | Was near-black text → now near-white text. C drops at high L. |
--neutral-800 | oklch(25% 0.015 250) | oklch(82% 0.010 250) | Secondary text. |
--neutral-600 | oklch(45% 0.012 250) | oklch(62% 0.012 250) | Mid gray — barely moves. This is correct: mid-tones converge. |
--neutral-400 | oklch(65% 0.010 250) | oklch(40% 0.014 250) | Subtle borders — need more C in dark to stay visible. |
--neutral-200 | oklch(85% 0.008 250) | oklch(22% 0.014 250) | Hover backgrounds. |
Status colors: light → dark
Status colors keep the same H but shift L and boost C so they read with equal urgency on dark surfaces.
| Token | Light mode | Dark mode | Notes |
--status-success | oklch(55% 0.17 155) | oklch(62% 0.22 155) | Lighter + more vivid. Green absorbs into dark surfaces — needs aggressive C boost. |
--status-warning | oklch(55% 0.16 85) | oklch(65% 0.20 85) | Yellow-range hues are already perceptually loud; moderate L lift + C boost. |
--status-error | oklch(55% 0.20 25) | oklch(62% 0.24 25) | Red reads urgently in both modes but needs C push to avoid looking muted on dark. |
--status-info | oklch(55% 0.14 220) | oklch(62% 0.18 220) | Cool blue on dark blue surface — needs L lift to separate from background. |
Pattern: Status colors shift L up by +7–10% and boost C by +0.04–0.06 in dark mode. They do NOT invert — they were already at perceptual midpoint (L≈55%), so inversion would make them light pastels, losing all urgency.
Copy-ready dark mode override
[data-theme="dark"] {
/* --- Neutrals --- */
--neutral-950: oklch(93% 0.008 250);
--neutral-800: oklch(82% 0.010 250);
--neutral-600: oklch(62% 0.012 250);
--neutral-400: oklch(40% 0.014 250);
--neutral-200: oklch(22% 0.014 250);
--neutral-50: oklch(14% 0.010 250);
/* --- Surfaces --- */
--surface-base: oklch(14% 0.010 250);
--surface-elevated: oklch(18% 0.008 250);
--surface-depressed: oklch(10% 0.014 250);
--surface-overlay: oklch(22% 0.010 250 / 0.95);
/* --- Status --- */
--status-success: oklch(62% 0.22 155);
--status-warning: oklch(65% 0.20 85);
--status-error: oklch(62% 0.24 25);
--status-info: oklch(62% 0.18 220);
/* --- Brand accent --- */
--accent-action: oklch(62% 0.22 250);
--accent-hover: oklch(68% 0.22 250);
--accent-subtle: oklch(20% 0.08 250);
}Dark mode accent note: Action L lifts from 55→62% (needs to pop on dark surface). Hover goes lighter (L=68%) — on dark surfaces, lightening signals interaction (opposite of light mode where darkening does). Subtle drops to L=20% with more C to remain a perceptible tint.
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.

