cloudflare-api
Hit the Cloudflare REST API directly for operations that wrangler and MCP can't handle well. Bulk DNS, custom hostnames, email routing, cache purge, WAF rules,…
Generate complete, accessible colour palettes from a single brand hex. Produces 11-shade scale (50-950), semantic tokens, dark mode variants, Tailwind v4 CSS output, WCAG contrast checks. Use whenever the user supplies a brand hex and asks for a palette, mentions setting up a
$ npx -y skills add jezweb/claude-skills --skill color-palette --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/color-paletteContext preview
The summary Claude sees to decide when to auto-load this skill.
Generate complete, accessible colour palettes from a single brand hex. Produces 11-shade scale (50-950), semantic tokens, dark mode variants, Tailwind v4 CSS output, WCAG contrast checks. Use whenever the user supplies a brand hex and asks for a palette, mentions setting up a
name: color-palette description: "Generate complete, accessible colour palettes from a single brand hex. Produces 11-shade scale (50-950), semantic tokens, dark mode variants, Tailwind v4 CSS output, WCAG contrast checks. Use whenever the user supplies a brand hex and asks for a palette, mentions setting up a design system, wants Tailwind theme colours from a brand colour, or asks to check colour accessibility / contrast." compatibility: claude-code-only
Generate a complete, accessible colour system from a single brand hex. Produces Tailwind v4 CSS ready to paste into your project.
Ask for the primary brand colour. A single hex like `#0D9488` is enough.
Convert hex to HSL, then generate shades by varying lightness while keeping hue constant.
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) };
}| Shade | Lightness | Saturation Mult | Use Case | |-------|-----------|-----------------|----------| | 50 | 97% | 0.80 | Subtle backgrounds | | 100 | 94% | 0.80 | Hover states | | 200 | 87% | 0.85 | Borders, dividers | | 300 | 75% | 0.90 | Disabled states | | 400 | 62% | 0.95 | Placeholder text | | 500 | 48% | 1.00 | **Brand colour baseline** | | 600 | 40% | 1.00 | Primary actions (often the brand colour) | | 700 | 33% | 1.00 | Hover on primary | | 800 | 27% | 1.00 | Active states | | 900 | 20% | 1.00 | Text on light bg | | 950 | 10% | 1.00 | Darkest 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.
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;
}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();
}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.
---
Every background token MUST have a paired foreground token. Never use a background without its pair or dark mode will break.
| Token | Shade | Use Case | |-------|-------|----------| | `background` | white | Page backgrounds | | `foreground` | 950 | Body text | | `card` | white | Card backgrounds | | `card-foreground` | 900 | Card text | | `popover` | white | Dropdown/tooltip backgrounds | | `popover-foreground` | 950 | Dropdown text | | `primary` | 600 | Primary buttons, links | | `primary-foreground` | white | Text on primary buttons | | `secondary` | 100 | Secondary buttons | | `secondary-foreground` | 900 | Text on secondary buttons | | `muted` | 50 | Disabled backgrounds, subtle sections | | `muted-foreground` | 600 | Muted text, captions | | `accent` | 100 | Hover states, subtle highlights | | `accent-foreground` | 900 | Text on accent backgrounds | | `destructive` | red-600 | Delete buttons, errors | | `destructive-foreground` | white | Text on destructive buttons | | `border` | 200 | Input borders, dividers | | `input` | 200 | Input field borders | | `ring` | 600 | Focus rings |
| Token | Shade | Use Case | |-------|-------|----------| | `background` | 950 | Page backgrounds | | `foreground` | 50 | Body text | | `card` | 900 | Card backgrounds | | `card-foreground` | 50 | Card text | | `popover` | 900 | Dropdown backgrounds | | `popover-foreground` | 50 | Dropdown text | | `primary` | 500 | Primary buttons (brighter in dark) | | `primary-foreground` | white | Text on primary buttons | | `secondary` | 800 | Secondary buttons | | `secondary-foreground` | 50 | Text on secondary buttons | | `muted` | 800 | Disabled backgrounds | | `muted-foreground` | 400 | Muted text | | `accent` | 800 | Hover states | | `accent-foreground` | 50 | Text on accent backgrounds | | `destructive` | red-500 | Delete buttons (brighter) | | `destructive-foreground` | white | Text on destructive | | `border` | 800 | Borders | | `input` | 800 | Inpu
Production workflow skills for Claude Code. Each skill guides Claude through a recipe to produce tangible output — scaffolded projects, generated assets, professional documents, deployed services. Ten plugins of practical, production-oriented skills.
Repo: jezweb/claude-skills
Hit the Cloudflare REST API directly for operations that wrangler and MCP can't handle well. Bulk DNS, custom hostnames, email routing, cache purge, WAF rules,…
Scaffold and deploy Cloudflare Workers with Hono routing, Vite plugin, and Static Assets. Describe project, scaffold structure, configure bindings, deploy. Use…
Generate Drizzle ORM schemas for Cloudflare D1 databases with correct D1-specific patterns. Produces schema files, migration commands, type exports, and…
Cloudflare D1 migration workflow: generate with Drizzle, inspect SQL for gotchas, apply to local and remote, fix stuck migrations, handle partial failures. Use…
Generate database seed scripts with realistic sample data. Reads Drizzle schemas or SQL migrations, respects foreign key ordering, produces idempotent…
Scaffold Hono API routes for Cloudflare Workers. Produces route files, middleware, typed bindings, Zod validation, error handling, and API_ENDPOINTS.md…