Back to skills
SkillHub ClubShip Full StackFull Stack

preline-theme-generator

Generates beautiful, consistent Preline Theme CSS files. Agent interprets user request, runs build script, delivers complete CSS.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
6,269
Hot score
99
Updated
March 20, 2026
Overall rating
C4.5
Composite score
4.5
Best-practice grade
C65.6

Install command

npx @skill-hub/cli install htmlstreamofficial-preline-theme-generator

Repository

htmlstreamofficial/preline

Skill path: skills/theme-generator

Generates beautiful, consistent Preline Theme CSS files. Agent interprets user request, runs build script, delivers complete CSS.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: htmlstreamofficial.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install preline-theme-generator into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/htmlstreamofficial/preline before adding preline-theme-generator to shared team environments
  • Use preline-theme-generator for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: preline-theme-generator
description: Generates beautiful, consistent Preline Theme CSS files. Agent interprets user request, runs build script, delivers complete CSS.
---

# Preline Theme Generator

> **This file is for the AGENT (LLM) only — not end users.**

Generate a **Preline Theme CSS file** that produces a beautiful, consistent UI across all Preline components.

---

## Docs

- `docs/step-1.md` to `docs/step-7.md` (detailed workflow guidance)
- `docs/palette-guidance.md` (mood, hue, neutral mapping, OKLCH rules)
- `docs/validation-checklist.md`
- `docs/token-reference.md`
- `docs/examples.md`

---

## Agent workflow (FOLLOW THIS)

When user requests a theme, the agent MUST:

### Step 1: Interpret user request
Parse the natural language description and determine the config inputs. See `docs/step-1.md` and `docs/palette-guidance.md`.

### Step 2: Detect themes folder
Find where existing themes are stored in the user's project:

```bash
# Look for existing theme.css to find the themes folder
find . -name "theme.css" -path "*/themes/*" -not -path "*/node_modules/*" 2>/dev/null | head -1
```

If no themes folder found: ask user where they want the theme file saved. See `docs/step-2.md`.

### Step 3: Create temp config file (AUTO-RUN, NO PROMPT)
Write a JSON config. This must run silently without user approval.

```bash
echo '{"name":"<theme-name>","hue":<hue>,"style":"<style>","useCustomDarkGray":<true|false>,"tailwindGray":"<gray>"}' > /tmp/preline-theme-config.json
```

### Step 4: Run the generator + cleanup (AUTO-RUN, NO PROMPT)
Run generator and immediately delete temp file in a single command chain. This must run silently without user approval.

Option A (preferred) - use npx if preline is installed:

```bash
npx preline-theme-generator /tmp/preline-theme-config.json <themes-folder>/<theme-name>.css && rm /tmp/preline-theme-config.json
```

Option B - find and run the script directly:

```bash
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
```

Option C - if script location is known (e.g., in node_modules):

```bash
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
```

### Step 5: Confirm to user
Tell user the theme was created and show enable snippet:

```css
/* In your main CSS file */
@import "./themes/<theme-name>.css";
```

```html
<!-- On HTML element -->
<html data-theme="theme-<theme-name>">
```

See `docs/step-5.md` and `docs/examples.md` for response examples.

---

## Generated themes include
- Coherent light + dark modes
- Safe to ship (valid CSS, correct selectors)
- No HTML class changes required (only `data-theme` + optional `.dark`)
- Custom color palettes in `@theme theme-<name> inline { }` block

---

## Example prompts

Simple:
> "Create a sunset theme"

Descriptive:
> "Create a theme that feels like a cozy coffee shop — warm browns, cream backgrounds, inviting and calm"

Specific:
> "Generate a cyberpunk theme with neon cyan accents on dark surfaces"

Brand-focused:
> "Create a theme matching this brand color #2F6BFF — professional, high-clarity SaaS feel"

---

## RULES (ALL MUST BE FOLLOWED)

### Rule 1 — Do not modify the shipped base theme
- Never change `theme.css`. Always generate a separate theme file.

### Rule 2 — Theme file MUST include imports in this exact order
Every generated theme file MUST begin with:

```css
@import "./theme.css";
```

### Rule 3 — Theme scoping block placement
Every theme file MUST include a scoping block after imports:

```css
@theme theme-<name> inline {
  /* Theme scoping - custom palettes only */
}
```

**What goes INSIDE this block:**
- Custom color palettes (soft grays, brand colors) — these create Tailwind utilities
- Example: `--color-<name>-50: oklch(98% 0.003 <hue>);` through `--color-<name>-950`

**What goes OUTSIDE (in selector blocks):**
- All semantic token overrides (`--background`, `--primary`, `--navbar-*`, etc.)
- Tailwind core mappings are owned by the shipped base theme only

See `docs/palette-guidance.md` for palette construction details and examples.

### Rule 4 — No HTML utility class edits required
- Theme must NOT require users to change Tailwind utility classes in HTML.
- Theme activation must work using ONLY:
  - CSS imports
  - `data-theme="theme-<name>"`
- Dark mode may use `.dark` if the project uses it; do not introduce new conventions.

### Rule 5 — Use exact, required theme selectors
Light mode token overrides MUST be under:

```css
:root[data-theme="theme-<name>"],
[data-theme="theme-<name>"] { ... }
```

Dark mode overrides MUST be theme-scoped and use:

```css
[data-theme="theme-<name>"].dark { ... }
```

### Rule 6 — Full-theme mode: comprehensive token coverage is REQUIRED
Because this generator is full-theme mode, output MUST define a comprehensive set of token values so the theme looks complete and intentional.

At minimum, the theme MUST define:

#### 6.1 Global surfaces + text
- `--background`
- `--background-1`
- `--background-2`
- `--background-plain`
- `--foreground`
- `--foreground-inverse`
- `--inverse`

#### 6.2 Borders (full scale)
- `--border`
- `--border-line-inverse`
- `--border-line-1` through `--border-line-8` (coherent scale)

#### 6.3 Primary ramp (full) + primary states
- `--primary-50` through `--primary-950`
- `--primary`
- `--primary-hover`
- `--primary-focus`
- `--primary-active`
- `--primary-checked`
- `--primary-foreground` (must be readable)

#### 6.4 Secondary / muted / destructive (at least)
- `--secondary`
- `--muted`
- `--destructive`
- Include related state/variant tokens if they exist in the base naming system.

