themes

Theme management for daisyUI

Built-in Themes

daisyUI provides 35 built-in themes:


DaisyUITheme


def DaisyUITheme(
    args:VAR_POSITIONAL, kwds:VAR_KEYWORD
):

All built-in daisyUI themes.

# List all available themes
print(f"Total themes: {len(DaisyUITheme)}")
print("Themes:", [theme.value for theme in DaisyUITheme])
Total themes: 35
Themes: ['light', 'dark', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'synthwave', 'retro', 'cyberpunk', 'valentine', 'halloween', 'garden', 'forest', 'aqua', 'lofi', 'pastel', 'fantasy', 'wireframe', 'black', 'luxury', 'dracula', 'cmyk', 'autumn', 'business', 'acid', 'lemonade', 'night', 'coffee', 'winter', 'dim', 'nord', 'sunset', 'caramellatte', 'abyss', 'silk']
# Theme access
print(f"Light theme: {DaisyUITheme.LIGHT.value}")
print(f"Dark theme: {DaisyUITheme.DARK.value}")
print(f"Cupcake theme: {DaisyUITheme.CUPCAKE.value}")
Light theme: light
Dark theme: dark
Cupcake theme: cupcake

Theme Selection

Helper functions for working with themes:


get_theme_value


def get_theme_value(
    theme:Union, # The theme to validate (DaisyUITheme enum or string)
    allow_custom:bool=False, # If True, allows any string value for custom themes
)->str: # The validated theme name as a string

Get the string value of a theme, supporting both enum and string inputs. This allows flexibility in how themes are specified while maintaining type safety.

# Test theme value getter
print(get_theme_value(DaisyUITheme.LIGHT))
print(get_theme_value("dark"))

# Test invalid theme
try:
    get_theme_value("invalid-theme")
except ValueError as e:
    print(f"Error: {e}")
light
dark
Error: 'invalid-theme' is not a valid daisyUI theme. Valid themes are: abyss, acid, aqua, autumn, black, bumblebee, business, caramellatte, cmyk, coffee, corporate, cupcake, cyberpunk, dark, dim, dracula, emerald, fantasy, forest, garden, halloween, lemonade, light, lofi, luxury, night, nord, pastel, retro, silk, sunset, synthwave, valentine, winter, wireframe
# Test with custom theme names (allow_custom=True)
print("Testing custom theme names:")
print(f"Custom theme 'my-custom-theme': {get_theme_value('my-custom-theme', allow_custom=True)}")
print(f"Custom theme 'neon_nights': {get_theme_value('neon_nights', allow_custom=True)}")

# Verify that custom themes are rejected when allow_custom=False
try:
    get_theme_value("my-custom-theme", allow_custom=False)
except ValueError as e:
    print(f"\nCustom theme rejected (expected): {e}")
Testing custom theme names:
Custom theme 'my-custom-theme': my-custom-theme
Custom theme 'neon_nights': neon_nights

Custom theme rejected (expected): 'my-custom-theme' is not a valid daisyUI theme. Valid themes are: abyss, acid, aqua, autumn, black, bumblebee, business, caramellatte, cmyk, coffee, corporate, cupcake, cyberpunk, dark, dim, dracula, emerald, fantasy, forest, garden, halloween, lemonade, light, lofi, luxury, night, nord, pastel, retro, silk, sunset, synthwave, valentine, winter, wireframe

Custom Theme Creation

For creating custom themes with full Python abstraction:


ThemeColors


def ThemeColors(
    args:VAR_POSITIONAL, kwargs:VAR_KEYWORD
):

Color definitions for a daisyUI theme using OKLCH color space.


ThemeConfig


def ThemeConfig(
    args:VAR_POSITIONAL, kwargs:VAR_KEYWORD
):

Complete configuration for a custom daisyUI theme.


create_theme_css


def create_theme_css(
    theme:ThemeConfig, # Theme configuration with colors, sizes, and effects
)->str: # CSS string with theme variables

