PhiaUi.Theme (phia_ui v0.1.17)

Copy Markdown View Source

Theme definition struct and API for PhiaUI.

A %PhiaUi.Theme{} describes a complete visual design system: color palettes for light and dark modes, typographic scale, border radius, and shadow definitions.

Color format

All color values use OKLCH notation (CSS Color Level 4), for example:

"oklch(0.546 0.245 262.881)"

OKLCH is a perceptually uniform color space that provides:

  • Wider gamut — can express P3 and Rec2020 colors beyond sRGB.
  • Perceptual uniformity — equal numeric steps produce equal perceptual differences, making it easy to construct accessible color ramps.
  • Predictable lightness — the L channel maps directly to human-perceived lightness, so dark-mode pairs are straightforward to reason about.

Color token mapping

Each theme defines the following semantic tokens (both light and dark variants):

TokenUsage
backgroundPage / panel background
foregroundDefault text color
cardCard surface background
card_foregroundText on cards
popoverPopover / dropdown background
popover_foregroundText in popovers
primaryPrimary action color (buttons, links)
primary_foregroundText on primary-colored surfaces
secondarySecondary / subtle surface
secondary_foregroundText on secondary surfaces
mutedMuted / disabled surface
muted_foregroundMuted / placeholder text
accentHover / focus highlight surface
accent_foregroundText on accent surfaces
destructiveError / delete action color
destructive_foregroundText on destructive surfaces
borderDefault border color
inputForm input border color
ringFocus ring color
sidebar_backgroundSidebar / navigation rail background

Token keys with underscores (e.g., card_foreground) are converted to hyphenated CSS custom properties (e.g., --color-card-foreground) by PhiaUi.ThemeCSS.

Built-in presets

Use PhiaUi.Theme.get/1 to retrieve a built-in preset by atom key:

{:ok, theme} = PhiaUi.Theme.get(:blue)
{:ok, theme} = PhiaUi.Theme.get(:zinc)

Available presets

KeyDescription
:zincNeutral dark — shadcn/ui default
:slateCool blue-grey, slightly cooler than zinc
:blueEnterprise blue, vibrant primary
:roseModern rose/pink, warm and playful
:orangeEnergetic orange, bold and attention-
:greenSuccess green, calm and natural
:violetPremium violet, creative and distinctive
:neutralPure achromatic grey, zero chroma

JSON schema

Themes can be serialised to / deserialised from JSON via PhiaUi.ThemeCSS:

%{
  "name"  => "my-brand",
  "label" => "My Brand",
  "colors" => %{
    "light" => %{"background" => "oklch(1 0 0)", ...},
    "dark"  => %{"background" => "oklch(0.145 0 0)", ...}
  },
  "radius"     => "0.5rem",
  "typography" => %{"font_sans" => "Inter, system-ui, sans-serif"},
  "shadows"    => %{}
}

Custom themes

You can construct a %PhiaUi.Theme{} directly and pass it to PhiaUi.ThemeCSS.generate/2:

theme = %PhiaUi.Theme{
  name: "brand",
  label: "Acme Brand",
  radius: "0.25rem",
  colors: %{
    light: %{background: "oklch(1 0 0)", primary: "oklch(0.6 0.2 30)", ...},
    dark:  %{background: "oklch(0.1 0 0)", primary: "oklch(0.6 0.2 30)", ...}
  }
}

css = PhiaUi.ThemeCSS.generate(theme)

Summary

Functions

Creates a %PhiaUi.Theme{} struct from a JSON-decoded map.

Retrieves a built-in preset theme by its atom key.

Retrieves a built-in preset theme by its atom key, raising on failure.

Returns all available built-in preset theme names as a list of atoms.

Converts a %PhiaUi.Theme{} to a JSON-serialisable map with string keys.

Types

color_map()

@type color_map() :: %{required(atom()) => String.t()}

t()

@type t() :: %PhiaUi.Theme{
  colors: %{light: color_map(), dark: color_map()},
  label: String.t(),
  name: String.t(),
  radius: String.t(),
  shadows: map(),
  typography: map()
}

Functions

from_map(map)

@spec from_map(map()) :: t()

Creates a %PhiaUi.Theme{} struct from a JSON-decoded map.

Accepts string keys as produced by Jason.decode/1. Both light and dark color maps are converted to atom-keyed maps internally (e.g., "card_foreground" becomes :card_foreground).

Falls back to sensible defaults when optional fields (radius, typography, shadows) are absent from the map.

Example

iex> map = %{
...>   "name" => "custom",
...>   "label" => "Custom",
...>   "colors" => %{
...>     "light" => %{"background" => "oklch(1 0 0)"},
...>     "dark"  => %{"background" => "oklch(0.1 0 0)"}
...>   }
...> }
iex> theme = PhiaUi.Theme.from_map(map)
iex> theme.name
"custom"

get(key)

@spec get(atom()) :: {:ok, t()} | {:error, :not_found}

Retrieves a built-in preset theme by its atom key.

Returns {:ok, %PhiaUi.Theme{}} on success or {:error, :not_found} when the key is not a known preset.

Examples

iex> {:ok, theme} = PhiaUi.Theme.get(:blue)
iex> theme.name
"blue"
iex> theme.label
"Blue"

iex> PhiaUi.Theme.get(:unknown)
{:error, :not_found}

get!(key)

@spec get!(atom()) :: t()

Retrieves a built-in preset theme by its atom key, raising on failure.

Prefer get/1 in application code. Use this variant in scripts or tests where a missing theme is a programming error.

Examples

iex> theme = PhiaUi.Theme.get!(:zinc)
iex> theme.name
"zinc"

Raises ArgumentError for unknown keys:

iex> PhiaUi.Theme.get!(:unknown)
** (ArgumentError) Unknown theme preset: :unknown

list()

@spec list() :: [atom()]

Returns all available built-in preset theme names as a list of atoms.

The order is not guaranteed; sort the result if you need stable ordering.

Example

iex> themes = PhiaUi.Theme.list()
iex> :zinc in themes
true
iex> :blue in themes
true

to_map(theme)

@spec to_map(t()) :: map()

Converts a %PhiaUi.Theme{} to a JSON-serialisable map with string keys.

The output map is suitable for passing to Jason.encode!/1 or for storing in a database. Atom keys in colors, typography, and shadows are stringified.

Example

iex> theme = PhiaUi.Theme.get!(:zinc)
iex> map = PhiaUi.Theme.to_map(theme)
iex> map["name"]
"zinc"
iex> is_map(map["colors"]["light"])
true