#### 6.5 Core component groups for a complete theme feel (prefer explicit values)
Provide explicit values for major component groups so the theme feels cohesive:
- `--navbar-*`
- `--sidebar-*`
- `--card-*`
- `--dropdown-*`
- `--select-*`
- `--overlay-*`
- `--popover-*`
- `--tooltip-*`
- Optionally: `--scrollbar-*`
- Charts/maps tokens ONLY if included safely (see Rule 10)

**Full-theme output MUST NOT:**
- put token definitions inside the `@theme` block
- invent new mappings that force HTML class edits

### Rule 7 — Token naming must match Preline's token system
- Do not invent random token names for Preline components.
- Only introduce new custom tokens if explicitly requested, and keep them isolated and documented in comments.

### Rule 8 — Theme-scoped behavior overrides ONLY
If the theme changes behavior (e.g. retro-sharp radii), it MUST be scoped to the theme only.

**Allowed:**
- Override radius tokens under the theme selector
- Add `@layer utilities` rules scoped to the theme selector

**Not allowed:**
- Global `.rounded { ... }` overrides without theme scoping
- Any behavior overrides that affect non-theme pages

### Rule 9 — Typography tokens are allowed, but font loading is separate by default
If fonts are requested, you MAY set:
- `--font-sans` and/or `--font-serif` and/or `--font-mono`

inside the theme selector.

Do NOT add Google Fonts `@import url(...)` into the theme file unless the user explicitly requests it.
(Font loading typically belongs in the main CSS entry file.)

### Rule 10 — Charts/maps compatibility rules
If adjusting chart/map tokens:
- Prefer safe formats where required by libraries.
- Keep any `*-hex` tokens as valid hex values if the ecosystem expects hex.
- Do not force `oklch(...)` where it may break gradients or third-party rendering.

### Rule 11 — Valid CSS is mandatory
- No missing braces
- No invalid selectors
- No broken comments
- No duplicate conflicting selectors

### Rule 12 — Output discipline
- First code block must contain ONLY the generated theme CSS.
- Optional second code block may contain ONLY the enable snippet.
- No additional commentary unless explicitly requested.

---

## Required file structure (MUST follow order)

1. **Imports** (`@import "./theme.css"`)
2. **Theme scoping block** (`@theme theme-<name> inline { }`) — contains custom color palettes only
3. **Light mode theme selector block** (full semantic token set using `var()` references)
4. **Dark mode theme selector block** (override only what differs, use `var()` for consistency)
5. **Optional theme-scoped `@layer utilities` overrides** (only if requested)

---

## Additional guidance

- Theme construction details: `docs/step-6.md` and `docs/step-7.md`
- Palette guidance: `docs/palette-guidance.md`
- Validation checklist: `docs/validation-checklist.md`
- Token reference: `docs/token-reference.md`

---

## Referenced Files

> The following files are referenced in this skill and included for context.

### docs/step-1.md

```markdown
# Step 1: Interpret user request

Parse the natural language description and determine:
- `name`: theme name in kebab-case
- `hue`: brand color hue (0-360) or `primaryColor` hex
- `style`: `"vibrant"` (default) or `"soft"` (if user says muted/pastel/ash)
- `useCustomDarkGray`: true only if user says "matching dark mode" / "cohesive dark"
- `tailwindGray`: neutral/stone/zinc/slate based on brand warmth

See `palette-guidance.md` for hue ranges, mood mapping, and palette rules.

## Natural language interpretation

Users can describe their theme naturally. Interpret intent such as:

| User says... | LLM understands... |
|--------------|---------------------|
| "warm sunset colors" | primaryHue: orange/amber, mood: warm, neutralFamily: stone |
| "professional and clean" | mood: minimal/serious, contrast: medium, primaryHue: blue/slate |
| "playful candy vibes" | mood: playful, primaryHue: pink/purple, contrast: high |
| "dark hacker aesthetic" | mood: serious, dark-focused, primaryHue: green/cyan |
| "earthy and organic" | primaryHue: green/brown, neutralFamily: stone, mood: calm |
| "bold and energetic" | contrast: high, mood: vibrant, saturated colors |
| "soft and muted" | contrast: low, mood: calm, desaturated colors |
| "90s retro feel" | mood: retro, radiusStyle: retro-sharp, bold colors |

## Internal parameters (derived from context)

- `themeName`: derived from user's name (kebab-case)
- `primaryColor`: hex value if provided, otherwise derived from description
- `primaryHue`: teal / indigo / orange / emerald / rose / amber / cyan / purple / etc.
- `mood`: calm / vibrant / serious / playful / retro / minimal / elegant / bold
- `contrast`: low / medium / high
- `lightNeutralFamily`: gray / neutral / slate / stone / zinc
- `darkNeutralFamily`: gray / neutral / slate / stone / zinc
- `radiusStyle`: default / soft / sharp / retro-sharp
- `includeCharts`: true if user mentions dashboards, analytics, data visualization
- `includeMaps`: true if user mentions maps, geography, location features

## Creative freedom

When user input is vague, the LLM should:
1. Make opinionated, beautiful choices (don't default to boring)
2. Create cohesive color stories (not random combinations)
3. Consider the emotional response the theme should evoke
4. Ensure accessibility (readable contrast, distinguishable states)

```

### docs/step-7.md

```markdown
# Step 7: Dark mode + optional overrides

## Dark mode overrides
- Override only what differs (inherit everything else)
- Ensure contrast and states remain readable

### Dark mode palette decision (CRITICAL)

```
ALWAYS: Create BOTH palettes in @theme inline { }:
  - Brand palette: --color-<name>-*
  - Gray palette: --color-<name>-gray-*

Light mode: ALWAYS uses custom gray palette (--color-<name>-gray-*)

Dark mode:
  IF user requested "matching dark mode" / "cohesive dark" / "consistent colors":
    -> Use custom gray palette: var(--color-<name>-gray-*) for dark mode neutrals

  ELSE (DEFAULT):
    -> Use Tailwind gray for dark mode: var(--color-zinc-*) or var(--color-stone-*)
    -> Choose based on brand warmth (warm brand -> stone, cool brand -> zinc)
