Themes

Learn how to control themes and create custom color schemes.

Fluxon UI supports automatic light/dark mode detection, manual theme control, and custom theme creation. All themes use the same semantic color tokens for consistency.

Automatic Theme Detection

By default, Fluxon UI automatically detects and follows the user's system theme preference. This works through CSS color-scheme and light-dark() functions.

theme.css
:root {
  color-scheme: light dark;

  /* Automatically switches based on system preference */
  --primary: light-dark(var(--color-zinc-900), var(--color-white));
  --background-base: light-dark(var(--color-white), var(--color-zinc-900));
}

This automatic behavior requires no JavaScript and works out of the box.

Manual Theme Control

For applications that need explicit theme control, you can use the data-theme attribute to override automatic detection.

app.css
[data-theme='light'] {
  color-scheme: light;
}

[data-theme='dark'] {
  color-scheme: dark;
}

Control the theme by setting the attribute on the HTML element:

root.html.heex
<!-- Force light theme -->
<html data-theme="light">

<!-- Force dark theme -->
<html data-theme="dark">

Forcing Specific Themes

To completely disable automatic theme switching and always use a specific theme, set the color-scheme property on :root:

app.css
/* Always use light theme, ignore system preference */
:root {
  color-scheme: light;
}

/* Always use dark theme, ignore system preference */
:root {
  color-scheme: dark;
}

Creating Custom Themes

Create entirely new themes by defining all semantic tokens with the data-theme selector. Custom themes override the default light/dark behavior.

app.css
[data-theme='pastel'] {
  color-scheme: light;

  /* Brand */
  --primary: oklch(0.66 0.14 290); /* Soft lavender */
  --primary-soft: color-mix(in oklab, var(--primary) 12%, var(--background-base) 88%);

  /* Foreground */
  --foreground: oklch(0.38 0.06 290); /* Dusty purple */
  --foreground-soft: oklch(0.50 0.05 290); /* Muted purple */
  --foreground-softer: oklch(0.62 0.04 290); /* Soft purple */
  --foreground-softest: oklch(0.76 0.03 290); /* Pale purple */
  --foreground-primary: oklch(0.98 0.012 290); /* Cloud white */

  /* Backgrounds */
  --background-base: oklch(0.985 0.008 300); /* Dreamy white */
  --surface: oklch(0.97 0.012 300); /* Silk surface */
  --background-sunken: oklch(0.95 0.016 300); /* Recessed mist */
  --background-control: oklch(0.98 0.010 300); /* Lifted control */
  --background-input: oklch(0.97 0.010 300);
  --background-input-disabled: oklch(0.93 0.014 300);
  --background-switch: oklch(0.86 0.028 300);
  --background-tooltip: var(--primary);

  /* Borders */
  --border-base: oklch(0.85 0.028 300); /* Gentle border */
  --border-subtle: oklch(0.91 0.018 300);
  --border-input: oklch(0.81 0.038 300);

  /* Danger */
  --danger: oklch(0.68 0.15 18); /* Soft rose */
  --danger-foreground: oklch(0.99 0.005 18);
  --danger-border: oklch(0.78 0.10 18);
  --danger-soft: color-mix(in oklab, var(--danger) 14%, var(--background-base) 86%);
  --danger-soft-foreground: oklch(0.44 0.17 18);

  /* Success */
  --success: oklch(0.72 0.13 158); /* Mint green */
  --success-foreground: oklch(0.99 0.005 158);
  --success-border: oklch(0.80 0.09 158);
  --success-soft: color-mix(in oklab, var(--success) 14%, var(--background-base) 86%);
  --success-soft-foreground: oklch(0.44 0.13 158);

  /* Warning */
  --warning: oklch(0.84 0.13 82); /* Butter yellow */
  --warning-foreground: oklch(0.34 0.08 82);
  --warning-border: oklch(0.80 0.14 82);
  --warning-soft: color-mix(in oklab, var(--warning) 16%, var(--background-base) 84%);
  --warning-soft-foreground: oklch(0.42 0.12 82);

  /* Info */
  --info: oklch(0.74 0.12 238); /* Sky blue */
  --info-foreground: oklch(0.99 0.005 238);
  --info-border: oklch(0.82 0.09 238);
  --info-soft: color-mix(in oklab, var(--info) 14%, var(--background-base) 86%);
  --info-soft-foreground: oklch(0.46 0.14 238);

  /* Focus */
  --focus: oklch(0.74 0.13 290);
  --focus-danger: oklch(0.70 0.16 18);

  /* Layout */
  --radius-base: var(--radius-sm);
  --radius-badge: var(--radius-md);
  --shadow-base: 0 2px 8px -1px oklch(0.66 0.14 290 / 0.10);
}

Applying Custom Themes

Use custom themes by setting the data-theme attribute:

root.html.heex
<!-- Apply pastel theme -->
<html data-theme="pastel">

<!-- Apply cappuccino theme -->
<html data-theme="cappuccino">

Example Custom Themes

Apply some custom themes that will effectively demonstrate the theme system.

Default Light
Default Dark
Midnight
Forest
Pastel
Cappuccino

For detailed information about the semantic color system and token reference, see the Colors guide.