Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
jezweb avatar

Color Palette

  • 2.7k installs
  • 946 repo stars
  • Updated July 2, 2026
  • jezweb/claude-skills

color-palette is a Claude skill that generates accessible shade scales, semantic tokens, and Tailwind v4 CSS from a single brand hex color.

About

The color-palette skill builds a complete accessible color system from a single brand hex for Tailwind v4 projects. It converts hex to HSL, generates an 11-shade scale from 50 through 950 with controlled saturation multipliers, and maps semantic tokens for light and dark modes with mandatory foreground pairs per background. WCAG contrast checks use luminance formulas with AA and AAA targets, quick check tables, and fix guidance for primary buttons and muted text failures. Output is paste-ready Tailwind v4 CSS with @theme shade variables, semantic tokens, and .dark overrides plus component usage examples for buttons, cards, and inputs. The workflow covers brand identity adjustments for conservative, vibrant, or minimal brands and ends with a verification checklist for both modes. Optional references mention CI contrast tests and multi-color palette expansion. Compatibility is marked claude-code-only in metadata.

  • 11-shade HSL scale generation from one brand hex input.
  • Semantic token mapping with required foreground pairs for dark mode.
  • WCAG contrast formulas with AA tables and common failure fixes.
  • Tailwind v4 @theme CSS output with light and dark overrides.
  • Verification checklist for body text, buttons, links, and borders.

Color Palette by the numbers

  • 2,707 all-time installs (skills.sh)
  • +80 installs in the week ending Jul 29, 2026 (Skillselion tracking)
  • Ranked #120 of 1,888 Design & UI/UX skills by installs in the Skillselion catalog
  • Security screen: LOW risk (skills.sh audit)
  • Data as of Jul 31, 2026 (Skillselion catalog sync)
At a glance

color-palette capabilities & compatibility

Capabilities
hsl shade scale generation from brand hex · semantic token mapping for light and dark modes · wcag contrast ratio calculation and fix guidance · tailwind v4 @theme css generation · component class usage examples for buttons and c · brand identity adjustment guidance by style prof
Use cases
ui design · frontend · web design
Pricing
Free
From the docs

What color-palette says it does

Every background token MUST have a paired foreground token.
SKILL.md
Produces Tailwind v4 CSS ready to paste into your project.
SKILL.md
Test both modes before shipping.
SKILL.md
npx skills add https://github.com/jezweb/claude-skills --skill color-palette

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs2.7k
repo stars946
Security audit3 / 3 scanners passed
Last updatedJuly 2, 2026
Repositoryjezweb/claude-skills

How do I turn one brand color into a full accessible palette with light and dark semantic tokens for Tailwind v4?

Generate accessible 11-shade palettes, semantic tokens, dark mode variants, and Tailwind v4 CSS from one brand hex.

Who is it for?

Teams setting up a Tailwind v4 design system who need WCAG-checked palettes from one brand hex.

Skip if: Skip for non-Tailwind stacks, finished design systems with locked tokens, or illustration color picking outside UI themes.

When should I use this skill?

User supplies a brand hex and wants a palette, design system colors, Tailwind theme colors, or contrast checks.

What you get

11-shade scale, paired semantic tokens, contrast-verified mappings, and paste-ready Tailwind v4 CSS for light and dark modes.

  • @theme CSS color file
  • Light and dark semantic token set

By the numbers

  • Template defines 11 primary shades from 50 through 950
  • Uses Tailwind v4 @theme syntax with @import "tailwindcss"

Files

SKILL.mdMarkdownGitHub ↗

Colour Palette Generator

Generate a complete, accessible colour system from a single brand hex. Produces Tailwind v4 CSS ready to paste into your project.

Workflow

Step 1: Get the Brand Hex

Ask for the primary brand colour. A single hex like #0D9488 is enough.

Step 2: Generate 11-Shade Scale

Convert hex to HSL, then generate shades by varying lightness while keeping hue constant.