```

Example:
- "soft rose theme" -> Light uses custom gray, Dark uses Tailwind gray (zinc/neutral)
- "soft rose theme with matching dark mode" -> Both use custom gray

DEFAULT (no matching dark mode requested):

| Brand Color | Tailwind Gray for Dark Mode |
|-------------|----------------------------|
| Warm (orange, amber, brown) | `stone` or `neutral` |
| Cool (blue, cyan, teal, indigo) | `zinc`, `slate`, or `gray` |
| Neutral (purple, green) | `zinc` or `neutral` |

```css
/* Single-palette: brand only, Tailwind gray for dark mode */
@theme theme-<name> inline {
  --color-<name>-50: oklch(...);
  /* ... brand palette only ... */
  --color-<name>-950: oklch(...);
}

[data-theme="theme-<name>"].dark {
  --background: var(--color-stone-950);  /* Tailwind gray */
  --background-1: var(--color-stone-900);
  /* ... */
}
```

DUAL-PALETTE MODE (when matching dark mode requested):

```css
/* Dual-palette: brand + custom gray */
@theme theme-<name> inline {
  /* Brand palette (vibrant) */
  --color-<name>-50: oklch(97% 0.08 <hue>);
  --color-<name>-100: oklch(94% 0.10 <hue>);
  /* ... */
  --color-<name>-950: oklch(20% 0.06 <hue>);

  /* Gray palette (bell curve chroma - low at dark end for clean dark mode) */
  --color-<name>-gray-50: oklch(98% 0.002 <hue>);
  --color-<name>-gray-100: oklch(95.5% 0.004 <hue>);
  /* ... midtones peak at 0.020 ... */
  --color-<name>-gray-800: oklch(34.6% 0.008 <hue>);  /* Very low */
  --color-<name>-gray-900: oklch(30.6% 0.005 <hue>);  /* Almost neutral */
  --color-<name>-gray-950: oklch(20% 0.003 <hue>);    /* Nearly pure dark */
}

[data-theme="theme-<name>"].dark {
  --background: var(--color-<name>-gray-950);  /* Custom gray */
  --background-1: var(--color-<name>-gray-900);
  --foreground: var(--color-<name>-gray-100);
  /* ... all neutrals use custom gray palette ... */
}
```

Always use CSS variables, never hardcoded oklch in dark mode:

```css
/* Good - readable and maintainable */
--background: var(--color-<name>-gray-900);

/* Bad - impossible to understand */
--background: oklch(21.1% 0.006 285.9);
```

## Optional typography tokens
- Only if requested; place inside the theme selector

## Optional behavior overrides
- Only if requested; must follow Rule 8

```

### docs/palette-guidance.md

```markdown
# Palette guidance

## Hue reference (for step 1)

| Color | Hue Range | Tailwind Gray |
|-------|-----------|---------------|
| Red/Coral | 0-30 | neutral |
| Orange/Amber | 30-60 | stone |
| Yellow/Gold | 60-90 | stone |
| Green/Lime/Avocado | 90-150 | stone |
| Teal/Cyan | 150-200 | zinc |
| Blue/Azure | 200-260 | slate |
| Violet/Purple | 260-300 | zinc |
| Magenta/Pink/Rose | 300-360 | neutral |

## Mood and palette guidance

### Color psychology reference

| Mood | Primary Hues | Neutral Family | Characteristics |
|------|--------------|----------------|-----------------|
| Professional | Blue, Slate, Indigo | gray, slate | Clean, trustworthy, corporate |
| Creative | Purple, Pink, Orange | neutral | Innovative, artistic, expressive |
| Natural | Green, Teal, Brown | stone | Organic, sustainable, calm |
| Energetic | Orange, Yellow, Red | neutral | Bold, active, attention-grabbing |
| Luxurious | Gold, Purple, Black | zinc | Premium, elegant, sophisticated |
| Playful | Pink, Cyan, Yellow | neutral | Fun, youthful, approachable |
| Tech/Cyber | Cyan, Green, Purple | slate, zinc | Modern, digital, futuristic |
| Warm | Orange, Amber, Brown | stone | Cozy, inviting, comfortable |
| Cool | Blue, Teal, Slate | gray, slate | Calm, refreshing, serene |
| Minimal | Gray, White, Black | gray | Simple, focused, uncluttered |

### Building cohesive palettes

1. Pick a primary: the hero color for buttons, links, CTAs
2. Choose matching neutrals: warm primaries -> stone/neutral; cool primaries -> gray/slate
3. Define contrast: bold brands -> high contrast; soft aesthetics -> low contrast
4. Consider dark mode: primary often shifts lighter; neutrals shift to darker family

### Accessibility and contrast guidelines (WCAG)

Ensure themes meet WCAG 2.1 AA contrast requirements:

| Element | Minimum Ratio | OKLCH Rule of Thumb |
|---------|---------------|---------------------|
| Body text on background | 4.5:1 | Lightness difference >= 50% |
| Large text (18pt+) | 3:1 | Lightness difference >= 40% |
| UI components (buttons, inputs) | 3:1 | Lightness difference >= 40% |
| Focus indicators | 3:1 | Use distinct color + lightness |

OKLCH lightness thresholds:

| Context | Light Mode | Dark Mode |
|---------|------------|-----------|
| Background | L >= 95% | L <= 25% |
| Text on light bg | L <= 45% | -- |
| Text on dark bg | -- | L >= 85% |
| Primary button text | Check against primary-500/600 | Check against primary-400 |

Readable primary foreground:
- Most hues (blue, purple, red, orange): use white (L: 100%)
- Light hues (yellow 50-110 degrees, light cyan 160-195): use dark gray (L <= 20%)

Quick check: If |L_text - L_background| >= 50%, contrast is likely sufficient for body text.

## Palette types

When generating custom themes, there are TWO distinct types of custom color palettes:

| Palette Type | Example Variable | Purpose | Chroma Level |
|--------------|------------------|---------|--------------|
| Brand palette | `--color-<name>-*` | Primary/accent colors, buttons, links, CTAs | Vibrant (high chroma) by default |
| Gray palette | `--color-<name>-gray-*` | Backgrounds, surfaces, borders, dark mode neutrals | Soft (low chroma 0.002-0.035) |

### When to create each palette

ALWAYS create TWO palettes in `@theme inline { }`:
1. Brand palette (`--color-<name>-*`) for primary/accent colors
2. Gray palette (`--color-<name>-gray-*`) for backgrounds, surfaces, borders

Both palettes are used in light mode for a cohesive feel.

Dark mode behavior depends on user request:

| User Request | Light Mode Uses | Dark Mode Uses |
|--------------|-----------------|----------------|
| "soft rose theme" (default) | Custom gray (`--color-<name>-gray-*`) | Tailwind gray (`zinc`, `stone`, etc.) |
| "soft rose theme with matching dark mode" | Custom gray (`--color-<name>-gray-*`) | Custom gray (`--color-<name>-gray-*`) |

Trigger phrases for custom dark mode palette:
- "matching dark mode"
- "cohesive dark mode"
- "consistent dark colors"
- "unified light and dark"
- "matching neutrals"

If none of these phrases appear -> light mode uses custom gray, dark mode uses Tailwind grays.

### Brand color style (vibrant vs soft)

Brand palettes are vibrant by default (like Tailwind's blue, green, orange). Only reduce chroma if user explicitly requests:

| User Says | Brand Palette Style | Chroma Level |
|-----------|---------------------|--------------|
| Default (no modifier) | Vibrant, follow Tailwind color saturation | 0.10-0.20+ |
| "soft", "muted", "ash", "pastel", "dusty", "desaturated" | Soft, reduced chroma like lavender/khaki | 0.02-0.05 |

Example comparison:

```css
/* Vibrant brand (default) */
--color-<name>-500: oklch(55% 0.14 <hue>);

