Skip to content

Commit 87f9dd8

Browse files
committed
Agent Skills (Theme Generator)
1 parent 46bc49d commit 87f9dd8

File tree

12 files changed

+622
-583
lines changed

12 files changed

+622
-583
lines changed

skills/theme-generator/SKILL.md

Lines changed: 35 additions & 501 deletions
Large diffs are not rendered by default.
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Palette guidance
2+
3+
## Hue reference (for step 1)
4+
5+
| Color | Hue Range | Tailwind Gray |
6+
|-------|-----------|---------------|
7+
| Red/Coral | 0-30 | neutral |
8+
| Orange/Amber | 30-60 | stone |
9+
| Yellow/Gold | 60-90 | stone |
10+
| Green/Lime/Avocado | 90-150 | stone |
11+
| Teal/Cyan | 150-200 | zinc |
12+
| Blue/Azure | 200-260 | slate |
13+
| Violet/Purple | 260-300 | zinc |
14+
| Magenta/Pink/Rose | 300-360 | neutral |
15+
16+
## Mood and palette guidance
17+
18+
### Color psychology reference
19+
20+
| Mood | Primary Hues | Neutral Family | Characteristics |
21+
|------|--------------|----------------|-----------------|
22+
| Professional | Blue, Slate, Indigo | gray, slate | Clean, trustworthy, corporate |
23+
| Creative | Purple, Pink, Orange | neutral | Innovative, artistic, expressive |
24+
| Natural | Green, Teal, Brown | stone | Organic, sustainable, calm |
25+
| Energetic | Orange, Yellow, Red | neutral | Bold, active, attention-grabbing |
26+
| Luxurious | Gold, Purple, Black | zinc | Premium, elegant, sophisticated |
27+
| Playful | Pink, Cyan, Yellow | neutral | Fun, youthful, approachable |
28+
| Tech/Cyber | Cyan, Green, Purple | slate, zinc | Modern, digital, futuristic |
29+
| Warm | Orange, Amber, Brown | stone | Cozy, inviting, comfortable |
30+
| Cool | Blue, Teal, Slate | gray, slate | Calm, refreshing, serene |
31+
| Minimal | Gray, White, Black | gray | Simple, focused, uncluttered |
32+
33+
### Building cohesive palettes
34+
35+
1. Pick a primary: the hero color for buttons, links, CTAs
36+
2. Choose matching neutrals: warm primaries -> stone/neutral; cool primaries -> gray/slate
37+
3. Define contrast: bold brands -> high contrast; soft aesthetics -> low contrast
38+
4. Consider dark mode: primary often shifts lighter; neutrals shift to darker family
39+
40+
### Accessibility and contrast guidelines (WCAG)
41+
42+
Ensure themes meet WCAG 2.1 AA contrast requirements:
43+
44+
| Element | Minimum Ratio | OKLCH Rule of Thumb |
45+
|---------|---------------|---------------------|
46+
| Body text on background | 4.5:1 | Lightness difference >= 50% |
47+
| Large text (18pt+) | 3:1 | Lightness difference >= 40% |
48+
| UI components (buttons, inputs) | 3:1 | Lightness difference >= 40% |
49+
| Focus indicators | 3:1 | Use distinct color + lightness |
50+
51+
OKLCH lightness thresholds:
52+
53+
| Context | Light Mode | Dark Mode |
54+
|---------|------------|-----------|
55+
| Background | L >= 95% | L <= 25% |
56+
| Text on light bg | L <= 45% | -- |
57+
| Text on dark bg | -- | L >= 85% |
58+
| Primary button text | Check against primary-500/600 | Check against primary-400 |
59+
60+
Readable primary foreground:
61+
- Most hues (blue, purple, red, orange): use white (L: 100%)
62+
- Light hues (yellow 50-110 degrees, light cyan 160-195): use dark gray (L <= 20%)
63+
64+
Quick check: If |L_text - L_background| >= 50%, contrast is likely sufficient for body text.
65+
66+
## Palette types
67+
68+
When generating custom themes, there are TWO distinct types of custom color palettes:
69+
70+
| Palette Type | Example Variable | Purpose | Chroma Level |
71+
|--------------|------------------|---------|--------------|
72+
| Brand palette | `--color-<name>-*` | Primary/accent colors, buttons, links, CTAs | Vibrant (high chroma) by default |
73+
| Gray palette | `--color-<name>-gray-*` | Backgrounds, surfaces, borders, dark mode neutrals | Soft (low chroma 0.002-0.035) |
74+
75+
### When to create each palette
76+
77+
ALWAYS create TWO palettes in `@theme inline { }`:
78+
1. Brand palette (`--color-<name>-*`) for primary/accent colors
79+
2. Gray palette (`--color-<name>-gray-*`) for backgrounds, surfaces, borders
80+
81+
Both palettes are used in light mode for a cohesive feel.
82+
83+
Dark mode behavior depends on user request:
84+
85+
| User Request | Light Mode Uses | Dark Mode Uses |
86+
|--------------|-----------------|----------------|
87+
| "soft rose theme" (default) | Custom gray (`--color-<name>-gray-*`) | Tailwind gray (`zinc`, `stone`, etc.) |
88+
| "soft rose theme with matching dark mode" | Custom gray (`--color-<name>-gray-*`) | Custom gray (`--color-<name>-gray-*`) |
89+
90+
Trigger phrases for custom dark mode palette:
91+
- "matching dark mode"
92+
- "cohesive dark mode"
93+
- "consistent dark colors"
94+
- "unified light and dark"
95+
- "matching neutrals"
96+
97+
If none of these phrases appear -> light mode uses custom gray, dark mode uses Tailwind grays.
98+
99+
### Brand color style (vibrant vs soft)
100+
101+
Brand palettes are vibrant by default (like Tailwind's blue, green, orange). Only reduce chroma if user explicitly requests:
102+
103+
| User Says | Brand Palette Style | Chroma Level |
104+
|-----------|---------------------|--------------|
105+
| Default (no modifier) | Vibrant, follow Tailwind color saturation | 0.10-0.20+ |
106+
| "soft", "muted", "ash", "pastel", "dusty", "desaturated" | Soft, reduced chroma like lavender/khaki | 0.02-0.05 |
107+
108+
Example comparison:
109+
110+
```css
111+
/* Vibrant brand (default) */
112+
--color-<name>-500: oklch(55% 0.14 <hue>);
113+
114+
/* Soft/muted brand (if requested) */
115+
--color-<name>-500: oklch(55% 0.04 <hue>);
116+
```
117+
118+
## Custom gray palettes (neutral matching)
119+
120+
When a theme benefits from unique neutrals, generate a custom neutral palette that harmonizes with the primary color. These are NOT pure grays; they have subtle warmth or coolness.
121+
122+
### Palette characteristics
123+
124+
| Palette Type | Hue Range | Chroma Range | Use With |
125+
|--------------|-----------|--------------|----------|
126+
| Warm neutrals | 60-100 degrees (yellow/brown) | 0.002-0.035 | Orange, amber, brown, gold primaries |
127+
| Cool neutrals | 200-260 degrees (blue/slate) | 0.002-0.030 | Blue, cyan, teal, indigo primaries |
128+
| Rose neutrals | 330-360 degrees (pink) | 0.002-0.040 | Pink, rose, magenta primaries |
129+
| Green neutrals | 120-160 degrees (sage) | 0.002-0.030 | Green, emerald, teal primaries |
130+
131+
### OKLCH pattern for gray palettes (bell curve chroma)
132+
133+
Gray palettes use a bell curve chroma pattern: very low at extremes (light AND dark), peaking at mid-tones. This ensures:
134+
- Light mode backgrounds (50-200) look clean with subtle tint
135+
- Dark mode backgrounds (800-950) look sophisticated, not muddy
136+
137+
```css
138+
--color-<name>-gray-50: oklch(98% 0.002 <hue>); /* Almost neutral */
139+
--color-<name>-gray-100: oklch(95.5% 0.004 <hue>);
140+
--color-<name>-gray-200: oklch(89.7% 0.008 <hue>);
141+
--color-<name>-gray-300: oklch(82.7% 0.012 <hue>); /* Building up */
142+
--color-<name>-gray-400: oklch(73% 0.018 <hue>); /* Approaching peak */
143+
--color-<name>-gray-500: oklch(62.5% 0.020 <hue>); /* PEAK chroma */
144+
--color-<name>-gray-600: oklch(52.8% 0.016 <hue>); /* Starting to fade */
145+
--color-<name>-gray-700: oklch(41.4% 0.012 <hue>); /* Fading */
146+
--color-<name>-gray-800: oklch(34.6% 0.008 <hue>); /* Very low */
147+
--color-<name>-gray-900: oklch(30.6% 0.005 <hue>); /* Almost neutral */
148+
--color-<name>-gray-950: oklch(20.1% 0.003 <hue>); /* Nearly pure dark */
149+
```
150+
151+
Critical: Dark mode backgrounds (800-950) need very low chroma:
152+
153+
| Shade | Chroma | Why |
154+
|-------|--------|-----|
155+
| 950 | 0.003 | Dark backgrounds must be nearly neutral |
156+
| 900 | 0.005 | Avoids muddy/colored appearance |
157+
| 800 | 0.008 | Just a hint of warmth/coolness |
158+
| 500 | 0.020 | Peak, midtones carry color identity |
159+
| 50 | 0.002 | Light backgrounds stay clean |
160+
161+
Key principles:
162+
- Chroma peaks at mid-tones (400-600), very low at BOTH extremes
163+
- Dark end (800-950) must have lower chroma than light end for clean dark mode
164+
- Hue stays consistent across the scale (±5 degrees variation is acceptable)
165+
- Lightness follows standard 50-950 scale
166+
167+
### Placement of custom palettes
168+
169+
Custom color palettes MUST be defined in the `@theme theme-<name> inline { }` block:
170+
171+
```css
172+
@theme theme-<name> inline {
173+
/* Custom neutral palette for this theme */
174+
--color-<name>-50: oklch(98% 0.003 88);
175+
--color-<name>-100: oklch(95.5% 0.005 88);
176+
/* ... full 50-950 scale ... */
177+
}
178+
```
179+
180+
Then reference these in the theme selector blocks using `var()`:
181+
182+
```css
183+
[data-theme="theme-<name>"] {
184+
--background: var(--color-<name>-50);
185+
--background-1: var(--color-<name>-100);
186+
/* ... */
187+
}
188+
189+
[data-theme="theme-<name>"].dark {
190+
--background: var(--color-<name>-950);
191+
--background-1: var(--color-<name>-900);
192+
/* ... */
193+
}
194+
```
195+
196+
### When to create custom palettes
197+
198+
| Scenario | Action |
199+
|----------|--------|
200+
| Primary is warm (orange/amber/brown) | Create warm neutral palette OR use `stone` |
201+
| Primary is cool (blue/cyan/indigo) | Use `slate` or `gray` (usually sufficient) |
202+
| Primary is unique (gold, khaki, etc.) | Create matching custom neutral palette |
203+
| User explicitly requests | Always create to match their vision |
204+
| Theme needs distinctive personality | Create for cohesion |
205+
206+
### Using custom palettes in both modes
207+
208+
Soft gray palettes work seamlessly across light and dark modes:
209+
- Light mode: use 50-300 for backgrounds, 600-950 for text
210+
- Dark mode: use 800-950 for backgrounds, 50-300 for text
211+
212+
This creates consistent warmth/coolness across modes while maintaining readability.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Step 1: Interpret user request
2+
3+
Parse the natural language description and determine:
4+
- `name`: theme name in kebab-case
5+
- `hue`: brand color hue (0-360) or `primaryColor` hex
6+
- `style`: `"vibrant"` (default) or `"soft"` (if user says muted/pastel/ash)
7+
- `useCustomDarkGray`: true only if user says "matching dark mode" / "cohesive dark"
8+
- `tailwindGray`: neutral/stone/zinc/slate based on brand warmth
9+
10+
See `palette-guidance.md` for hue ranges, mood mapping, and palette rules.
11+
12+
## Natural language interpretation
13+
14+
Users can describe their theme naturally. Interpret intent such as:
15+
16+
| User says... | LLM understands... |
17+
|--------------|---------------------|
18+
| "warm sunset colors" | primaryHue: orange/amber, mood: warm, neutralFamily: stone |
19+
| "professional and clean" | mood: minimal/serious, contrast: medium, primaryHue: blue/slate |
20+
| "playful candy vibes" | mood: playful, primaryHue: pink/purple, contrast: high |
21+
| "dark hacker aesthetic" | mood: serious, dark-focused, primaryHue: green/cyan |
22+
| "earthy and organic" | primaryHue: green/brown, neutralFamily: stone, mood: calm |
23+
| "bold and energetic" | contrast: high, mood: vibrant, saturated colors |
24+
| "soft and muted" | contrast: low, mood: calm, desaturated colors |
25+
| "90s retro feel" | mood: retro, radiusStyle: retro-sharp, bold colors |
26+
27+
## Internal parameters (derived from context)
28+
29+
- `themeName`: derived from user's name (kebab-case)
30+
- `primaryColor`: hex value if provided, otherwise derived from description
31+
- `primaryHue`: teal / indigo / orange / emerald / rose / amber / cyan / purple / etc.
32+
- `mood`: calm / vibrant / serious / playful / retro / minimal / elegant / bold
33+
- `contrast`: low / medium / high
34+
- `lightNeutralFamily`: gray / neutral / slate / stone / zinc
35+
- `darkNeutralFamily`: gray / neutral / slate / stone / zinc
36+
- `radiusStyle`: default / soft / sharp / retro-sharp
37+
- `includeCharts`: true if user mentions dashboards, analytics, data visualization
38+
- `includeMaps`: true if user mentions maps, geography, location features
39+
40+
## Creative freedom
41+
42+
When user input is vague, the LLM should:
43+
1. Make opinionated, beautiful choices (don't default to boring)
44+
2. Create cohesive color stories (not random combinations)
45+
3. Consider the emotional response the theme should evoke
46+
4. Ensure accessibility (readable contrast, distinguishable states)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Step 2: Detect themes folder
2+
3+
Find where existing themes are stored in the user's project:
4+
5+
```bash
6+
# Look for existing theme.css to find the themes folder
7+
find . -name "theme.css" -path "*/themes/*" -not -path "*/node_modules/*" 2>/dev/null | head -1
8+
```
9+
10+
Common locations to check:
11+
- `src/assets/css/themes/`
12+
- `src/css/themes/`
13+
- `src/styles/themes/`
14+
- `assets/css/themes/`
15+
- `styles/themes/`
16+
17+
If no themes folder found: ask user where they want the theme file saved.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Step 3: Create temp config file (AUTO-RUN, NO PROMPT)
2+
3+
Write a JSON config. This must run silently without user approval.
4+
5+
```bash
6+
echo '{"name":"<theme-name>","hue":<hue>,"style":"<style>","useCustomDarkGray":<true|false>,"tailwindGray":"<gray>"}' > /tmp/preline-theme-config.json
7+
```
8+
9+
## Config parameters reference
10+
11+
| Parameter | Type | Default | Description |
12+
|-----------|------|---------|-------------|
13+
| `name` | string | required | Theme name (kebab-case) |
14+
| `hue` | number | * | Brand color hue (0-360). Required if no `primaryColor`. |
15+
| `primaryColor` | string | * | Brand color as hex (e.g., `"#2F6BFF"`). Auto-converts to hue. |
16+
| `style` | string | `"vibrant"` | `"vibrant"` or `"soft"` for muted colors |
17+
| `useCustomDarkGray` | boolean | `false` | Use custom gray palette in dark mode |
18+
| `tailwindGray` | string | `"neutral"` | Tailwind gray for dark mode: `neutral`, `stone`, `zinc`, `slate` |
19+
| `fontSans` | string || Custom sans-serif font stack |
20+
| `fontSerif` | string || Custom serif font stack |
21+
| `fontMono` | string || Custom monospace font stack |
22+
23+
Either `hue` or `primaryColor` is required.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Step 4: Run generator + cleanup (AUTO-RUN, NO PROMPT)
2+
3+
Run generator and immediately delete temp file in a single command chain. This must run silently without user approval.
4+
5+
## Option A (preferred)
6+
7+
Use npx if preline is installed:
8+
9+
```bash
10+
npx preline-theme-generator /tmp/preline-theme-config.json <themes-folder>/<theme-name>.css && rm /tmp/preline-theme-config.json
11+
```
12+
13+
## Option B
14+
15+
Find and run the script directly:
16+
17+
```bash
18+
node $(find . -path "*/preline/skills/theme-generator/scripts/generate-theme.js" 2>/dev/null | head -1) /tmp/preline-theme-config.json <themes-folder>/<theme-name>.css && rm /tmp/preline-theme-config.json
19+
```
20+
21+
## Option C
22+
23+
If script location is known (e.g., in node_modules):
24+
25+
```bash
26+
node node_modules/preline/skills/theme-generator/scripts/generate-theme.js /tmp/preline-theme-config.json <themes-folder>/<theme-name>.css && rm /tmp/preline-theme-config.json
27+
```
28+
29+
## Script location & features
30+
31+
```
32+
src/assets/vendor/preline/skills/theme-generator/scripts/generate-theme.js
33+
```
34+
35+
Script capabilities:
36+
- Generates complete theme with all tokens (220+)
37+
- Chart tokens with hex values (Apexcharts compatible)
38+
- Map tokens (jsvectormap compatible)
39+
- Accepts hex color input (`primaryColor`)
40+
- Input validation with clear error messages
41+
- Generation timestamp in output
42+
- Font configuration support
43+
44+
See `token-reference.md` for the complete token list.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Step 5: Confirm to user
2+
3+
Tell user the theme was created and show enable snippet:
4+
5+
```css
6+
/* In your main CSS file */
7+
@import "./themes/<theme-name>.css";
8+
```
9+
10+
```html
11+
<!-- On HTML element -->
12+
<html data-theme="theme-<theme-name>">
13+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Step 6: Build the theme system
2+
3+
## Choose palette strategy
4+
- Light: coherent backgrounds + readable foreground + quiet border scale
5+
- Dark: coherent dark surfaces + readable text + borders that separate layers
6+
7+
## Build full primary ramp + states
8+
- `50..950` must feel like one family
9+
- `hover/focus/active/checked` must be incremental and consistent
10+
- `--primary-foreground` must be readable
11+
12+
## Define border scale
13+
- `--border-line-1..8` must be consistent and usable across surfaces
14+
15+
## Define secondary/muted/destructive
16+
- Ensure destructive is clearly distinct from primary
17+
18+
## Define core component groups
19+
- Ensure navbar/sidebar/card/dropdown/select/overlay feel like one system

0 commit comments

Comments
 (0)