{"id":"color-palette","name":"color-palette","summary":"単一のブランドの六角形から、完全でアクセスしやすいカラーパレットを生成しましょう。11シェードスケール(50-950)、セマンティックトークン、ダークモードバリアント、Tailwind v4 CSS出力、WCAGコントラストチェックを生成します。","body":"# Colour Palette Generator\n\nGenerate a complete, accessible colour system from a single brand hex. Produces Tailwind v4 CSS ready to paste into your project.\n\n## Workflow\n\n### Step 1: Get the Brand Hex\n\nAsk for the primary brand colour. A single hex like `#0D9488` is enough.\n\n### Step 2: Generate 11-Shade Scale\n\nConvert hex to HSL, then generate shades by varying lightness while keeping hue constant.\n\n#### Hex to HSL Conversion\n\n```javascript\nfunction hexToHSL(hex) {\n  hex = hex.replace(/^#/, '');\n  const r = parseInt(hex.substring(0, 2), 16) / 255;\n  const g = parseInt(hex.substring(2, 4), 16) / 255;\n  const b = parseInt(hex.substring(4, 6), 16) / 255;\n\n  const max = Math.max(r, g, b);\n  const min = Math.min(r, g, b);\n  const diff = max - min;\n\n  let l = (max + min) / 2;\n  let s = 0;\n  if (diff !== 0) {\n    s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);\n  }\n\n  let h = 0;\n  if (diff !== 0) {\n    if (max === r) h = ((g - b) / diff + (g < b ? 6 : 0)) / 6;\n    else if (max === g) h = ((b - r) / diff + 2) / 6;\n    else h = ((r - g) / diff + 4) / 6;\n  }\n\n  return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n```\n\n#### Lightness and Saturation Values\n\n| Shade | Lightness | Saturation Mult | Use Case |\n|-------|-----------|-----------------|----------|\n| 50 | 97% | 0.80 | Subtle backgrounds |\n| 100 | 94% | 0.80 | Hover states |\n| 200 | 87% | 0.85 | Borders, dividers |\n| 300 | 75% | 0.90 | Disabled states |\n| 400 | 62% | 0.95 | Placeholder text |\n| 500 | 48% | 1.00 | **Brand colour baseline** |\n| 600 | 40% | 1.00 | Primary actions (often the brand colour) |\n| 700 | 33% | 1.00 | Hover on primary |\n| 800 | 27% | 1.00 | Active states |\n| 900 | 20% | 1.00 | Text on light bg |\n| 950 | 10% | 1.00 | Darkest accents |\n\nReduce 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.\n\n#### Complete Scale Generator\n\n```javascript\nfunction generateShadeScale(brandHex) {\n  const { h, s } = hexToHSL(brandHex);\n  const shades = {\n    50:  { l: 97, sMul: 0.8 },  100: { l: 94, sMul: 0.8 },\n    200: { l: 87, sMul: 0.85 }, 300: { l: 75, sMul: 0.9 },\n    400: { l: 62, sMul: 0.95 }, 500: { l: 48, sMul: 1.0 },\n    600: { l: 40, sMul: 1.0 },  700: { l: 33, sMul: 1.0 },\n    800: { l: 27, sMul: 1.0 },  900: { l: 20, sMul: 1.0 },\n    950: { l: 10, sMul: 1.0 }\n  };\n  const result = {};\n  for (const [shade, { l, sMul }] of Object.entries(shades)) {\n    result[shade] = `hsl(${h}, ${Math.round(s * sMul)}%, ${l}%)`;\n  }\n  return result;\n}\n```\n\n#### HSL to Hex Conversion\n\n```javascript\nfunction hslToHex(h, s, l) {\n  s = s / 100; l = l / 100;\n  const c = (1 - Math.abs(2 * l - 1)) * s;\n  const x = c * (1 - Math.abs((h / 60) % 2 - 1));\n  const m = l - c / 2;\n  let r = 0, g = 0, b = 0;\n  if (h < 60) { r = c; g = x; }\n  else if (h < 120) { r = x; g = c; }\n  else if (h < 180) { g = c; b = x; }\n  else if (h < 240) { g = x; b = c; }\n  else if (h < 300) { r = x; b = c; }\n  else { r = c; b = x; }\n  r = Math.round((r + m) * 255);\n  g = Math.round((g + m) * 255);\n  b = Math.round((b + m) * 255);\n  return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`.toUpperCase();\n}\n```\n\n#### Verification\n\nGenerated 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.\n\n---\n\n### Step 3: Map Semantic Tokens\n\nEvery background token MUST have a paired foreground token. Never use a background without its pair or dark mode will break.\n\n#### Light Mode Tokens\n\n| Token | Shade | Use Case |\n|-------|-------|----------|\n| `background` | white | Page backgrounds |\n| `foreground` | 950 | Body text |\n| `card` | white | Card backgrounds |\n| `card-foreground` | 900 | Card text |\n| `popover` | white | Dropdown/tooltip backgrounds |\n| `popover-foreground` | 950 | Dropdown text |\n| `primary` | 600 | Primary buttons, links |\n| `primary-foreground` | white | Text on primary buttons |\n| `secondary` | 100 | Secondary buttons |\n| `secondary-foreground` | 900 | Text on secondary buttons |\n| `muted` | 50 | Disabled backgrounds, subtle sections |\n| `muted-foreground` | 600 | Muted text, captions |\n| `accent` | 100 | Hover states, subtle highlights |\n| `accent-foreground` | 900 | Text on accent backgrounds |\n| `destructive` | red-600 | Delete buttons, errors |\n| `destructive-foreground` | white | Text on destructive buttons |\n| `border` | 200 | Input borders, dividers |\n| `input` | 200 | Input field borders |\n| `ring` | 600 | Focus rings |\n\n#### Dark Mode Tokens\n\n| Token | Shade | Use Case |\n|-------|-------|----------|\n| `background` | 950 | Page backgrounds |\n| `foreground` | 50 | Body text |\n| `card` | 900 | Card backgrounds |\n| `card-foreground` | 50 | Card text |\n| `popover` | 900 | Dropdown backgrounds |\n| `popover-foreground` | 50 | Dropdown text |\n| `primary` | 500 | Primary buttons (brighter in dark) |\n| `primary-foreground` | white | Text on primary buttons |\n| `secondary` | 800 | Secondary buttons |\n| `secondary-foreground` | 50 | Text on secondary buttons |\n| `muted` | 800 | Disabled backgrounds |\n| `muted-foreground` | 400 | Muted text |\n| `accent` | 800 | Hover states |\n| `accent-foreground` | 50 | Text on accent backgrounds |\n| `destructive` | red-500 | Delete buttons (brighter) |\n| `destructive-foreground` | white | Text on destructive |\n| `border` | 800 | Borders |\n| `input` | 800 | Input borders |\n| `ring` | 500 | Focus rings |\n\n#### Dark Mode Inversion Pattern\n\nDark mode inverts lightness while preserving hue and saturation. Swap extremes (50 becomes 950, 950 becomes 50), preserve middle (500 stays near 500).\n\n| Light Shade | Dark Equivalent | Role |\n|-------------|-----------------|------|\n| 50 | 950 | Backgrounds |\n| 100 | 900 | Subtle backgrounds |\n| 200 | 800 | Borders |\n| 500 | 500 (slightly brighter) | Brand baseline |\n| 600 | 400 | Primary actions |\n| 950 | 50 | Text colour |\n\nKey dark mode principles:\n- Use shade 500 (not 600) for primary -- brighter for visibility on dark backgrounds\n- Use shade 50 (off-white) for text instead of pure `#FFFFFF` -- easier on eyes\n- Borders need ~10-15% lighter than background (e.g. 800 border on 950 background)\n- Higher elevation = lighter colour (opposite of light mode shadows)\n- Always update foreground when changing background\n\n---\n\n### Step 4: Check Contrast\n\n#### WCAG Minimum Ratios\n\n| Content Type | AA | AAA |\n|--------------|-----|-----|\n| Normal text (<18px or <14px bold) | 4.5:1 | 7:1 |\n| Large text (>=18px or >=14px bold) | 3:1 | 4.5:1 |\n| UI components (buttons, borders) | 3:1 | Not defined |\n| Graphical objects (icons, charts) | 3:1 | Not defined |\n\nTarget AA for most projects, AAA for high-accessibility needs (government, healthcare).\n\n#### Luminance and Contrast Formulas\n\n```javascript\nfunction getLuminance(hex) {\n  hex = hex.replace(/^#/, '');\n  const r = parseInt(hex.substring(0, 2), 16) / 255;\n  const g = parseInt(hex.substring(2, 4), 16) / 255;\n  const b = parseInt(hex.substring(4, 6), 16) / 255;\n  const rsRGB = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);\n  const gsRGB = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);\n  const bsRGB = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);\n  return 0.2126 * rsRGB + 0.7152 * gsRGB + 0.0722 * bsRGB;\n}\n\nfunction getContrastRatio(hex1, hex2) {\n  const lum1 = getLuminance(hex1);\n  const lum2 = getLuminance(hex2);\n  const lighter = Math.max(lum1, lum2);\n  const darker = Math.min(lum1, lum2);\n  return (lighter + 0.05) / (darker + 0.05);\n}\n```\n\n#### Quick Check Table -- Light Mode\n\n| Foreground | Background | Ratio | Pass? | Use Case |\n|------------|------------|-------|-------|----------|\n| 950 | white | 18.5:1 | AAA | Body text |\n| 900 | white | 14.2:1 | AAA | Card text |\n| 700 | white | 8.1:1 | AAA | Text |\n| 600 | white | 5.7:1 | AA | Text, buttons |\n| 500 | white | 3.9:1 | Fail | Too light for text |\n| white | 600 | 5.7:1 | AA | Button text |\n| white | 700 | 8.1:1 | AAA | Button text |\n| 600 | 50 | 5.4:1 | AA | Muted section text |\n\n#### Quick Check Table -- Dark Mode\n\n| Foreground | Background | Ratio | Pass? | Use Case |\n|------------|------------|-------|-------|----------|\n| 50 | 950 | 18.5:1 | AAA | Body text |\n| 50 | 900 | 14.2:1 | AAA | Card text |\n| 400 | 950 | 8.2:1 | AAA | Muted text |\n| 400 | 900 | 6.3:1 | AA | Muted text |\n| white | 600 | 5.7:1 | AA | Button text |\n\n**Rule of thumb**: For text, aim for 50%+ lightness difference between foreground and background.\n\n#### Essential Pairs to Verify\n\n1. **Body text**: foreground on background (light: 950 on white = 18.5:1, dark: 50 on 950 = 18.5:1)\n2. **Primary button**: primary-foreground on primary (light: white on 600 = 5.7:1, dark: white on 500 = 3.9:1 -- borderline)\n3. **Muted text**: muted-foreground on muted (light: 600 on 50 = 5.4:1, dark: 400 on 800 = 4.1:1 -- may fail)\n4. **Card text**: card-foreground on card (light: 900 on white = 14.2:1, dark: 50 on 900 = 14.2:1)\n\n#### Fixing Common Contrast Failures\n\n**White on primary-500 fails (3.9:1)**: Use primary-600 instead (5.7:1), or use dark text on the button.\n\n**Muted text in dark mode fails (400 on 800 = 4.1:1)**: Use 300 on 900 = 6.8:1.\n\n**Links hard to see (500 on white = 3.9:1)**: Use primary-700 (8.1:1), or add underline decoration.\n\n---\n\n### Step 5: Output Tailwind v4 CSS\n\n```css\n@import \"tailwindcss\";\n\n@theme {\n  /* Shade scale */\n  --color-primary-50: #F0FDFA;\n  --color-primary-100: #CCFBF1;\n  --color-primary-200: #99F6E4;\n  --color-primary-300: #5EEAD4;\n  --color-primary-400: #2DD4BF;\n  --color-primary-500: #14B8A6;\n  --color-primary-600: #0D9488;\n  --color-primary-700: #0F766E;\n  --color-primary-800: #115E59;\n  --color-primary-900: #134E4A;\n  --color-primary-950: #042F2E;\n\n  /* Light mode semantic tokens */\n  --color-background: #FFFFFF;\n  --color-foreground: var(--color-primary-950);\n  --color-card: #FFFFFF;\n  --color-card-foreground: var(--color-primary-900);\n  --color-popover: #FFFFFF;\n  --color-popover-foreground: var(--color-primary-950);\n  --color-primary: var(--color-primary-600);\n  --color-primary-foreground: #FFFFFF;\n  --color-secondary: var(--color-primary-100);\n  --color-secondary-foreground: var(--color-primary-900);\n  --color-muted: var(--color-primary-50);\n  --color-muted-foreground: var(--color-primary-600);\n  --color-accent: var(--color-primary-100);\n  --color-accent-foreground: var(--color-primary-900);\n  --color-destructive: #DC2626;\n  --color-destructive-foreground: #FFFFFF;\n  --color-border: var(--color-primary-200);\n  --color-input: var(--color-primary-200);\n  --color-ring: var(--color-primary-600);\n  --radius: 0.5rem;\n}\n\n/* Dark mode overrides */\n.dark {\n  --color-background: var(--color-primary-950);\n  --color-foreground: var(--color-primary-50);\n  --color-card: var(--color-primary-900);\n  --color-card-foreground: var(--color-primary-50);\n  --color-popover: var(--color-primary-900);\n  --color-popover-foreground: var(--color-primary-50);\n  --color-primary: var(--color-primary-500);\n  --color-primary-foreground: #FFFFFF;\n  --color-secondary: var(--color-primary-800);\n  --color-secondary-foreground: var(--color-primary-50);\n  --color-muted: var(--color-primary-800);\n  --color-muted-foreground: var(--color-primary-400);\n  --color-accent: var(--color-primary-800);\n  --color-accent-foreground: var(--color-primary-50);\n  --color-destructive: #EF4444;\n  --color-destructive-foreground: #FFFFFF;\n  --color-border: var(--color-primary-800);\n  --color-input: var(--color-primary-800);\n  --color-ring: var(--color-primary-500);\n}\n```\n\nCopy `assets/tailwind-colors.css` as a starting template.\n\n---\n\n## Component Usage Examples\n\n```tsx\n// Primary button\n<button className=\"bg-primary text-primary-foreground hover:bg-primary/90\">Click me</button>\n\n// Secondary button\n<button className=\"bg-secondary text-secondary-foreground hover:bg-secondary/80\">Cancel</button>\n\n// Card\n<div className=\"bg-card text-card-foreground border-border rounded-lg\">\n  <h2>Title</h2>\n  <p className=\"text-muted-foreground\">Description</p>\n</div>\n\n// Input\n<input className=\"bg-background text-foreground border-input focus:ring-ring\" />\n```\n\n---\n\n## Common Adjustments\n\n- **Too vibrant at light shades**: Reduce saturation by 10-20%\n- **Poor contrast on primary**: Use shade 700+ for text\n- **Dark mode too dark**: Use shade 900 instead of 950 for backgrounds\n- **Brand colour too light/dark**: Adjust to shade 500-600 range\n- **Dark mode looks washed out**: Use shade 500 for primary (brighter than light mode's 600)\n- **Pure white text too harsh in dark mode**: Use shade 50 (off-white) instead\n- **Dark mode muted text fails contrast**: Use more extreme shades (300 on 900 instead of 400 on 800)\n\n### Brand Identity Adjustments\n\n- **Conservative brands** (finance, law): Use primary-700 for buttons, reduce saturation in light shades\n- **Vibrant brands** (creative, tech): Use primary-500-600, keep full saturation\n- **Minimal brands** (design, architecture): Use primary sparingly, emphasise muted tones, subtle borders (primary-100)\n\n---\n\n## Verification Checklist\n\n- [ ] Body text: >=4.5:1 (normal) or >=3:1 (large)\n- [ ] Primary button text: >=4.5:1\n- [ ] Secondary button text: >=4.5:1\n- [ ] Muted text: >=4.5:1\n- [ ] Links: >=4.5:1 (or underlined)\n- [ ] UI elements (borders): >=3:1\n- [ ] Focus indicators: >=3:1\n- [ ] Error text: >=4.5:1\n- [ ] Dark mode: All above checks pass\n- [ ] Every background has a foreground pair\n- [ ] Brand colour recognisable in both modes\n- [ ] Borders visible but not harsh\n- [ ] Cards/sections have clear boundaries\n\n**Test both modes before shipping.**\n\n---\n\n## Optional References\n\n- **Online contrast checkers**: WebAIM (webaim.org/resources/contrastchecker), Coolors (coolors.co/contrast-checker), Accessible Colors (accessible-colors.com)\n- **CI/CD contrast tests**: Use `getContrastRatio()` in test suites to assert minimum ratios for all token pairs\n- **Transparent/gradient edge cases**: For colours with opacity, calculate against final rendered colour. For gradients, check both endpoints.\n- **OLED dark mode**: Use `@media (prefers-contrast: high)` with `#000000` background for battery savings on AMOLED screens\n- **Multi-colour palettes**: Generate separate shade scales for each brand colour, map to different semantic roles (primary, accent)\n- **Palette visualisation tools**: coolors.co, paletton.com, Figma swatches\n- `assets/tailwind-colors.css` — Complete CSS output template","author":"@jezweb","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/jezweb/claude-skills/tree/main/plugins/design-assets/skills/color-palette","license":"MIT","category":"coding","lang":"en","tokens":4614,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"assets/tailwind-colors.css","size":5136,"sha256":"826e093dcae1eac9f0a86c8264df455708b0badabc480b0e26d861c03c300c63"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["coolors.co","webaim.org"]}}