/* Soft/muted brand (if requested) */
--color-<name>-500: oklch(55% 0.04 <hue>);
```

## Custom gray palettes (neutral matching)

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.

### Palette characteristics

| Palette Type | Hue Range | Chroma Range | Use With |
|--------------|-----------|--------------|----------|
| Warm neutrals | 60-100 degrees (yellow/brown) | 0.002-0.035 | Orange, amber, brown, gold primaries |
| Cool neutrals | 200-260 degrees (blue/slate) | 0.002-0.030 | Blue, cyan, teal, indigo primaries |
| Rose neutrals | 330-360 degrees (pink) | 0.002-0.040 | Pink, rose, magenta primaries |
| Green neutrals | 120-160 degrees (sage) | 0.002-0.030 | Green, emerald, teal primaries |

### OKLCH pattern for gray palettes (bell curve chroma)

Gray palettes use a bell curve chroma pattern: very low at extremes (light AND dark), peaking at mid-tones. This ensures:
- Light mode backgrounds (50-200) look clean with subtle tint
- Dark mode backgrounds (800-950) look sophisticated, not muddy

```css
--color-<name>-gray-50:  oklch(98%   0.002 <hue>);  /* Almost neutral */
--color-<name>-gray-100: oklch(95.5% 0.004 <hue>);
--color-<name>-gray-200: oklch(89.7% 0.008 <hue>);
--color-<name>-gray-300: oklch(82.7% 0.012 <hue>);  /* Building up */
--color-<name>-gray-400: oklch(73%   0.018 <hue>);  /* Approaching peak */
--color-<name>-gray-500: oklch(62.5% 0.020 <hue>);  /* PEAK chroma */
--color-<name>-gray-600: oklch(52.8% 0.016 <hue>);  /* Starting to fade */
--color-<name>-gray-700: oklch(41.4% 0.012 <hue>);  /* Fading */
--color-<name>-gray-800: oklch(34.6% 0.008 <hue>);  /* Very low */
--color-<name>-gray-900: oklch(30.6% 0.005 <hue>);  /* Almost neutral */
--color-<name>-gray-950: oklch(20.1% 0.003 <hue>);  /* Nearly pure dark */
```

Critical: Dark mode backgrounds (800-950) need very low chroma:

| Shade | Chroma | Why |
|-------|--------|-----|
| 950 | 0.003 | Dark backgrounds must be nearly neutral |
| 900 | 0.005 | Avoids muddy/colored appearance |
| 800 | 0.008 | Just a hint of warmth/coolness |
| 500 | 0.020 | Peak, midtones carry color identity |
| 50 | 0.002 | Light backgrounds stay clean |

Key principles:
- Chroma peaks at mid-tones (400-600), very low at BOTH extremes
- Dark end (800-950) must have lower chroma than light end for clean dark mode
- Hue stays consistent across the scale (±5 degrees variation is acceptable)
- Lightness follows standard 50-950 scale

### Placement of custom palettes

Custom color palettes MUST be defined in the `@theme theme-<name> inline { }` block:

```css
@theme theme-<name> inline {
  /* Custom neutral palette for this theme */
  --color-<name>-50: oklch(98% 0.003 88);
  --color-<name>-100: oklch(95.5% 0.005 88);
  /* ... full 50-950 scale ... */
}
```

Then reference these in the theme selector blocks using `var()`:

```css
[data-theme="theme-<name>"] {
  --background: var(--color-<name>-50);
  --background-1: var(--color-<name>-100);
  /* ... */
}

[data-theme="theme-<name>"].dark {
  --background: var(--color-<name>-950);
  --background-1: var(--color-<name>-900);
  /* ... */
}
```

### When to create custom palettes

| Scenario | Action |
|----------|--------|
| Primary is warm (orange/amber/brown) | Create warm neutral palette OR use `stone` |
| Primary is cool (blue/cyan/indigo) | Use `slate` or `gray` (usually sufficient) |
| Primary is unique (gold, khaki, etc.) | Create matching custom neutral palette |
| User explicitly requests | Always create to match their vision |
| Theme needs distinctive personality | Create for cohesion |

### Using custom palettes in both modes

Soft gray palettes work seamlessly across light and dark modes:
- Light mode: use 50-300 for backgrounds, 600-950 for text
- Dark mode: use 800-950 for backgrounds, 50-300 for text

This creates consistent warmth/coolness across modes while maintaining readability.

```

### docs/validation-checklist.md