Generate CSS for a custom daisyUI theme.

This creates the CSS variables needed for a custom theme when using the CDN approach.

Example custom theme:

# Create a custom theme configuration
custom_light_theme: ThemeConfig = {
    "name": "custom_light_theme",
    "default": False,
    "prefersdark": False,
    "color_scheme": "light",
    "colors": {
        "base_100": "oklch(98% 0.005 220)",
        "base_200": "oklch(96% 0.008 215)",
        "base_300": "oklch(92% 0.012 210)",
        "base_content": "oklch(18% 0.015 230)",
        "primary": "oklch(55% 0.18 260)",
        "primary_content": "oklch(98% 0.005 260)",
        "secondary": "oklch(45% 0.12 340)",
        "secondary_content": "oklch(98% 0.005 340)",
        "accent": "oklch(65% 0.15 180)",
        "accent_content": "oklch(15% 0.01 180)",
        "neutral": "oklch(25% 0.01 240)",
        "neutral_content": "oklch(95% 0.005 240)",
        "info": "oklch(60% 0.16 230)",
        "info_content": "oklch(98% 0.005 230)",
        "success": "oklch(58% 0.14 150)",
        "success_content": "oklch(98% 0.005 150)",
        "warning": "oklch(72% 0.16 85)",
        "warning_content": "oklch(18% 0.01 85)",
        "error": "oklch(55% 0.20 15)",
        "error_content": "oklch(98% 0.005 15)"
    },
    "radius_selector": "0.5rem",
    "radius_field": "0.75rem",
    "radius_box": "1.25rem",
    "size_selector": "0.375rem",
    "size_field": "0.375rem",
    "border": "1.5px",
    "depth": 2,
    "noise": 1
}

# Generate the CSS
css = create_theme_css(custom_light_theme)
print(css)
:root:has(input.theme-controller[value=custom_light_theme]:checked),[data-theme="custom_light_theme"] {
  color-scheme: light;
  --color-base-100: oklch(98% 0.005 220);
  --color-base-200: oklch(96% 0.008 215);
  --color-base-300: oklch(92% 0.012 210);
  --color-base-content: oklch(18% 0.015 230);
  --color-primary: oklch(55% 0.18 260);
  --color-primary-content: oklch(98% 0.005 260);
  --color-secondary: oklch(45% 0.12 340);
  --color-secondary-content: oklch(98% 0.005 340);
  --color-accent: oklch(65% 0.15 180);
  --color-accent-content: oklch(15% 0.01 180);
  --color-neutral: oklch(25% 0.01 240);
  --color-neutral-content: oklch(95% 0.005 240);
  --color-info: oklch(60% 0.16 230);
  --color-info-content: oklch(98% 0.005 230);
  --color-success: oklch(58% 0.14 150);
  --color-success-content: oklch(98% 0.005 150);
  --color-warning: oklch(72% 0.16 85);
  --color-warning-content: oklch(18% 0.01 85);
  --color-error: oklch(55% 0.20 15);
  --color-error-content: oklch(98% 0.005 15);
  --radius-selector: 0.5rem;
  --radius-field: 0.75rem;
  --radius-box: 1.25rem;
  --size-selector: 0.375rem;
  --size-field: 0.375rem;
  --border: 1.5px;
  --depth: 2;
  --noise: 1;
}

Theme File Management

Functions for saving and loading theme configurations:


save_theme_css


def save_theme_css(
    theme:ThemeConfig, # Theme configuration to convert to CSS
    path:Union, # File path where CSS will be saved
)->None: # None

Save a theme configuration as a CSS file.


save_theme_json


def save_theme_json(
    theme:ThemeConfig, # Theme configuration to save
    path:Union, # File path where JSON will be saved
)->None: # None

Save a theme configuration as a JSON file for reuse.


load_theme_json


