Communitygithub.com

Adamparishspok/skills

Build or migrate a UI's neutral palette (grounds, surfaces, lines, ink/text) as an OKLCH formula driven by a few inputs instead of dozens of hand-picked hex values, with light and dark themes, per-step ladder overrides, a fit script that reproduces existing colours, and a browser contrast audit. Use when creating a theme, adding dark mode, retinting a product, auditing text contrast, or consolidating several apps' palettes. Triggers on: palette, neutrals, grey ramp, dark mode, theme tokens, oklch, retint, hue, contrast audit, design tokens, Tailwind @theme colours.

What is skills?

skills is a Claude Code agent skill that build or migrate a UI's neutral palette (grounds, surfaces, lines, ink/text) as an OKLCH formula driven by a few inputs instead of dozens of hand-picked hex values, with light and dark themes, per-step ladder overrides, a fit script that reproduces existing colours, and a browser contrast audit. Use when creating a theme, adding dark mode, retinting a product, auditing text contrast, or consolidating several apps' palettes. Triggers on: palette, neutrals, grey ramp, dark mode, theme tokens, oklch, retint, hue, contrast audit, design tokens, Tailwind @theme colours.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Adamparishspok/skills/tree/HEAD/skills/oklch-palette-formula

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation


name: oklch-palette-formula description: Build or migrate a UI's neutral palette (grounds, surfaces, lines, ink/text) as an OKLCH formula driven by a few inputs instead of dozens of hand-picked hex values, with light and dark themes, per-step ladder overrides, a fit script that reproduces existing colours, and a browser contrast audit. Use when creating a theme, adding dark mode, retinting a product, auditing text contrast, or consolidating several apps' palettes. Triggers on: palette, neutrals, grey ramp, dark mode, theme tokens, oklch, retint, hue, contrast audit, design tokens, Tailwind @theme colours. license: MIT

OKLCH palette formula

A product's neutrals (page ground, card surface, wells, hairlines, four steps of text) are usually 15 to 30 hex values per theme, picked by eye. They drift, dark mode is a second hand-picked set, and nobody knows which pairs still pass contrast. Replace them with a formula:

surface-ish token:  L = paper + depth  × n(token)     chroma = tint      hue = hue
ink token:          L = ink   + step   × n(token)     chroma = ink-tint  hue = hue

n(token) is the token's position on a ladder, one ladder per theme. Move an input and the whole ramp moves together, so the gaps between steps, which is where contrast lives, survive a retint. Retinting a product is one line (--hue).

Three knobs (--hue, --lightness, --chroma or --contrast) drive every colour.

Files

  • references/palette.css: drop-in rules with starter inputs and ladders.
  • scripts/fit_palette.py: fit inputs to an existing hex palette and print ΔE per token.
  • scripts/ladders.json: the default ladders (light, dark).
  • scripts/palette-audit.mjs: resolve every token in a real browser in both themes and fail on contrast.
  • scripts/oklch.py: conversions and ΔE / WCAG helpers.

Procedure

  1. Name the tokens by role, not value. The defaults:

    • Grounds: ground (page), panel (rails, bars), surface (cards, menus), surface-2 (wells), surface-3 (chips, hovered rows), field.
    • Lines: line, line-2, line-3 (hairlines, lightest to darkest), plus field-line, a control's edge, which must clear 3:1.
    • Ink: ink, ink-2, ink-3, ink-4 (text, darkest to quietest). Every ink step is text, so each must clear 4.5:1.
  2. Fit, don't re-pick. For an existing product run python3 scripts/fit_palette.py theme.css. It reads the --color-* hex declarations (light, then the [data-theme="dark"] rule), fits the inputs against the default ladder, and prints ΔE per token. Under 2 is visually identical.

  3. Override ladder steps where the design spaces them differently, never the colour. Evaluate candidates with fit_palette.py theme.css light '<inputs>' '<overrides>' until the worst ΔE is under ~2. Typical overrides: field: 0 (fields are the card face), a wider line-3, a bigger gap between ink and ink-2.

  4. Write the rules (see references/palette.css):

    • Put the inputs and the --color-* formulas on the element that carries the theme attribute.
    • Restate every input you change in the dark rule. A dark rule written for the kit's defaults otherwise wins with its values.
    • With Tailwind v4, keep the token names in @theme so the utilities exist; the runtime declarations override them.
  5. Audit in a real browser: node scripts/palette-audit.mjs <url>. It paints each token, reads back sRGB, and checks:

    • every ink step at 4.5:1 against every ground;
    • field-line and ring at 3:1.

    Fix a FAIL by moving that token's ladder step, not by special-casing a colour. Expect FAILs on existing palettes: in a 17-app migration the audit found failing ink-4 or field-line values in more than half of them, all of them previously shipped.