```markdown
# Validation checklist (must self-check before returning)

## Structure
- [ ] Imports present and ordered (`tailwindcss`, then `./theme.css`)
- [ ] `@theme theme-<name> inline { }` block present with custom palettes
- [ ] Theme key consistent: `theme-<name>` everywhere
- [ ] Light selector uses `:root[data-theme=...]`, `[data-theme=...]`
- [ ] Dark selector is `[data-theme="theme-<name>"].dark`
- [ ] Semantic token definitions are in selector blocks (NOT inside @theme block)
- [ ] No requirement to change HTML utility classes (only `data-theme`, optional `.dark`)

## Palettes (always create both)
- [ ] Brand palette created: `--color-<name>-*` (vibrant or soft based on request)
- [ ] Gray palette created: `--color-<name>-gray-*` (bell curve chroma, low at dark end)
- [ ] Light mode uses custom gray palette for backgrounds/surfaces/borders

## Dark mode palette choice (critical)
- [ ] Did user say "matching dark mode" / "cohesive dark" / "consistent colors"?
  - YES -> Dark mode uses `var(--color-<name>-gray-*)` (custom gray)
  - NO -> Dark mode uses `var(--color-zinc-*)` or `var(--color-stone-*)` (Tailwind gray)

## Brand color style
- [ ] Brand palette is VIBRANT (high chroma) by default
- [ ] Brand palette is SOFT (low chroma) only if user said "soft/muted/ash/pastel/dusty"

## Token coverage
- [ ] Full coverage present (Rule 6.1-6.5)
- [ ] CSS syntax valid (balanced braces, no broken comments)
- [ ] If behavior overrides exist, they are theme-scoped only (Rule 8)
- [ ] If charts/maps changed, `*-hex` tokens remain hex (Rule 10)
- [ ] Output discipline followed (Rule 12)

## Dark mode consistency
- [ ] Dark mode uses CSS variables (`var(--color-*)`) not hardcoded oklch values
- [ ] Dark mode uses ONE consistent grayscale family (not mixed)
- [ ] All custom palettes are defined in `@theme inline { }` and referenced via `var()`
- [ ] Gray palette uses bell curve chroma (low at extremes, peak at midtones)
- [ ] Gray palette dark end (800-950) has very low chroma (<=0.008) for clean dark backgrounds

```

### docs/token-reference.md

```markdown
---
name: preline-theme-generator-token-reference
description: Complete reference of all Preline theme tokens, their purposes, naming patterns, and default values for both light and dark modes.
---

# Preline Theme Token Reference

This document provides the complete reference for all semantic tokens available in Preline themes. Use this when generating themes to ensure comprehensive coverage.

---

## Overview

Preline themes use CSS custom properties (variables) as semantic tokens. These tokens are:
- Defined in theme files under `:root` (light) and `.dark` selectors
- Mapped to Tailwind utilities via `@theme inline` in the base theme
- Consumed by Preline components for consistent styling

Important: Generated themes override token values only. Never redefine the Tailwind mappings.

---

## Core token groups

| Group | Tokens | Used for |
|-------|--------|----------|
| Brand | `--primary`, `--primary-*` | Primary actions (buttons, links) |
| Secondary | `--secondary`, `--secondary-*` | Secondary emphasis |
| Muted | `--muted`, `--muted-*` | Muted/subdued elements |
| Destructive | `--destructive`, `--destructive-*` | Destructive/danger actions |
| Background | `--background`, `--background-*` | App surfaces |
| Foreground | `--foreground`, `--foreground-*` | Default typography colors |
| Inverse | `--inverse` | Inverted color scheme |
| Border | `--border` | Default border color |
| Border line | `--border-line-*` | Border colors for different shades |
| Layer | `--layer`, `--layer-*` | Layered elements (e.g. white buttons on white panel) |
| Surface | `--surface`, `--surface-*` | Elevated surfaces |
| Navbar | `--navbar`, `--navbar-*` | Navigation bar (background, border, divider) |
| Navbar nav | `--navbar-nav-*` | Navigation bar items (foreground, hover, active) |
| Sidebar | `--sidebar`, `--sidebar-*` | Sidebar (background, border, divider) |
| Sidebar nav | `--sidebar-nav-*` | Sidebar items (foreground, hover, active) |
| Card | `--card`, `--card-*` | Card component (border, divider, header, footer) |
| Dropdown | `--dropdown`, `--dropdown-*` | Dropdown menus (items, hover, active) |
| Select | `--select`, `--select-*` | Select component (items, hover, active) |
| Overlay | `--overlay`, `--overlay-*` | Modal/overlay backgrounds |
| Popover | `--popover`, `--popover-*` | Popover component |
| Tooltip | `--tooltip`, `--tooltip-*` | Tooltip component |
| Table | `--table-line` | Table borders |
| Switch | `--switch` | Switch/toggle component |
| Footer | `--footer`, `--footer-*` | Footer component |
| Scrollbar | `--scrollbar-*` | Custom scrollbar (track, thumb) |
| Chart | `--chart-*` | Chart colors (Apexcharts) |
| Map | `--map-colors-*` | Map colors (jsvectormap) |

---

## Component preset patterns

Preline uses numbered variants for component groups to offer different ready-made looks:

| Pattern | Meaning |
|---------|---------|
| `--navbar-*` | Default navbar preset (base style) |
| `--navbar-1-*` | Variant 1 preset (typically subtle/tinted surface) |
| `--navbar-2-*` | Variant 2 preset (typically stronger tint or different border) |
| `*` (no suffix) | Background/surface color of the component |
| `*-line` | Border color for the component container |
| `*-divider` | Divider lines inside the component |
| `*-nav-*` | Nav item colors (foreground, hover, focus, active) |
| `*-nav-list-divider` | Divider used in nav dropdown/list layouts |
| `*-inverse` | Inverted/contrast variant |

This pattern applies to: navbar, sidebar, and similar navigation components.

---

## Full token reference by category

### 1. Global surfaces + text

```css
/* Background tokens */
--background          /* Main app background */
--background-1        /* Secondary background (slightly tinted) */
--background-2        /* Tertiary background (more tinted) */
--background-plain    /* Plain white/black background */

/* Foreground tokens */
--foreground          /* Default text color */
--foreground-inverse  /* Inverted text color (for dark backgrounds) */

/* Inverse */
--inverse             /* Inverted surface color */
```

### 2. Borders (full scale)

```css
--border              /* Default border color */
--border-line-inverse /* Inverse border color */
--border-line-1       /* Lightest border */
--border-line-2
--border-line-3
--border-line-4
--border-line-5
--border-line-6
--border-line-7
--border-line-8       /* Darkest border */
```

### 3. Primary ramp + states

```css
/* Full color ramp (11 shades) */
--primary-50
--primary-100
--primary-200
--primary-300
--primary-400
--primary-500
--primary-600
--primary-700
--primary-800
--primary-900
--primary-950

