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
-
primarywhen 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
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 default_themes() -> Themes
light() by default with dark() as the alternative.
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.