def load_theme_json(
    path:Union, # Path to JSON file containing theme configuration
)->ThemeConfig: # Theme configuration dictionary

Load a theme configuration from a JSON file.


load_style_css


def load_style_css(
    path:Union, # Path to CSS file containing theme configuration
)->functools.partial(<function ft at 0x7f07810da5c0>, 'style', void_=False): # FasthHTML Style element

Load a theme configuration from a CSS file to a FasthHTML Style element.

from nbdev.config import get_config
cfg = get_config()
project_dir = cfg.config_path
css_dir = project_dir/"css"
css_dir.mkdir(exist_ok=True, parents=True)
save_path = css_dir/f"{custom_light_theme['name']}.css"
save_theme_css(custom_light_theme, save_path)
load_style_css(save_path)
<style>:root:has(input.theme-controller[value=custom_light_theme]:checked),[data-theme=&quot;custom_light_theme&quot;] {
  color-scheme: light;
  --color-base-100: oklch(98% 0.005 220);
  --color-base-200: oklch(96% 0.008 215);
  --color-base-300: oklch(92% 0.012 210);
  --color-base-content: oklch(18% 0.015 230);
  --color-primary: oklch(55% 0.18 260);
  --color-primary-content: oklch(98% 0.005 260);
  --color-secondary: oklch(45% 0.12 340);
  --color-secondary-content: oklch(98% 0.005 340);
  --color-accent: oklch(65% 0.15 180);
  --color-accent-content: oklch(15% 0.01 180);
  --color-neutral: oklch(25% 0.01 240);
  --color-neutral-content: oklch(95% 0.005 240);
  --color-info: oklch(60% 0.16 230);
  --color-info-content: oklch(98% 0.005 230);
  --color-success: oklch(58% 0.14 150);
  --color-success-content: oklch(98% 0.005 150);
  --color-warning: oklch(72% 0.16 85);
  --color-warning-content: oklch(18% 0.01 85);
  --color-error: oklch(55% 0.20 15);
  --color-error-content: oklch(98% 0.005 15);
  --radius-selector: 0.5rem;
  --radius-field: 0.75rem;
  --radius-box: 1.25rem;
  --size-selector: 0.375rem;
  --size-field: 0.375rem;
  --border: 1.5px;
  --depth: 2;
  --noise: 1;
}</style>

Theme JSON Persistence

Save and load theme configurations as JSON files for easy reuse and sharing:

# Create a new custom theme for JSON save/load demonstration
neon_theme: ThemeConfig = {
    "name": "neon_nights",
    "default": False,
    "prefersdark": True,
    "color_scheme": "dark",
    "colors": {
        "base_100": "oklch(10% 0.02 260)",
        "base_200": "oklch(8% 0.02 260)",
        "base_300": "oklch(6% 0.02 260)",
        "base_content": "oklch(85% 0.15 320)",
        "primary": "oklch(70% 0.25 320)",
        "primary_content": "oklch(10% 0.02 320)",
        "secondary": "oklch(60% 0.22 180)",
        "secondary_content": "oklch(10% 0.02 180)",
        "accent": "oklch(75% 0.28 90)",
        "accent_content": "oklch(10% 0.02 90)",
        "neutral": "oklch(20% 0.03 260)",
        "neutral_content": "oklch(80% 0.10 320)",
        "info": "oklch(65% 0.20 220)",
        "info_content": "oklch(10% 0.02 220)",
        "success": "oklch(65% 0.22 150)",
        "success_content": "oklch(10% 0.02 150)",
        "warning": "oklch(72% 0.25 60)",
        "warning_content": "oklch(10% 0.02 60)",
        "error": "oklch(68% 0.25 15)",
        "error_content": "oklch(10% 0.02 15)"
    },
    "radius_selector": "0.25rem",
    "radius_field": "0.125rem",
    "radius_box": "0.5rem",
    "size_selector": "0.25rem",
    "size_field": "0.25rem",
    "border": "2px",
    "depth": 0.5,
    "noise": 0.2
}