/* State tokens */
--primary             /* Base primary color */
--primary-line        /* Primary border (usually transparent) */
--primary-foreground  /* Text on primary background */
--primary-hover       /* Hover state */
--primary-focus       /* Focus state */
--primary-active      /* Active/pressed state */
--primary-checked     /* Checked state (checkboxes, radios) */
```

### 4. Secondary

```css
--secondary            /* Base secondary color */
--secondary-line       /* Secondary border */
--secondary-foreground /* Text on secondary background */
--secondary-hover      /* Hover state */
--secondary-focus      /* Focus state */
--secondary-active     /* Active state */
```

### 5. Layer

```css
--layer               /* Layer background */
--layer-line          /* Layer border */
--layer-foreground    /* Text on layer */
--layer-hover         /* Hover state */
--layer-focus         /* Focus state */
--layer-active        /* Active state */
```

### 6. Surface

```css
--surface             /* Base surface */
--surface-1           /* Surface variant 1 */
--surface-2           /* Surface variant 2 */
--surface-3           /* Surface variant 3 */
--surface-4           /* Surface variant 4 */
--surface-5           /* Surface variant 5 */
--surface-line        /* Surface border */
--surface-foreground  /* Text on surface */
--surface-hover       /* Hover state */
--surface-focus       /* Focus state */
--surface-active      /* Active state */
```

### 7. Muted

```css
--muted               /* Muted background */
--muted-foreground    /* Muted text (lightest) */
--muted-foreground-1  /* Muted text variant 1 */
--muted-foreground-2  /* Muted text variant 2 (darkest) */
--muted-hover         /* Hover state */
--muted-focus         /* Focus state */
--muted-active        /* Active state */
```

### 8. Destructive

```css
--destructive            /* Destructive/danger color */
--destructive-foreground /* Text on destructive background */
--destructive-hover      /* Hover state */
--destructive-focus      /* Focus state */
```

### 9. Navbar (base + 2 variants)

```css
/* Base navbar */
--navbar                /* Navbar background */
--navbar-line           /* Navbar border */
--navbar-divider        /* Navbar divider */
--navbar-nav-foreground /* Nav item text */
--navbar-nav-hover      /* Nav item hover */
--navbar-nav-focus      /* Nav item focus */
--navbar-nav-active     /* Nav item active */
--navbar-nav-list-divider /* Nav list divider */
--navbar-inverse        /* Inverse navbar background */

/* Navbar variant 1 (subtle/tinted) */
--navbar-1
--navbar-1-line
--navbar-1-divider
--navbar-1-nav-foreground
--navbar-1-nav-hover
--navbar-1-nav-focus
--navbar-1-nav-active
--navbar-1-nav-list-divider

/* Navbar variant 2 (stronger tint) */
--navbar-2
--navbar-2-line
--navbar-2-divider
--navbar-2-nav-foreground
--navbar-2-nav-hover
--navbar-2-nav-focus
--navbar-2-nav-active
--navbar-2-nav-list-divider
```

### 10. Sidebar (base + 2 variants)

```css
/* Base sidebar */
--sidebar
--sidebar-line
--sidebar-divider
--sidebar-nav-foreground
--sidebar-nav-hover
--sidebar-nav-focus
--sidebar-nav-active
--sidebar-nav-list-divider
--sidebar-inverse

/* Sidebar variant 1 */
--sidebar-1
--sidebar-1-line
--sidebar-1-divider
--sidebar-1-nav-foreground
--sidebar-1-nav-hover
--sidebar-1-nav-focus
--sidebar-1-nav-active
--sidebar-1-nav-list-divider

/* Sidebar variant 2 */
--sidebar-2
--sidebar-2-line
--sidebar-2-divider
--sidebar-2-nav-foreground
--sidebar-2-nav-hover
--sidebar-2-nav-focus
--sidebar-2-nav-active
--sidebar-2-nav-list-divider
```

### 11. Card

```css
--card                /* Card background */
--card-line           /* Card border */
--card-divider        /* Card divider */
--card-header         /* Card header background */
--card-footer         /* Card footer background */
--card-inverse        /* Inverse card background */
```

### 12. Dropdown

```css
--dropdown                /* Dropdown background */
--dropdown-1              /* Dropdown variant background */
--dropdown-line           /* Dropdown border */
--dropdown-divider        /* Dropdown divider */
--dropdown-header         /* Dropdown header background */
--dropdown-footer         /* Dropdown footer background */
--dropdown-item-foreground /* Item text */
--dropdown-item-hover     /* Item hover */
--dropdown-item-focus     /* Item focus */
--dropdown-item-active    /* Item active */
--dropdown-inverse        /* Inverse dropdown */
```

### 13. Select

```css
--select                /* Select background */
--select-1              /* Select variant background */
--select-line           /* Select border */
--select-item-foreground /* Item text */
--select-item-hover     /* Item hover */
--select-item-focus     /* Item focus */
--select-item-active    /* Item active */
--select-inverse        /* Inverse select */
```

### 14. Overlay

```css
--overlay             /* Overlay/modal background */
--overlay-line        /* Overlay border */
--overlay-divider     /* Overlay divider */
--overlay-header      /* Overlay header background */
--overlay-footer      /* Overlay footer background */
--overlay-inverse     /* Inverse overlay */
```

### 15. Popover, tooltip, table, switch

```css
/* Popover */
--popover             /* Popover background */
--popover-line        /* Popover border */

/* Tooltip */
--tooltip             /* Tooltip background */
--tooltip-foreground  /* Tooltip text */
--tooltip-line        /* Tooltip border */

/* Table */
--table-line          /* Table border */

/* Switch */
--switch              /* Switch knob color */
```

### 16. Footer

```css
--footer              /* Footer background */
--footer-line         /* Footer border */
--footer-inverse      /* Inverse footer */
```

### 17. Scrollbar

```css
--scrollbar-track         /* Scrollbar track */
--scrollbar-thumb         /* Scrollbar thumb */
--scrollbar-track-inverse /* Inverse track */
--scrollbar-thumb-inverse /* Inverse thumb */
```

### 18. Charts (Apexcharts)

Important: Apexcharts does NOT support oklch color format. Keep `-hex` tokens as valid hex values.

```css
/* Primary chart color */
--chart-primary
--chart-colors-primary
--chart-colors-primary-inverse
--chart-colors-primary-hex          /* Must be hex */
--chart-colors-primary-hex-inverse  /* Must be hex */