Traps

  • var() inside a custom property resolves where it is declared.
    • Declare the formulas on :root and set inputs on a child: the child never recolours.
    • Alias tokens (--color-heading: var(--color-ink)) must be declared on the same themed element too.
  • Tailwind v4 @theme values live on :root. A theme scoped to an app root ([data-app]) must declare the formula there, not in @theme.
  • Anything that hands colours to a canvas or map library (getComputedStyle(...).getPropertyValue('--color-x')) now receives an unresolved oklch(calc(...)) string. Resolve it first: paint a probe element and read a 1px canvas, the same way the audit does.
  • One hue for grounds and inks. If the design's inks carry a different hue from its grounds, keep --ink-tint low; at L 0.98 any chroma reads as a colour cast.
  • Brand, status and chart colours stay outside the formula. Derive text-on-tint variants (for example brand-text) from the same hue at a fixed lightness instead of picking them separately.
  • Mixed-hue, light-only palettes sampled off comps may not fit within ~2 ΔE. Skip them rather than distort the design. The audit still applies.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Adamparishspok/skills

Make a website, web app or online store ready for AI agents. Scan it with Cloudflare's Agent Readiness checks, decide which checks apply to this kind of site, ship the discovery files and headers in priority order, and add the AI-first basics the scanner does not score (server-rendered content, JSON-LD, llms.txt). Covers robots.txt AI rules, Content Signals, sitemap, Markdown for Agents (Accept text/markdown), Link headers, API catalog, OAuth discovery, auth.md, MCP server card, A2A agent card, agent skills index, WebMCP, and commerce protocols (ACP, UCP, AP2, MPP, x402). Use when making a site AI-ready, agent-ready or AI-first, or fixing an isitagentready.com or Cloudflare Agent Readiness report. Triggers on: AI ready, agent ready, AI-first, agent readiness, isitagentready, AI Crawl Control, Markdown for Agents, text/markdown, llms.txt, GPTBot, ClaudeBot, AI crawlers, Content-Signal, ai-train, api-catalog, .well-known, oauth-protected-resource, auth.md, MCP server card, agent card, agent skills, WebMCP, a...

Adamparishspok/skills

Install a complete, lean favicon set on any website or web app — the five files modern browsers, iOS and Android actually use, one block of head markup added once in the root layout, and a check that every file resolves. Covers finding the public folder per framework, generating the set from one SVG (or from a RealFaviconGenerator package), dark-mode SVG icons, maskable manifest icons, cache busting and verification. Use when adding, replacing or auditing a favicon, app icon, apple-touch-icon or web manifest. Triggers on: favicon, favicon.ico, favicon.svg, apple-touch-icon, site.webmanifest, manifest.json, app icon, PWA icon, maskable icon, tab icon, RealFaviconGenerator.

Adamparishspok/skills

Give an interface one shared tempo and a small vocabulary of hover and state micro-interactions: duration tiers (tap/pop/hover/card/layout) for CSS and Motion, swap hand-offs, rows whose highlight grows in, trailing arrows, chevron nudges, card lifts without translate, scroll-edge fades, and icon/label swaps for copy and toggle buttons. Use when animations feel inconsistent, durations are scattered magic numbers, hovers only change colour, or a product feels flat or unfinished. Triggers on: animation duration, transition timing, hover state, micro-interaction, polish, copy button feedback, menu icon to close, back arrow, card hover, scroll fade, motion tokens, Framer Motion durations.

Adamparishspok/skills

Consolidate several apps or prototypes onto one shared component library without flattening their looks. Covers finding duplicated local components (buttons, selects, fields, modals, pagers, toasts), making shared components token-sized so each suite or app sets its own measurements, mapping variant names, deciding what stays app-specific, and the tailwind-merge traps that silently break custom tokens. Use when several apps each grew their own Btn/Select/Modal, when a design system's components "don't fit" a second product, or when retheming a family of apps. Triggers on: shared component library, design system consolidation, duplicate components, retheme, suite of apps, shadcn retokened, token-driven components, cn tailwind-merge custom tokens, variant mapping.

Related Skills