# Theme extraction

Inspect the source site's CSS for colors (ranked hex codes from inline
styles + `<style>` blocks) and fonts (ranked font families). This file
describes the deterministic algorithm to derive a daisyUI theme from
that input.

## Step 1: classify each color

For each hex color from `css_colors`, convert to HSL. Then bucket:

| Saturation | Lightness | Bucket |
|---|---|---|
| `< 0.10` | any | `neutral` (gray) |
| `≥ 0.10` | `< 0.30` | `dark-vivid` |
| `≥ 0.10` | `0.30 - 0.70` | `vivid` |
| `≥ 0.10` | `> 0.70` | `light-vivid` |

## Step 2: assign semantic roles

- **primary** — most frequent `vivid` color (highest saturation × frequency)
- **secondary** — second-most-frequent `vivid` color with hue Δ ≥ 60° from primary
- **accent** — third-most-frequent `vivid` color (or a complementary hue if none)
- **neutral** — most frequent `neutral` color
- **base-100** — lightest frequent `neutral` color (default: white if none)
- **base-200/300** — auto-derived from base-100 by lowering lightness 3% / 6%

If no vivid colors exist in the source, fall back to daisyUI defaults
(blue 240 hue) and note this in your rebuild summary.

## Step 3: convert hex to OKLCH

Use the OKLCH conversion. Ruby snippet (already available via the
`color` gem if bundled, otherwise compute):

```ruby
# Pseudocode — actual conversion uses CSS Color 4 spec
def hex_to_oklch(hex)
  rgb = parse_hex(hex)
  linear = rgb.map { |c| gamma_decode(c / 255.0) }
  # ... linear → XYZ → OKLab → OKLCH
  "#{lightness.round(0)}% #{chroma.round(3)} #{hue.round(0)}"
end
```

For OKLCH values, hue (h) is in degrees 0-360, lightness (L) is 0-100%,
chroma (C) is typically 0-0.4.

## Step 4: derive `*-content` colors

For each semantic color, the `*-content` color must contrast ≥4.5:1
(WCAG AA). Quick heuristic:
- If the parent color has lightness > 60% in OKLCH, content is dark
  (use `oklch(20% 0.05 <same-hue>)`)
- Otherwise content is light (`oklch(98% 0.01 <same-hue>)`)

## Step 5: assemble theme CSS

Wrap in `[data-theme="custom"] { ... }` block. Pass to `set_theme` tool.
The platform's `Website::Theme::ConfigNormalizer` may auto-wrap; check
the response.

If base-100 is dark (sanity check below), add `color-scheme: dark;`
to the block — platform and browser UI detect darkness from it.

## Sanity check

Before calling `set_theme`:
- primary and secondary should be visually distinguishable
- base-100 should be a light color (for a light theme) OR a very dark color (for dark)
- accent should pop against base-100