/* Chart palette (1-10) */
--chart-1 through --chart-10
--chart-colors-chart-1 through --chart-colors-chart-10
--chart-colors-chart-1-inverse through --chart-colors-chart-10-inverse
--chart-colors-chart-1-hex through --chart-colors-chart-10-hex           /* Must be hex */
--chart-colors-chart-1-hex-inverse through --chart-colors-chart-10-hex-inverse /* Must be hex */

/* Chart backgrounds */
--chart-colors-background
--chart-colors-background-inverse
--chart-colors-chart-inverse

/* Chart foregrounds */
--chart-colors-foreground
--chart-colors-foreground-inverse

/* Chart labels */
--chart-colors-labels
--chart-colors-labels-inverse
--chart-colors-xaxis-labels
--chart-colors-xaxis-labels-inverse
--chart-colors-yaxis-labels
--chart-colors-yaxis-labels-inverse

/* Chart grid */
--chart-colors-grid-border
--chart-colors-grid-border-inverse

/* Chart special */
--chart-colors-bar-ranges
--chart-colors-bar-ranges-inverse
--chart-colors-candlestick-upward
--chart-colors-candlestick-upward-inverse
--chart-colors-candlestick-downward
--chart-colors-candlestick-downward-inverse
```

### 19. Maps (jsvectormap)

```css
--map-colors-primary
--map-colors-primary-inverse
--map-colors-default
--map-colors-default-inverse
--map-colors-highlight
--map-colors-highlight-inverse
--map-colors-border
--map-colors-border-inverse
```

---

## Custom color palettes

The base theme defines custom color palettes that themes can use:

### Khaki (earthy/warm neutral)

```css
--color-khaki-50 through --color-khaki-950
```

### Lavender (soft purple/mauve neutral)

```css
--color-lavender-50 through --color-lavender-950
```

These are useful for creating warm or soft-toned themes instead of using standard gray/neutral.

---

## Default values reference

### Light mode defaults (:root)

```css
:root {
  /* Backgrounds */
  --background: var(--color-white);
  --background-1: var(--color-gray-50);
  --background-2: var(--color-gray-100);
  --background-plain: var(--color-white);

  /* Foregrounds */
  --foreground: var(--color-gray-800);
  --foreground-inverse: var(--color-white);
  --inverse: var(--color-gray-800);

  /* Borders */
  --border: var(--color-gray-200);
  --border-line-inverse: var(--color-white);
  --border-line-1: var(--color-gray-100);
  --border-line-2: var(--color-gray-200);
  --border-line-3: var(--color-gray-300);
  --border-line-4: var(--color-gray-400);
  --border-line-5: var(--color-gray-500);
  --border-line-6: var(--color-gray-600);
  --border-line-7: var(--color-gray-700);
  --border-line-8: var(--color-gray-800);

  /* Primary (blue by default) */
  --primary-50: var(--color-blue-50);
  --primary-100: var(--color-blue-100);
  --primary-200: var(--color-blue-200);
  --primary-300: var(--color-blue-300);
  --primary-400: var(--color-blue-400);
  --primary-500: var(--color-blue-500);
  --primary-600: var(--color-blue-600);
  --primary-700: var(--color-blue-700);
  --primary-800: var(--color-blue-800);
  --primary-900: var(--color-blue-900);
  --primary-950: var(--color-blue-950);

  --primary: var(--color-primary-600);
  --primary-line: transparent;
  --primary-foreground: var(--color-white);
  --primary-hover: var(--color-primary-700);
  --primary-focus: var(--color-primary-700);
  --primary-active: var(--color-primary-700);
  --primary-checked: var(--color-primary-600);

  /* Secondary */
  --secondary: var(--color-gray-900);
  --secondary-line: transparent;
  --secondary-foreground: var(--color-white);
  --secondary-hover: var(--color-gray-800);
  --secondary-focus: var(--color-gray-800);
  --secondary-active: var(--color-gray-800);

  /* Layer */
  --layer: var(--color-white);
  --layer-line: var(--color-gray-200);
  --layer-foreground: var(--color-gray-800);
  --layer-hover: var(--color-gray-50);
  --layer-focus: var(--color-gray-50);
  --layer-active: var(--color-gray-50);

  /* Surface */
  --surface: var(--color-gray-100);
  --surface-1: var(--color-gray-200);
  --surface-2: var(--color-gray-300);
  --surface-3: var(--color-gray-400);
  --surface-4: var(--color-gray-500);
  --surface-5: var(--color-gray-600);
  --surface-line: transparent;
  --surface-foreground: var(--color-gray-800);
  --surface-hover: var(--color-gray-200);
  --surface-focus: var(--color-gray-200);
  --surface-active: var(--color-gray-200);

  /* Muted */
  --muted: var(--color-gray-50);
  --muted-foreground: var(--color-gray-400);
  --muted-foreground-1: var(--color-gray-500);
  --muted-foreground-2: var(--color-gray-600);
  --muted-hover: var(--color-gray-100);
  --muted-focus: var(--color-gray-100);
  --muted-active: var(--color-gray-100);

  /* Destructive */
  --destructive: var(--color-red-500);
  --destructive-foreground: var(--color-white);
  --destructive-hover: var(--color-red-600);
  --destructive-focus: var(--color-red-600);

  /* Navbar (base) */
  --navbar: var(--color-white);
  --navbar-line: var(--color-gray-200);
  --navbar-divider: var(--color-gray-200);
  --navbar-nav-foreground: var(--color-gray-800);
  --navbar-nav-hover: var(--color-gray-100);
  --navbar-nav-focus: var(--color-gray-100);
  --navbar-nav-active: var(--color-gray-100);
  --navbar-nav-list-divider: var(--color-gray-200);
  --navbar-inverse: var(--color-primary-950);

  /* Navbar-1 */
  --navbar-1: var(--color-gray-50);
  --navbar-1-line: var(--color-gray-200);
  --navbar-1-divider: var(--color-gray-200);
  --navbar-1-nav-foreground: var(--color-gray-800);
  --navbar-1-nav-hover: var(--color-gray-200);
  --navbar-1-nav-focus: var(--color-gray-200);
  --navbar-1-nav-active: var(--color-gray-200);
  --navbar-1-nav-list-divider: var(--color-gray-200);

  /* Navbar-2 */
  --navbar-2: var(--color-gray-100);
  --navbar-2-line: transparent;
  --navbar-2-divider: var(--color-gray-300);
  --navbar-2-nav-foreground: var(--color-gray-800);
  --navbar-2-nav-hover: var(--color-gray-200);
  --navbar-2-nav-focus: var(--color-gray-200);
  --navbar-2-nav-active: var(--color-gray-200);
  --navbar-2-nav-list-divider: var(--color-gray-200);

  /* Sidebar follows same pattern as navbar */
  /* Card, Dropdown, Select, Overlay, Popover, Tooltip, Table, Switch, Footer, Scrollbar */
  /* See theme.css for complete default values */
}
```

### Dark mode defaults (.dark)

```css
.dark {
  /* Backgrounds */
  --background: var(--color-neutral-800);
  --background-1: var(--color-neutral-900);
  --background-2: var(--color-neutral-900);
  --foreground: var(--color-neutral-200);
  --inverse: var(--color-neutral-950);

  /* Borders */
  --border: var(--color-neutral-700);
  --border-line-1: var(--color-neutral-800);
  --border-line-2: var(--color-neutral-700);
  --border-line-3: var(--color-neutral-600);
  --border-line-4: var(--color-neutral-500);
  --border-line-5: var(--color-neutral-400);
  --border-line-6: var(--color-neutral-300);
  --border-line-7: var(--color-neutral-200);
  --border-line-8: var(--color-neutral-100);

  /* Primary (shifts to 500 in dark mode) */
  --primary: var(--color-primary-500);
  --primary-line: transparent;
  --primary-foreground: var(--color-white);
  --primary-hover: var(--color-primary-600);
  --primary-focus: var(--color-primary-600);
  --primary-active: var(--color-primary-600);
  --primary-checked: var(--color-primary-500);

  /* Secondary */
  --secondary: var(--color-white);
  --secondary-line: transparent;
  --secondary-foreground: var(--color-neutral-800);
  --secondary-hover: var(--color-neutral-100);
  --secondary-focus: var(--color-neutral-100);
  --secondary-active: var(--color-neutral-100);

  /* Layer */
  --layer: var(--color-neutral-800);
  --layer-line: var(--color-neutral-700);
  --layer-foreground: var(--color-white);
  --layer-hover: var(--color-neutral-700);
  --layer-focus: var(--color-neutral-700);
  --layer-active: var(--color-neutral-700);

  /* Surface */
  --surface: var(--color-neutral-700);
  --surface-1: var(--color-neutral-600);
  --surface-2: var(--color-neutral-500);
  --surface-3: var(--color-neutral-600);
  --surface-4: var(--color-neutral-500);
  --surface-5: var(--color-neutral-400);
  --surface-line: transparent;
  --surface-foreground: var(--color-neutral-200);
  --surface-hover: var(--color-neutral-600);
  --surface-focus: var(--color-neutral-600);
  --surface-active: var(--color-neutral-600);

  /* Muted */
  --muted: var(--color-neutral-800);
  --muted-foreground: var(--color-neutral-500);
  --muted-foreground-1: var(--color-neutral-400);
  --muted-foreground-2: var(--color-neutral-300);
  --muted-hover: var(--color-neutral-700);
  --muted-focus: var(--color-neutral-700);
  --muted-active: var(--color-neutral-700);

  /* Destructive (same red, readable in dark) */
  --destructive: var(--color-red-500);
  --destructive-foreground: var(--color-white);
  --destructive-hover: var(--color-red-600);
  --destructive-focus: var(--color-red-600);

  /* Components shift to neutral-800/900 backgrounds */
  /* Nav items shift to neutral-700 hover states */
  /* See theme.css for complete dark mode values */
}
```

---

## Theme-scoped overrides

When creating a theme, override values under theme-scoped selectors:

```css
/* Light mode */
:root[data-theme="theme-<name>"],
[data-theme="theme-<name>"] {
  /* Override tokens here */
}

