Accessible, harmonious color palettes from one seed color — with WCAG contrast guarantees baked in.
Give it one color. Get a full palette — tonal ramps (50→950), harmonious accents, neutral surfaces, and ready-to-use light/dark roles — plus a contrast audit so you know it's actually readable before you ship it. Zero dependencies. CLI + library.
Built by Velkina.
npm i -g @velkina/hueshift
hueshift "#3b82f6"Most palette generators give you pretty swatches and leave accessibility as your problem. hueshift treats contrast as a first-class output: it tells you which pairings pass WCAG AA/AAA, picks readable text colors for you, and exits non-zero if a key pairing fails — so it works as a CI gate on your design tokens.
The color math is correct, not approximate: proper sRGB↔linear transfer, WCAG 2.x relative luminance and contrast ratio, and tonal ramps scaled in linear light (which keeps hue stable, unlike naive RGB mixing).
hueshift "#e84d3c" # report + swatches + contrast audit
hueshift "#3b82f6" --scheme triadic # complementary | analogous | triadic | splitcomp | tetradic
hueshift "#3b82f6" --mode dark # dark-mode roles
hueshift "#3b82f6" --json # full palette as JSON
hueshift "#3b82f6" --css > tokens.css # CSS custom propertiesExample output:
hueshift seed #3b82f6 · triadic · light
primary #3b82f6 ▥▥▥▥▥
accent1 #f63b82 ▥▥▥▥▥
accent2 #82f63b ▥▥▥▥▥
✓ foreground / background 8.26:1 AAA
✓ foreground / surface 7.57:1 AAA
✗ primary / background 3.46:1 AA-large
✓ primaryText / primary 5.71:1 AA
import { generatePalette, contrastRatio, wcagLevels, bestTextOn, tonalRamp } from "@velkina/hueshift";
const p = generatePalette("#3b82f6", { scheme: "triadic", mode: "light" });
p.accents; // [{ name, hex, ramp: {50..950} }, ...]
p.roles; // { background, foreground, surface, primary, primaryText }
p.audit; // [{ label, ratio, AA_normal, AAA_normal, ... }]
contrastRatio({r:0,g:0,b:0}, {r:255,g:255,b:255}); // 21
bestTextOn(parseColor("#3b82f6")); // { color: "#ffffff", ratio }| fn | what |
|---|---|
generatePalette(seed, {scheme, mode}) |
full palette + roles + contrast audit |
tonalRamp(color) |
50→950 ramp (linear-light scaled) |
contrastRatio(a, b) |
WCAG ratio 1..21 |
wcagLevels(ratio) |
{AA_normal, AAA_normal, AA_large, AAA_large} |
bestTextOn(bg) |
black or white, whichever is more readable |
parseColor / toHex |
#rgb/#rrggbb/rgb() ↔ {r,g,b} |
WCAG 2.2 1.4.3 Contrast (Minimum): normal text needs 4.5:1, large text 3:1 (AA); AAA raises these to 7:1 and 4.5:1. hueshift reports all four.
npm test — 12 tests covering the color math (incl. the black/white = 21:1 invariant), ramp monotonicity, and that generated palettes actually pass their contrast guarantees.
MIT © Velkina (Ömer Can Nalbant, Baha Taşkın)