Hex to HSL Conversion
function hexToHSL(hex) {
  hex = hex.replace(/^#/, '');
  const r = parseInt(hex.substring(0, 2), 16) / 255;
  const g = parseInt(hex.substring(2, 4), 16) / 255;
  const b = parseInt(hex.substring(4, 6), 16) / 255;

  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const diff = max - min;

  let l = (max + min) / 2;
  let s = 0;
  if (diff !== 0) {
    s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
  }

  let h = 0;
  if (diff !== 0) {
    if (max === r) h = ((g - b) / diff + (g < b ? 6 : 0)) / 6;
    else if (max === g) h = ((b - r) / diff + 2) / 6;
    else h = ((r - g) / diff + 4) / 6;
  }

  return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}
Lightness and Saturation Values
ShadeLightnessSaturation MultUse Case
5097%0.80Subtle backgrounds
10094%0.80Hover states
20087%0.85Borders, dividers
30075%0.90Disabled states
40062%0.95Placeholder text
50048%1.00Brand colour baseline
60040%1.00Primary actions (often the brand colour)
70033%1.00Hover on primary
80027%1.00Active states
90020%1.00Text on light bg
95010%1.00Darkest accents

Reduce saturation for lighter shades (50-200 by 15-20%, 300-400 by 5-10%) to prevent overly vibrant pastels. Keep full saturation for 500-950.

Complete Scale Generator
function generateShadeScale(brandHex) {
  const { h, s } = hexToHSL(brandHex);
  const shades = {
    50:  { l: 97, sMul: 0.8 },  100: { l: 94, sMul: 0.8 },
    200: { l: 87, sMul: 0.85 }, 300: { l: 75, sMul: 0.9 },
    400: { l: 62, sMul: 0.95 }, 500: { l: 48, sMul: 1.0 },
    600: { l: 40, sMul: 1.0 },  700: { l: 33, sMul: 1.0 },
    800: { l: 27, sMul: 1.0 },  900: { l: 20, sMul: 1.0 },
    950: { l: 10, sMul: 1.0 }
  };
  const result = {};
  for (const [shade, { l, sMul }] of Object.entries(shades)) {
    result[shade] = `hsl(${h}, ${Math.round(s * sMul)}%, ${l}%)`;
  }
  return result;
}
HSL to Hex Conversion
function hslToHex(h, s, l) {
  s = s / 100; l = l / 100;
  const c = (1 - Math.abs(2 * l - 1)) * s;
  const x = c * (1 - Math.abs((h / 60) % 2 - 1));
  const m = l - c / 2;
  let r = 0, g = 0, b = 0;
  if (h < 60) { r = c; g = x; }
  else if (h < 120) { r = x; g = c; }
  else if (h < 180) { g = c; b = x; }
  else if (h < 240) { g = x; b = c; }
  else if (h < 300) { r = x; b = c; }
  else { r = c; b = x; }
  r = Math.round((r + m) * 255);
  g = Math.round((g + m) * 255);
  b = Math.round((b + m) * 255);
  return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`.toUpperCase();
}
Verification

Generated shades should look like the same colour family with smooth progression. Light shades (50-300) usable for backgrounds, dark shades (700-950) usable for text. Brand colour recognisable in 500-700.

---

Step 3: Map Semantic Tokens

Every background token MUST have a paired foreground token. Never use a background without its pair or dark mode will break.

Light Mode Tokens
TokenShadeUse Case
backgroundwhitePage backgrounds
foreground950Body text
cardwhiteCard backgrounds
card-foreground900Card text
popoverwhiteDropdown/tooltip backgrounds
popover-foreground950Dropdown text
primary600Primary buttons, links
primary-foregroundwhiteText on primary buttons
secondary100Secondary buttons
secondary-foreground900Text on secondary buttons
muted50Disabled backgrounds, subtle sections
muted-foreground600Muted text, captions
accent100Hover states, subtle highlights
accent-foreground900Text on accent backgrounds
destructivered-600Delete buttons, errors
destructive-foregroundwhiteText on destructive buttons
border200Input borders, dividers
input200Input field borders
ring600Focus rings
Dark Mode Tokens
TokenShadeUse Case
background950Page backgrounds
foreground50Body text
card900Card backgrounds
card-foreground50Card text
popover900Dropdown backgrounds
popover-foreground50Dropdown text
primary500Primary buttons (brighter in dark)
primary-foregroundwhiteText on primary buttons
secondary800Secondary buttons
secondary-foreground50Text on secondary buttons
muted800Disabled backgrounds
muted-foreground400Muted text
accent800Hover states
accent-foreground50Text on accent backgrounds
destructivered-500Delete buttons (brighter)
destructive-foregroundwhiteText on destructive
border800Borders
input800Input borders
ring500Focus rings
Dark Mode Inversion Pattern

Dark mode inverts lightness while preserving hue and saturation. Swap extremes (50 becomes 950, 950 becomes 50), preserve middle (500 stays near 500).

Light ShadeDark EquivalentRole
50950Backgrounds
100900Subtle backgrounds
200800Borders
500500 (slightly brighter)Brand baseline
600400Primary actions
95050Text colour

Key dark mode principles:

  • Use shade 500 (not 600) for primary -- brighter for visibility on dark backgrounds
  • Use shade 50 (off-white) for text instead of pure #FFFFFF -- easier on eyes
  • Borders need ~10-15% lighter than background (e.g. 800 border on 950 background)
  • Higher elevation = lighter colour (opposite of light mode shadows)
  • Always update foreground when changing background

---

Step 4: Check Contrast

WCAG Minimum Ratios
Content TypeAAAAA
Normal text (<18px or <14px bold)4.5:17:1
Large text (>=18px or >=14px bold)3:14.5:1
UI components (buttons, borders)3:1Not defined
Graphical objects (icons, charts)3:1Not defined

Target AA for most projects, AAA for high-accessibility needs (government, healthcare).

Luminance and Contrast Formulas
function getLuminance(hex) {
  hex = hex.replace(/^#/, '');
  const r = parseInt(hex.substring(0, 2), 16) / 255;
  const g = parseInt(hex.substring(2, 4), 16) / 255;
  const b = parseInt(hex.substring(4, 6), 16) / 255;
  const rsRGB = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
  const gsRGB = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
  const bsRGB = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
  return 0.2126 * rsRGB + 0.7152 * gsRGB + 0.0722 * bsRGB;
}

function getContrastRatio(hex1, hex2) {
  const lum1 = getLuminance(hex1);
  const lum2 = getLuminance(hex2);
  const lighter = Math.max(lum1, lum2);
  const darker = Math.min(lum1, lum2);
  return (lighter + 0.05) / (darker + 0.05);
}
Quick Check Table -- Light Mode
ForegroundBackgroundRatioPass?Use Case
950white18.5:1AAABody text
900white14.2:1AAACard text
700white8.1:1AAAText
600white5.7:1AAText, buttons
500white3.9:1FailToo light for text
white6005.7:1AAButton text
white7008.1:1AAAButton text
600505.4:1AAMuted section text
Quick Check Table -- Dark Mode
ForegroundBackgroundRatioPass?Use Case
5095018.5:1AAABody text
5090014.2:1AAACard text
4009508.2:1AAAMuted text
4009006.3:1AAMuted text
white6005.7:1AAButton text

Rule of thumb: For text, aim for 50%+ lightness difference between foreground and background.

Essential Pairs to Verify

1. Body text: foreground on background (light: 950 on white = 18.5:1, dark: 50 on 950 = 18.5:1) 2. Primary button: primary-foreground on primary (light: white on 600 = 5.7:1, dark: white on 500 = 3.9:1 -- borderline) 3. Muted text: muted-foreground on muted (light: 600 on 50 = 5.4:1, dark: 400 on 800 = 4.1:1 -- may fail) 4. Card text: card-foreground on card (light: 900 on white = 14.2:1, dark: 50 on 900 = 14.2:1)

Fixing Common Contrast Failures

White on primary-500 fails (3.9:1): Use primary-600 instead (5.7:1), or use dark text on the button.

Muted text in dark mode fails (400 on 800 = 4.1:1): Use 300 on 900 = 6.8:1.

Links hard to see (500 on white = 3.9:1): Use primary-700 (8.1:1), or add underline decoration.

---

Step 5: Output Tailwind v4 CSS

@import "tailwindcss";

@theme {
  /* Shade scale */
  --color-primary-50: #F0FDFA;
  --color-primary-100: #CCFBF1;
  --color-primary-200: #99F6E4;
  --color-primary-300: #5EEAD4;
  --color-primary-400: #2DD4BF;
  --color-primary-500: #14B8A6;
  --color-primary-600: #0D9488;
  --color-primary-700: #0F766E;
  --color-primary-800: #115E59;
  --color-primary-900: #134E4A;
  --color-primary-950: #042F2E;

  /* Light mode semantic tokens */
  --color-background: #FFFFFF;
  --color-foreground: var(--color-primary-950);
  --color-card: #FFFFFF;
  --color-card-foreground: var(--color-primary-900);
  --color-popover: #FFFFFF;
  --color-popover-foreground: var(--color-primary-950);
  --color-primary: var(--color-primary-600);
  --color-primary-foreground: #FFFFFF;
  --color-secondary: var(--color-primary-100);
  --color-secondary-foreground: var(--color-primary-900);
  --color-muted: var(--color-primary-50);
  --color-muted-foreground: var(--color-primary-600);
  --color-accent: var(--color-primary-100);
  --color-accent-foreground: var(--color-primary-900);
  --color-destructive: #DC2626;
  --color-destructive-foreground: #FFFFFF;
  --color-border: var(--color-primary-200);
  --color-input: var(--color-primary-200);
  --color-ring: var(--color-primary-600);
  --radius: 0.5rem;
}

/* Dark mode overrides */
.dark {
  --color-background: var(--color-primary-950);
  --color-foreground: var(--color-primary-50);
  --color-card: var(--color-primary-900);
  --color-card-foreground: var(--color-primary-50);
  --color-popover: var(--color-primary-900);
  --color-popover-foreground: var(--color-primary-50);
  --color-primary: var(--color-primary-500);
  --color-primary-foreground: #FFFFFF;
  --color-secondary: var(--color-primary-800);
  --color-secondary-foreground: var(--color-primary-50);
  --color-muted: var(--color-primary-800);
  --color-muted-foreground: var(--color-primary-400);
  --color-accent: var(--color-primary-800);
  --color-accent-foreground: var(--color-primary-50);
  --color-destructive: #EF4444;
  --color-destructive-foreground: #FFFFFF;
  --color-border: var(--color-primary-800);
  --color-input: var(--color-primary-800);
  --color-ring: var(--color-primary-500);
}

Copy assets/tailwind-colors.css as a starting template.

---

Component Usage Examples

// Primary button
<button className="bg-primary text-primary-foreground hover:bg-primary/90">Click me</button>

// Secondary button
<button className="bg-secondary text-secondary-foreground hover:bg-secondary/80">Cancel</button>

// Card
<div className="bg-card text-card-foreground border-border rounded-lg">
  <h2>Title</h2>
  <p className="text-muted-foreground">Description</p>
</div>

// Input
<input className="bg-background text-foreground border-input focus:ring-ring" />

---

Common Adjustments

  • Too vibrant at light shades: Reduce saturation by 10-20%
  • Poor contrast on primary: Use shade 700+ for text
  • Dark mode too dark: Use shade 900 instead of 950 for backgrounds
  • Brand colour too light/dark: Adjust to shade 500-600 range
  • Dark mode looks washed out: Use shade 500 for primary (brighter than light mode's 600)
  • Pure white text too harsh in dark mode: Use shade 50 (off-white) instead
  • Dark mode muted text fails contrast: Use more extreme shades (300 on 900 instead of 400 on 800)

Brand Identity Adjustments

  • Conservative brands (finance, law): Use primary-700 for buttons, reduce saturation in light shades
  • Vibrant brands (creative, tech): Use primary-500-600, keep full saturation
  • Minimal brands (design, architecture): Use primary sparingly, emphasise muted tones, subtle borders (primary-100)

---

Verification Checklist

  • [ ] Body text: >=4.5:1 (normal) or >=3:1 (large)
  • [ ] Primary button text: >=4.5:1
  • [ ] Secondary button text: >=4.5:1
  • [ ] Muted text: >=4.5:1
  • [ ] Links: >=4.5:1 (or underlined)
  • [ ] UI elements (borders): >=3:1
  • [ ] Focus indicators: >=3:1
  • [ ] Error text: >=4.5:1
  • [ ] Dark mode: All above checks pass
  • [ ] Every background has a foreground pair
  • [ ] Brand colour recognisable in both modes
  • [ ] Borders visible but not harsh
  • [ ] Cards/sections have clear boundaries

Test both modes before shipping.

---

Optional References

  • Online contrast checkers: WebAIM (webaim.org/resources/contrastchecker), Coolors (coolors.co/contrast-checker), Accessible Colors (accessible-colors.com)
  • CI/CD contrast tests: Use getContrastRatio() in test suites to assert minimum ratios for all token pairs
  • Transparent/gradient edge cases: For colours with opacity, calculate against final rendered colour. For gradients, check both endpoints.
  • OLED dark mode: Use @media (prefers-contrast: high) with #000000 background for battery savings on AMOLED screens
  • Multi-colour palettes: Generate separate shade scales for each brand colour, map to different semantic roles (primary, accent)
  • Palette visualisation tools: coolors.co, paletton.com, Figma swatches
  • assets/tailwind-colors.css — Complete CSS output template

Related skills

How it compares

Pick color-palette over generic CSS skills when the stack is Tailwind v4 @theme and you need a full light/dark semantic token ramp.

FAQ

What shade is the brand baseline?

Shade 500 is the brand color baseline in the generated HSL scale, with 600 often used for primary actions in light mode.

Why must every background have a foreground pair?

Paired foreground tokens keep dark mode readable; using backgrounds without pairs breaks theme switching.

What contrast ratio should body text target?

Aim for at least 4.5:1 for normal text under WCAG AA, with AAA at 7:1 for higher accessibility needs.

Is Color Palette safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

Design & UI/UXuibranding

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.