# Save the theme as JSON
json_path = css_dir / f"{neon_theme['name']}.json"
save_theme_json(neon_theme, json_path)
print(f"Saved theme to: {json_path}")

# Verify the JSON file was created
print(f"\nJSON file contents:")
with open(json_path, 'r') as f:
    print(f.read()[:500] + "...")  # Show first 500 chars
Saved theme to: /mnt/SN850X_8TB_EXT4/Projects/GitHub/cj-mills/cjm-fasthtml-daisyui/css/neon_nights.json

JSON file contents:
{
  "name": "neon_nights",
  "default": false,
  "prefersdark": true,
  "color_scheme": "dark",
  "colors": {
    "base_100": "oklch(10% 0.02 260)",
    "base_200": "oklch(8% 0.02 260)",
    "base_300": "oklch(6% 0.02 260)",
    "base_content": "oklch(85% 0.15 320)",
    "primary": "oklch(70% 0.25 320)",
    "primary_content": "oklch(10% 0.02 320)",
    "secondary": "oklch(60% 0.22 180)",
    "secondary_content": "oklch(10% 0.02 180)",
    "accent": "oklch(75% 0.28 90)",
    "accent_content": "o...
# Load the theme back from JSON
loaded_theme = load_theme_json(json_path)

# Verify it loaded correctly
print(f"Loaded theme name: {loaded_theme['name']}")
print(f"Color scheme: {loaded_theme['color_scheme']}")
print(f"Primary color: {loaded_theme['colors']['primary']}")
print(f"Border radius (box): {loaded_theme['radius_box']}")

# Convert the loaded theme to CSS
loaded_css = create_theme_css(loaded_theme)
print(f"\nGenerated CSS from loaded theme (first 500 chars):")
print(loaded_css[:500] + "...")
Loaded theme name: neon_nights
Color scheme: dark
Primary color: oklch(70% 0.25 320)
Border radius (box): 0.5rem

Generated CSS from loaded theme (first 500 chars):
:root:has(input.theme-controller[value=neon_nights]:checked),[data-theme="neon_nights"] {
  color-scheme: dark;
  --color-base-100: oklch(10% 0.02 260);
  --color-base-200: oklch(8% 0.02 260);
  --color-base-300: oklch(6% 0.02 260);
  --color-base-content: oklch(85% 0.15 320);
  --color-primary: oklch(70% 0.25 320);
  --color-primary-content: oklch(10% 0.02 320);
  --color-secondary: oklch(60% 0.22 180);
  --color-secondary-content: oklch(10% 0.02 180);
  --color-accent: oklch(75% 0.28 90);
  --...
# Save the loaded theme as CSS to demonstrate the full workflow
css_from_json_path = css_dir / f"{loaded_theme['name']}_from_json.css"
save_theme_css(loaded_theme, css_from_json_path)

# Verify both CSS files have the same content
original_css = create_theme_css(neon_theme)
loaded_and_saved_css = Path(css_from_json_path).read_text()

print(f"CSS files match: {original_css == loaded_and_saved_css}")
print(f"\nWorkflow complete:")
print(f"1. Created theme configuration in Python")
print(f"2. Saved to JSON: {json_path}")
print(f"3. Loaded from JSON")
print(f"4. Generated CSS from loaded theme")
print(f"5. Saved CSS: {css_from_json_path}")
CSS files match: True

Workflow complete:
1. Created theme configuration in Python
2. Saved to JSON: /mnt/SN850X_8TB_EXT4/Projects/GitHub/cj-mills/cjm-fasthtml-daisyui/css/neon_nights.json
3. Loaded from JSON
4. Generated CSS from loaded theme
5. Saved CSS: /mnt/SN850X_8TB_EXT4/Projects/GitHub/cj-mills/cjm-fasthtml-daisyui/css/neon_nights_from_json.css

Export