TermUI.Theme (TermUI v0.2.0)

View Source

Theme system for application-wide visual consistency.

Themes define colors, semantic meanings, and component style defaults. The theme system supports runtime switching and notifies subscribers of changes.

Theme Structure

A theme contains:

  • :name - Theme identifier (e.g., :dark, :light)
  • :colors - Base colors (background, foreground, primary, etc.)
  • :semantic - Semantic colors (success, warning, error, etc.)
  • :components - Per-component style defaults

Built-in Themes

  • :dark - Dark background with light text (default)
  • :light - Light background with dark text
  • :high_contrast - High contrast for accessibility

Examples

# Start theme server
Theme.start_link(theme: :dark)

# Get current theme
theme = Theme.get_theme()

# Switch themes at runtime
Theme.set_theme(:light)

# Subscribe to theme changes
Theme.subscribe()
receive do
  {:theme_changed, new_theme} -> handle_change(new_theme)
end

# Get colors
bg = Theme.get_color(:background)
error = Theme.get_semantic(:error)

# Get component style
style = Theme.get_component_style(:button, :focused)

Summary

Functions

Returns a specification to start this module under a supervisor.

Creates a theme from a keyword list or map, merging with a base theme.

Gets a built-in theme by name.

Gets a base color from the current theme.

Gets a component style from the current theme.

Gets a semantic color from the current theme.

Gets the current theme.

Lists all available built-in themes.

Sets the current theme.

Starts the theme server.

Creates a style from theme values with optional overrides.

Subscribes the calling process to theme change notifications.

Unsubscribes the calling process from theme change notifications.

Validates a theme struct.

Types

color()

@type color() :: TermUI.Style.color()

colors()

@type colors() :: %{
  background: color(),
  foreground: color(),
  primary: color(),
  secondary: color(),
  accent: color()
}

component_styles()

@type component_styles() :: %{
  required(atom()) => %{required(atom()) => TermUI.Style.t()}
}

semantic()

@type semantic() :: %{
  success: color(),
  warning: color(),
  error: color(),
  info: color(),
  muted: color()
}

t()

@type t() :: %TermUI.Theme{
  colors: colors(),
  components: component_styles(),
  name: atom(),
  semantic: semantic()
}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

from(opts)

@spec from(keyword() | map()) :: {:ok, t()} | {:error, term()}

Creates a theme from a keyword list or map, merging with a base theme.

Options

  • :base - Base theme to merge with (default :dark)
  • :name - Theme name
  • :colors - Color overrides
  • :semantic - Semantic color overrides
  • :components - Component style overrides

Examples

# Create custom theme based on dark
{:ok, theme} = Theme.from(
  base: :dark,
  name: :my_theme,
  colors: %{primary: :magenta}
)

get_builtin(name)

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

Gets a built-in theme by name.

get_color(name, server \\ __MODULE__)

@spec get_color(atom(), GenServer.server()) :: color() | nil

Gets a base color from the current theme.

Examples

Theme.get_color(:background)  # => :black
Theme.get_color(:primary)     # => :blue

get_component_style(component, variant, server \\ __MODULE__)

@spec get_component_style(atom(), atom(), GenServer.server()) ::
  TermUI.Style.t() | nil

Gets a component style from the current theme.

Examples

Theme.get_component_style(:button, :focused)
Theme.get_component_style(:text_input, :normal)

get_semantic(name, server \\ __MODULE__)

@spec get_semantic(atom(), GenServer.server()) :: color() | nil

Gets a semantic color from the current theme.

Examples

Theme.get_semantic(:error)    # => :red
Theme.get_semantic(:success)  # => :green

get_theme(server \\ __MODULE__)

@spec get_theme(GenServer.server()) :: t()

Gets the current theme.

list_builtin()

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

Lists all available built-in themes.

set_theme(theme, server \\ __MODULE__)

@spec set_theme(atom() | t(), GenServer.server()) :: :ok | {:error, term()}

Sets the current theme.

Accepts a theme name atom (for built-in themes) or a Theme struct. Notifies all subscribers of the change.

Examples

Theme.set_theme(:light)
Theme.set_theme(%Theme{name: :custom, ...})

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Starts the theme server.

Options

  • :theme - Initial theme (atom name or Theme struct, default :dark)
  • :name - GenServer name (default Elixir.TermUI.Theme)

Examples

Theme.start_link(theme: :dark)
Theme.start_link(theme: :light, name: MyApp.Theme)

style_from_theme(component, variant, overrides \\ [], server \\ __MODULE__)

@spec style_from_theme(atom(), atom(), keyword(), GenServer.server()) ::
  TermUI.Style.t()

Creates a style from theme values with optional overrides.

Useful for components that want to use theme defaults but allow customization.

Examples

# Use theme button style as base, override foreground
style = Theme.style_from_theme(:button, :normal, fg: :red)

subscribe(server \\ __MODULE__)

@spec subscribe(GenServer.server()) :: :ok

Subscribes the calling process to theme change notifications.

Subscribers receive {:theme_changed, theme} messages when the theme changes.

unsubscribe(server \\ __MODULE__)

@spec unsubscribe(GenServer.server()) :: :ok

Unsubscribes the calling process from theme change notifications.

validate(theme)

@spec validate(t()) :: :ok | {:error, [String.t()]}

Validates a theme struct.

Returns :ok if valid, {:error, reasons} if invalid.