Skip to content

About

Zen-like colors, tints, shades, contrasts and scales

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zen-colors

npm Node.js License: MIT CI

Zen-like CSS color scales, tints, shades, and contrast helpers — OKLCH by default.

Turn any common CSS color into perceptual tints, shades, scales, and readable foregrounds.

import { Color } from '@kematzy/zen-colors';

new Color('#0af').tint(25).oklchString();
// → 'oklch(78.04% 0.126 242.04)'

Install

npm install @kematzy/zen-colors

Requirements: Node.js ≥ 20

Quick start

import { Color, parse } from '@kematzy/zen-colors';

const cyan = new Color('#0af');

// Single mixes (OKLCH toward white / black)
cyan.tint(25); // lighter
cyan.shade(40); // darker

// Series (include pure white / black at weight 100)
cyan.tints(10); // weights 10…100
cyan.shades(25); // 25, 50, 75, 100
cyan.all(10); // lightest → base → darkest (21 colors)

// Basic scale (skip pure white/black) — primary scale API
cyan.scale(); // weight 10 default → Color[]
cyan.scale(25); // 75,50,25 + base + 25,50,75

// Optional named presets (explicit only — never the default)
cyan.scale(10, { preset: 'tailwind' }); // fixed { 50…950 }, base at 500
cyan.scale(10, { preset: 'zen' }); // { t90…t10, base, s10…s90 }
cyan.scale(25, { preset: 'zen' }); // { t75, t50, t25, base, s25, s50, s75 }
cyan.scale(2, { preset: 'zen' }); // { t98…t2, base, s2…s98 }

// CSS custom properties (default: zen keys at weight 10, oklch values)
cyan.cssVariablesString('primary');
// --color-primary-t90: oklch(...);
// --color-primary-base: oklch(...);
// --color-primary-s10: oklch(...);
cyan.cssVariablesString('primary', { preset: 'tailwind', format: 'hex' });
// --color-primary-50: #...; … --color-primary-500: #...; …

// Or format any scale / series you already built
import { cssVariablesString } from '@kematzy/zen-colors';
cssVariablesString(cyan.all(10), 'accent'); // keys from type + weight
cssVariablesString(cyan.scale(10, { preset: 'zen' }), 'primary');

// Contrast (current-color centric)
cyan.contrast('#fff'); // { ratio, passes, darker, lighter, current }
cyan.fg(); // pure black or white on cyan (higher contrast)
cyan.fg('aa'); // soft grey meeting ≥ 4.5:1 on cyan when possible
cyan.fg('base'); // intent minimum 5:1 (not a WCAG grade name)
cyan.on(4.5); // tint/shade of cyan with ≥ 4.5:1 on white

// Formats
cyan.oklchString(); // preferred
cyan.rgbString(); // modern: rgb(0 170 255)
cyan.hslString();
cyan.hexString();
cyan.toString(); // = oklchString()

// Null-safe parse
parse('not-a-color'); // null
new Color('not-a-color'); // throws ColorError

Concepts

Idea Behavior
Mix space OKLCH (perceptual; stable hue)
OKLCH object l Percent 0–100 (not 0–1)
String default Modern oklch(L% C H)
Primary job Tints & shades from one color
scale(weight) Integer weight 2–25, default 10; skips 0 & 100
Presets Extra only — preset: 'tailwind' (fixed) or 'zen' (weight-driven keys)
Immutability Methods return new Color instances

scale() weight examples

Weight Steps in each direction Array shape (basic)
25 25, 50, 75 [t75, t50, t25, base, s25, s50, s75]
10 10…90 19 colors
2 2, 4, …, 98 99 colors

Contrast helpers

  • contrast(other) — rich report for custom checks (ratio, WCAG passes, darker / lighter).
  • fg() — pure black or white on this background (higher contrast).
  • fg(level) — soft grey (when possible) meeting that level’s minimum ratio on this background. Levels: WCAG aaa · aaa-large · aa · aa-large · ui, or intent strong (6) · base (5) · muted (4) · subtle (3). Falls back to pure B/W if out of range.
  • on(ratio, { against? }) — adjust this color’s tint/shade until it meets the ratio against a surface (default white).