/* Dark mode */
[data-theme="theme-<name>"].dark {
  /* Override dark-specific tokens here */
}
```

---

## Color format guidelines

- For most tokens: use Tailwind color variables `var(--color-<color>-<shade>)`
- For oklch colors: acceptable for standard tokens
- For chart `-hex` tokens: MUST use valid hex values (for example, `#2563eb`)
- For custom colors: can use hex, rgb, hsl, or oklch

---

## Quick token count summary

| Category | Token Count |
|----------|-------------|
| Global surfaces + text | 7 |
| Borders | 10 |
| Primary (ramp + states) | 18 |
| Secondary | 6 |
| Layer | 6 |
| Surface | 11 |
| Muted | 7 |
| Destructive | 4 |
| Navbar (3 variants) | 27 |
| Sidebar (3 variants) | 27 |
| Card | 6 |
| Dropdown | 11 |
| Select | 8 |
| Overlay | 6 |
| Popover | 2 |
| Tooltip | 3 |
| Table, Switch, Footer | 5 |
| Scrollbar | 4 |
| Charts | ~50+ |
| Maps | 8 |
| Total | ~220+ tokens

```

### docs/step-2.md

```markdown
# Step 2: Detect themes folder

Find where existing themes are stored in the user's project:

```bash
# Look for existing theme.css to find the themes folder
find . -name "theme.css" -path "*/themes/*" -not -path "*/node_modules/*" 2>/dev/null | head -1
```

Common locations to check:
- `src/assets/css/themes/`
- `src/css/themes/`
- `src/styles/themes/`
- `assets/css/themes/`
- `styles/themes/`

If no themes folder found: ask user where they want the theme file saved.

```

### docs/step-5.md

```markdown
# Step 5: Confirm to user

Tell user the theme was created and show enable snippet:

```css
/* In your main CSS file */
@import "./themes/<theme-name>.css";
```

```html
<!-- On HTML element -->
<html data-theme="theme-<theme-name>">
```

```

### docs/step-6.md

```markdown
# Step 6: Build the theme system

## Choose palette strategy
- Light: coherent backgrounds + readable foreground + quiet border scale
- Dark: coherent dark surfaces + readable text + borders that separate layers

## Build full primary ramp + states
- `50..950` must feel like one family
- `hover/focus/active/checked` must be incremental and consistent
- `--primary-foreground` must be readable

## Define border scale
- `--border-line-1..8` must be consistent and usable across surfaces

## Define secondary/muted/destructive
- Ensure destructive is clearly distinct from primary

## Define core component groups
- Ensure navbar/sidebar/card/dropdown/select/overlay feel like one system

```

preline-theme-generator | SkillHub