howdy/ui/theme

Themes are plain records of colours, fonts and radii. Each one compiles to a block of CSS custom properties, and every component in howdy/ui reads those properties through howdy/ui/theme/tokens instead of naming a colour directly. Change a theme and every element follows.

import howdy/ui/theme.{Colors}

pub fn brand() -> theme.Theme {
  theme.light()
  |> theme.colors(fn(c) { Colors(..c, primary: "#0f766e", on_primary: "#fff") })
}

theme.themes(default: brand(), alternatives: [theme.dark()])

The default theme goes on :root. Every theme, including the default, is also written under [data-theme="name"], so setting that attribute on the root element switches theme. When the attribute is absent, the first alternative with the opposite colour scheme to the default is applied through prefers-color-scheme.

Adding a field to Colors, Font or Radius is a compile error until every theme supplies it. That is what keeps light and dark in step.

Types

Every colour a component may use. Values are any CSS colour.

pub type Colors {
  Colors(
    background: String,
    surface: String,
    muted: String,
    border: String,
    text: String,
    text_muted: String,
    primary: String,
    primary_hover: String,
    on_primary: String,
    danger: String,
    on_danger: String,
    focus: String,
    chart_1: String,
    chart_2: String,
    chart_3: String,
    chart_4: String,
    chart_5: String,
  )
}

Constructors

  • Colors(
      background: String,
      surface: String,
      muted: String,
      border: String,
      text: String,
      text_muted: String,
      primary: String,
      primary_hover: String,
      on_primary: String,
      danger: String,
      on_danger: String,
      focus: String,
      chart_1: String,
      chart_2: String,
      chart_3: String,
      chart_4: String,
      chart_5: String,
    )

    Arguments

    background

    Page background.

    surface

    Cards, panels and inputs.

    muted

    Subtle fills: secondary buttons and badges, hovered rows and ghost buttons, skeletons.

    border

    Lines around surfaces and inputs.

    text

    Body text.

    text_muted

    Secondary text such as captions and placeholders.

    primary

    Buttons and links.

    primary_hover

    primary when hovered.

    on_primary

    Text on top of primary.

    danger

    Destructive actions and errors.

    on_danger

    Text on top of danger.

    focus

    Focus rings.

    chart_1

    Chart series, in the order they are used. The built-in themes’ five pass colour-vision-deficiency separation checks as an ordered set against their own surface; keep that order, and check any replacement, rather than shuffling them.

Font stacks.

pub type Font {
  Font(body: String, heading: String, mono: String)
}

Constructors

  • Font(body: String, heading: String, mono: String)

Corner radii, as CSS lengths.

pub type Radius {
  Radius(small: String, medium: String, large: String)
}

Constructors

  • Radius(small: String, medium: String, large: String)

Whether a theme is light or dark. Sets the color-scheme property so form controls and scrollbars match, and drives the system preference media query.

pub type Scheme {
  Light
  Dark
}

Constructors

  • Light
  • Dark

A complete theme.

pub type Theme {
  Theme(
    name: String,
    scheme: Scheme,
    colors: Colors,
    font: Font,
    radius: Radius,
  )
}

Constructors

  • Theme(
      name: String,
      scheme: Scheme,
      colors: Colors,
      font: Font,
      radius: Radius,
    )

    Arguments

    name

    The value of the data-theme attribute that selects this theme.

The set of themes a page offers.

pub type Themes {
  Themes(default: Theme, alternatives: List(Theme))
}

Constructors

  • Themes(default: Theme, alternatives: List(Theme))

Values

pub fn colors(
  theme: Theme,
  update: fn(Colors) -> Colors,
) -> Theme

Adjust the colours. Use record update syntax to change only some:

theme.colors(theme.light(), fn(c) { Colors(..c, primary: "#0f766e") })
pub fn dark() -> Theme

The built-in dark theme, named "dark".

pub fn default_themes() -> Themes

light() by default with dark() as the alternative.

pub fn font(theme: Theme, update: fn(Font) -> Font) -> Theme

Adjust the fonts.

pub fn light() -> Theme

The built-in light theme, named "light".

pub fn named(theme: Theme, name: String) -> Theme

A copy of the theme under a different name.

pub const prefix: String

The prefix of every custom property, as in --howdy-primary.

pub fn radius(
  theme: Theme,
  update: fn(Radius) -> Radius,
) -> Theme

Adjust the radii.

pub fn themes(
  default default: Theme,
  alternatives alternatives: List(Theme),
) -> Themes

Build a set of themes.

pub fn to_css(themes: Themes) -> String

The CSS for a set of themes: the default on :root, every theme under its data-theme selector, and a prefers-color-scheme fallback.

pub fn variables(theme: Theme) -> List(#(String, String))

The custom properties a theme defines, as #(name, value) pairs. This is the single list howdy/ui/theme/tokens and the CSS output are both built from.

✨ Search Document