ODS ships 35 named themes that any ODS app can use via theme.base in the spec. Each theme defines a complete color palette, design tokens, optional typography, and both light and dark mode variants. See ADR-0002 for the design rationale.
{
"theme": {
"base": "nord",
"mode": "system"
}
}
To customize on top of a base theme, add overrides:
{
"theme": {
"base": "abyss",
"overrides": {
"primary": "#5B21B6",
"fontSans": "Inter"
}
}
}
| Theme | Native Scheme | Tags | Description |
|---|---|---|---|
| Indigo (default) | light | minimal, default | Deep indigo primary with hot pink secondary and teal accent on pure white bases |
| Slate | dark | minimal, default | Indigo primary with blue-tinted slate bases |
| Cupcake | light | cute, playful | Soft pinks and pastels |
| Bumblebee | light | warm, yellow | Bold yellow and amber tones |
| Emerald | light | green, nature | Rich green primary |
| Corporate | light | professional, clean | Muted blue, professional look |
| Synthwave | dark | retro, neon, 80s | Neon pink on dark purple |
| Retro | light | vintage, warm | Warm earth tones with a vintage feel |
| Cyberpunk | light | futuristic, bold | Vivid yellow and magenta |
| Valentine | light | pink, romantic | Pink and rose tones |
| Halloween | dark | spooky, orange | Orange and black |
| Garden | light | floral, nature | Soft greens and pinks |
| Forest | dark | green, nature | Deep greens on dark background |
| Aqua | dark | ocean, blue | Ocean blues and teals |
| Lo-Fi | light | minimal, monochrome | High-contrast black and white |
| Pastel | light | soft, light | Soft pastel colors |
| Fantasy | light | magical, purple | Purple and gold |
| Wireframe | light | minimal, prototype | Grayscale, no-nonsense |
| Black | dark | minimal, monochrome | Pure black background |
| Luxury | dark | elegant, gold | Gold accents on dark |
| Dracula | dark | popular, purple | The popular Dracula color scheme |
| CMYK | light | colorful, print | Bold print-inspired colors |
| Autumn | light | warm, seasonal | Warm oranges and browns |
| Business | dark | professional, corporate | Professional dark theme |
| Acid | light | bold, vibrant | High-saturation neon green |
| Lemonade | light | fresh, green | Fresh greens and yellows |
| Night | dark | blue, deep | Deep blue tones |
| Coffee | dark | warm, brown | Rich coffee and chocolate |
| Winter | light | cool, blue | Cool blues and whites |
| Dim | dark | muted, subtle | Muted dark palette |
| Nord | light | cool, scandinavian | The popular Nord color scheme |
| Sunset | dark | warm, orange | Warm sunset gradients |
| Caramel Latte | light | warm, cozy | Warm caramel and cream |
| Abyss | dark | deep, ocean | Deep ocean blues |
| Silk | light | elegant, minimal | Refined, silky tones |
ODS themes are derived from the DaisyUI theme collection, but they are not used directly. There are three reasons:
The original DaisyUI themes prioritize visual aesthetics, which sometimes comes at the cost of text readability. We audited every theme’s color/content pairs against the WCAG AA contrast standard (minimum 4.5:1 ratio for normal text) and found 79 failures across 40 themes.
The worst offenders included pastel (7 failures), valentine (6), retro (5), bumblebee (5), and the original “dark” theme (4). Most failures were in the primary/primaryContent pair, where the text color intended to sit on top of a primary-colored button was too close in luminance to be readable.
We fixed every failure by adjusting the OKLCH lightness of the *Content color (the text that sits on top of the colored element) until it met a 4.6:1 ratio (slightly above the 4.5:1 minimum for safety margin). Hue and chroma were preserved so the color tint stays consistent with the theme’s design intent.
All 35 ODS themes now pass WCAG AA for every color/content pair in both light and dark modes.
The five audited pairs per mode are:
| Background Color | Text Color | Where It Appears |
|---|---|---|
primary |
primaryContent |
Text on primary buttons, links |
secondary |
secondaryContent |
Text on secondary buttons, tags |
accent |
accentContent |
Text on accent elements, badges |
base100 |
baseContent |
Body text on page background |
error |
errorContent |
Error message text |
DaisyUI is a Tailwind CSS plugin. ODS is framework-agnostic — the same theme must work in Flutter (which has no CSS), React, and any future framework. By extracting themes into standalone JSON files with OKLCH color values, any framework can resolve the tokens into its native theming system:
Color objects via OKLab/linear sRGB mathEvery ODS theme ships with both a light and dark variant, regardless of its nativeScheme. DaisyUI themes only define one mode (whichever they were designed for). ODS auto-generates the missing mode by flipping base colors and adjusting primary lightness, then audits both modes for contrast compliance. This means theme.mode: "system" always works with every theme.
Each theme is a JSON file in Specification/Themes/:
Themes/
catalog.json # Index of all themes with metadata
indigo.json # Default light theme
slate.json # Default dark theme
nord.json
dracula.json
...
{
"name": "indigo",
"displayName": "Indigo",
"author": "daisyui",
"nativeScheme": "light",
"design": {
"radiusSelector": ".5rem",
"radiusField": ".25rem",
"radiusBox": ".5rem",
"sizeSelector": ".25rem",
"sizeField": ".25rem",
"border": "1px",
"depth": 1,
"noise": 0
},
"light": {
"colorScheme": "light",
"colors": {
"primary": "oklch(45% .24 277)",
"primaryContent": "oklch(93% .034 273)",
"secondary": "oklch(65% .241 354)",
"secondaryContent": "oklch(23% .028 342)",
"accent": "oklch(77% .152 182)",
"accentContent": "oklch(38% .063 188)",
"neutral": "oklch(14% .005 286)",
"neutralContent": "oklch(92% .004 286)",
"base100": "oklch(100% 0 0)",
"base200": "oklch(98% 0 0)",
"base300": "oklch(95% 0 0)",
"baseContent": "oklch(21% .006 286)",
"info": "oklch(74% .16 233)",
"infoContent": "oklch(29% .066 243)",
"success": "oklch(76% .177 163)",
"successContent": "oklch(37% .077 169)",
"warning": "oklch(82% .189 84)",
"warningContent": "oklch(41% .112 46)",
"error": "oklch(71% .194 13)",
"errorContent": "oklch(27% .105 12)"
}
},
"dark": {
"colorScheme": "dark",
"colors": { "..." : "same structure with dark-mode values" }
}
}
A theme may declare typography to ship with the palette:
{
"fonts": {
"sans": "Inter",
"serif": "Source Serif 4",
"mono": "JetBrains Mono"
}
}
Most themes leave this out (renderers fall back to the system default). A few signature themes ship matching typography — for example, abyss pairs an atmospheric serif with its dark ocean palette. Builders can override per-app via theme.overrides.fontSans (or fontSerif/fontMono). Resolution order at runtime is: theme.overrides → theme.fonts → system default.
Colors are organized in pairs — a background color and the text/content color designed to sit on top of it:
| Token | Content Token | Purpose |
|---|---|---|
primary |
primaryContent |
Main action color (buttons, links, active states) |
secondary |
secondaryContent |
Supporting actions (secondary buttons, tags) |
accent |
accentContent |
Highlights (badges, notifications, emphasis) |
neutral |
neutralContent |
Neutral elements (borders, subtle backgrounds) |
base100 |
baseContent |
Page background and body text |
base200 |
— | Slightly darker background (cards, sidebars) |
base300 |
— | Borders, dividers, input outlines |
info |
infoContent |
Informational messages |
success |
successContent |
Success states |
warning |
warningContent |
Warning states |
error |
errorContent |
Error states |
All colors use the OKLCH format: oklch(L% C H) where:
OKLCH is perceptually uniform — equal steps in L produce equal perceived brightness changes. This makes contrast calculations reliable and color adjustments predictable. It’s the same color space used by modern CSS (oklch() is supported in all major browsers).
The design object controls non-color visual properties:
| Token | Purpose | Example |
|---|---|---|
radiusSelector |
Border radius for buttons, tabs | .5rem |
radiusField |
Border radius for input fields | .25rem |
radiusBox |
Border radius for cards, containers | .5rem |
sizeSelector |
Padding/spacing for selectable elements | .25rem |
sizeField |
Padding/spacing for input fields | .25rem |
border |
Default border width | 1px |
depth |
Shadow depth (0 = flat, 1 = subtle, 2 = raised) | 1 |
noise |
Background texture (0 = none, 1 = subtle grain) | 0 |
We encourage you to create and share new themes! Here’s how:
The easiest approach is to copy an existing theme and modify it. Pick a theme close to what you want:
cp indigo.json my-theme.json
Edit the color values in both light and dark sections. You can use any OKLCH color tool to pick colors:
oklch() in the color pickerStart with primary — it’s the most visible color (buttons, links, active states). Then work outward:
{
"name": "ocean",
"displayName": "Ocean",
"author": "your-name",
"nativeScheme": "light",
"design": {
"radiusSelector": ".5rem",
"radiusField": ".25rem",
"radiusBox": ".75rem",
"sizeSelector": ".25rem",
"sizeField": ".25rem",
"border": "1px",
"depth": 1,
"noise": 0
},
"light": {
"colorScheme": "light",
"colors": {
"primary": "oklch(50% .18 230)",
"primaryContent": "oklch(95% .02 230)",
"secondary": "oklch(60% .15 180)",
"secondaryContent": "oklch(20% .03 180)",
"accent": "oklch(75% .12 160)",
"accentContent": "oklch(25% .03 160)",
"neutral": "oklch(20% .01 230)",
"neutralContent": "oklch(85% .01 230)",
"base100": "oklch(99% .005 230)",
"base200": "oklch(96% .008 230)",
"base300": "oklch(92% .01 230)",
"baseContent": "oklch(20% .02 230)",
"info": "oklch(74% .16 233)",
"infoContent": "oklch(29% .066 243)",
"success": "oklch(76% .177 163)",
"successContent": "oklch(37% .077 169)",
"warning": "oklch(82% .189 84)",
"warningContent": "oklch(41% .112 46)",
"error": "oklch(71% .194 13)",
"errorContent": "oklch(27% .105 12)"
}
},
"dark": {
"colorScheme": "dark",
"colors": {
"primary": "oklch(65% .18 230)",
"primaryContent": "oklch(20% .03 230)",
"...": "define all tokens for dark mode"
}
}
}
Every *Content color must have a contrast ratio of at least 4.5:1 against its paired background color. This is a hard requirement — ODS themes must be accessible.
Quick rule of thumb: If the background is dark (L < 50%), the content should be light (L > 80%). If the background is light (L > 50%), the content should be dark (L < 30%).
You can verify contrast with:
python scripts/check_contrast.py from the Specification directory. Use --fix to auto-correct failing pairs.The five pairs that must pass WCAG AA (4.5:1) in both light and dark modes:
| Must Pass | Background | Text |
|---|---|---|
| 1 | primary |
primaryContent |
| 2 | secondary |
secondaryContent |
| 3 | accent |
accentContent |
| 4 | base100 |
baseContent |
| 5 | error |
errorContent |
Every theme must have both a light and dark object. Tips for creating the opposite mode:
base100 is near-white (L ~98-100%); dark mode is near-black (L ~20-28%). Keep the same hue and low chroma for tinted backgrounds.Set nativeScheme to whichever mode you designed first ("light" or "dark").
Add your theme to catalog.json:
{
"name": "ocean",
"displayName": "Ocean",
"file": "ocean.json",
"nativeScheme": "light",
"tags": ["cool", "blue", "nature"]
}
branding.theme to your theme nameWe’d love to include community themes in the catalog! To contribute:
If you like a theme but want to tweak a few colors, you don’t need to create a whole new theme. Use branding.overrides:
{
"branding": {
"theme": "nord",
"overrides": {
"primary": "oklch(55% .20 250)",
"primaryContent": "oklch(95% .02 250)"
}
}
}
Overrides are applied on top of the selected theme. Only the tokens you specify are changed; everything else comes from the theme. This is the recommended approach for brand-color matching without maintaining a full custom theme.
Important: If you override a color, also override its content pair to maintain contrast compliance.