preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
236 lines (179 loc) • 10.6 kB
Markdown
# Palette guidance
Use this file for color-direction heuristics only.
Important: references below to custom palettes like `--color-<name>-*` or `--color-<name>-gray-*` describe draft palette exploration and shared-palette design patterns. They do NOT override `docs/final-output-style.md`, which defines how the final `<name>.css` file should be structured.
## Contents
- [Hue reference](#hue-reference-for-workflow-normalization)
- [Mood and palette guidance](#mood-and-palette-guidance)
- [Accessibility and contrast guidelines](#accessibility-and-contrast-guidelines-wcag)
- [Palette types](#palette-types)
- [Custom gray palettes](#custom-gray-palettes-neutral-matching)
## Hue reference (for workflow normalization)
| 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:
- Do not decide from hue alone.
- Compare candidate text colors against the actual generated primary shades used by the theme.
- For light mode, check the generated `primary-600` and `primary-700` states.
- For dark mode, check the generated `primary-400` and `primary-500` states.
- Choose the text color with the stronger worst-case contrast across those states.
- White usually wins for medium or dark primaries; dark gray should only be used when the generated brand states are genuinely light.
- In dark mode, it is valid to shift the primary button one step deeper (for example `500/600` instead of `400/500`) when that keeps white text readable and better matches the design system.
Readable inverse and decorative accents:
- `--foreground-inverse` is not just for neutral inverse surfaces. In Preline it is also used on vivid utility colors such as red/orange badges and chips.
- In dark mode, prefer keeping `--foreground-inverse` white unless contrast checks prove a darker value is still safe across those saturated utility colors.
- Do not let dark mode inherit very deep brand steps for decorative tokens such as `--chart-5` when those tokens are likely to appear in gradient text or accent UI.
- For dark decorative/chart tokens, prefer mid-to-light brand steps that stay clearly visible on dark backgrounds.
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.