Supported input

Any string culori understands in common CSS, including:

  • Hex: #RGB, #RRGGBB, #RGBA, #RRGGBBAA
  • rgb() / rgba() (comma and space syntax)
  • hsl() / hsla()
  • oklch() / oklab() / lab() / lch() / hwb()
  • Named CSS keywords
  • transparent

CSS variables

Call Result
color.cssVariablesString(name) Zen scale (weight 10) → --color-{name}-t* / base / s*
color.cssVariablesString(name, { preset: 'tailwind' }) --color-{name}-50…950
color.cssVariablesString(name, { weight, preset, format, prefix }) Same with options
cssVariablesString(colors, name, options?) Format an existing Color[] or Record

Default value format is oklch; set format: 'hex' | 'rgb' | 'hsl' as needed. Default prefix is color (--color-…); override with prefix.

API surface

Export Kind
Color class
Color#cssVariablesString(name, options?) method → CSS custom properties string
cssVariablesString(colors, name, options?) format Color[] / Record as CSS vars
Color#fg(level?) black/white on this bg; optional min-ratio level
FG_LEVEL_MIN_RATIO / resolveFgLevel level → minimum ratio map / resolver
parse(input) Color | null
ColorError error class
VERSION string
Types …, FgLevel, ContrastResult, ContrastPasses, OnOptions, …

Full signatures are in the published TypeScript declarations (dist/index.d.ts).

Development

Agent / contributor workflow lives in AGENTS.md and the project skill .grok/skills/dev-workflow (git, checks, changelog, release). Other repos can install the same pattern via the user Grok skill /setup-dev-workflow.

npm install
npm test              # vitest (node)
npm run test:ui       # vitest interactive UI
npm run test:coverage # 100% statements/lines/functions; ≥95% branches
npm run test:browser  # vitest browser smoke (Playwright / Chromium)
npm run typecheck
npm run lint
npm run spellcheck    # project dictionary via cspell (`.cspell.json`)
npm run check         # typecheck + lint + format + spellcheck + test (pre-push gate)
npm run build
npm run demo:dev      # playground (Vite)
npm run demo:build    # static demo → demo/dist

First-time browser tests need Playwright browsers: npx playwright install chromium

This library is developed test-first (TDD).

Quality gates (push / main / publish)

Local pre-push hook (installed via npm install / prepare):

  • Runs npm run check and blocks the push if anything fails
  • check matches CI’s quality steps, including spellcheck (same dictionary as Node 24 CI)
  • See .githooks/README.md
  • Bypass only when intentional: git push --no-verify

npm publish (prepublishOnly): typecheck, lint, format, spellcheck, coverage tests, and build — so dictionary/format drift fails before a release tarball is uploaded.

GitHub (required for real protection): on main, enable a ruleset / branch protection with Require status checks to pass, selecting the CI test (…) jobs from .github/workflows/ci.yml (at least test (24), which runs spellcheck + demo build). That cannot be skipped with --no-verify.

Playground demo

Static GitHub Pages app under demo/:

  • Intro first (skippable; preference in localStorage)
  • Scale — all(weight) swatch grid, OKLCH sliders, Set Base, share URL
  • Methods — live tint / shade / series / scale presets
  • Contrast — contrast, fg, on, scale matrix
  • Docs — short getting-started + API notes
  • API — browser-only JSON builder for agents
  • Drawer: CSS export (zen keys default, Tailwind optional) + history

Markup is split with vite-plugin-handlebars: demo/index.html is a short shell; sections live in demo/partials/ (header, page-*, drawer, footer, …).

npm run demo:dev
# http://localhost:5173/zen-colors/

Credits

Inspired by Noel Delgado’s values.js and parse-css-color.
Color math via culori.

Helpfully assisted by Grok 4.5 using Pi Coding Agent and Grok Build.

See CREDITS.md.

License

MIT © kematzy

About

Zen-like colors, tints, shades, contrasts and scales

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages