Visualize.Theme (Visualize v0.2.35)

Copy Markdown View Source

The colours, font and sizes a chart draws with (spec/08 §7).

A theme is a struct of named slots: :series_1 … :series_n for the categorical colours, :axis, :grid, :text, :background and :surface — the plane the data is laid on, between the background and the grid — and :font_family, :font_size, :label_size and :title_size. Nothing in the library carries a colour literal; every axis, mark and component reads its values through resolve/3, in the mode its render target needs:

  • :literal - the stored value, which a canvas needs
  • :css - a custom-property reference with the literal as its fallback, var(--vis-axis, #666666), which the SVG output carries so a stylesheet can override it from any ancestor (D-55)

stylesheet/1 writes a .vis-theme-<name> rule declaring every slot, so a consumer switches every chart under an element of that class without touching Elixir.

Examples

iex> Visualize.Theme.resolve(Visualize.Theme.default(), :axis, :css)
"var(--vis-axis, #666666)"
iex> Visualize.Theme.resolve(Visualize.Theme.dark(), {:series, 11}, :literal)
"#7fb3e6"
iex> Visualize.Theme.new(font_family: "Inter, sans-serif").font_family
"Inter, sans-serif"

Summary

Types

How a slot renders: a CSS reference with fallback, or the stored value.

A slot name, or {:series, i} for the i-th series colour, cycling.

t()

Functions

The theme's colour slots — the series, then axis, grid, text, background and surface (spec/08 §7.1).

The custom-property name of a slot atom: --vis- and the slot with _ as -.

The dark theme: light text and axes on #1b1e24 (spec/08 §7.2).

The light theme: dark text and axes on white (spec/08 §7.2).

The contrast ink for a text drawn on under (spec/08 §7.3, D-123): the literal of the theme's text or background, whichever has the higher WCAG contrast ratio against it — text on a tie — or #000000/#ffffff, whichever contrasts more, when neither slot reaches 4.5:1. One of black and white always does, so the ink always meets WCAG AA.

The theme with the given fields replaced; the checks of new/1 apply.

The mode a backend needs: :css for Visualize.Backend.SVG, :literal for any other.

default/0 with the given fields replaced.

The slot's value: the stored literal, or a CSS custom-property reference that falls back to it (spec/08 §7.3).

The :series_k slot the positive index i cycles to over the theme's series.

The theme's slot names: :series_1 to :series_n, then the fixed slots.

The .vis-theme-<name> CSS rule declaring every slot of the theme, one line each.

Types

mode()

@type mode() :: :css | :literal

How a slot renders: a CSS reference with fallback, or the stored value.

slot()

@type slot() :: atom() | {:series, pos_integer()}

A slot name, or {:series, i} for the i-th series colour, cycling.

t()

@type t() :: %Visualize.Theme{
  axis: String.t(),
  background: String.t(),
  font_family: String.t(),
  font_size: number(),
  grid: String.t(),
  label_size: number(),
  name: atom(),
  series: [String.t(), ...],
  surface: String.t(),
  text: String.t(),
  title_size: number()
}

Functions

colour_slots(theme)

@spec colour_slots(t()) :: [atom(), ...]

The theme's colour slots — the series, then axis, grid, text, background and surface (spec/08 §7.1).

css_name(slot)

@spec css_name(atom()) :: String.t()

The custom-property name of a slot atom: --vis- and the slot with _ as -.

dark()

@spec dark() :: t()

The dark theme: light text and axes on #1b1e24 (spec/08 §7.2).

default()

@spec default() :: t()

The light theme: dark text and axes on white (spec/08 §7.2).

ink(theme, under)

@spec ink(t(), String.t() | Visualize.Color.t() | nil) :: String.t()

The contrast ink for a text drawn on under (spec/08 §7.3, D-123): the literal of the theme's text or background, whichever has the higher WCAG contrast ratio against it — text on a tie — or #000000/#ffffff, whichever contrasts more, when neither slot reaches 4.5:1. One of black and white always does, so the ink always meets WCAG AA.

under is a colour string Visualize.Color.parse/1 reads, a %Visualize.Color{}, or nil for nothing under the text — the theme's own background. An unreadable string is nil, and a slot whose own value does not parse is skipped.

iex> theme = Visualize.Theme.default()
iex> Visualize.Theme.ink(theme, "#3b6fa8")
"#ffffff"
iex> Visualize.Theme.ink(theme, "#ffd098")
"#333333"
iex> Visualize.Theme.ink(theme, nil)
"#333333"
iex> Visualize.Theme.ink(theme, "#777777")
"#000000"

merge(theme, fields)

@spec merge(t(), keyword() | map()) :: t()

The theme with the given fields replaced; the checks of new/1 apply.

mode(backend)

@spec mode(module() | atom() | nil) :: mode()

The mode a backend needs: :css for Visualize.Backend.SVG, :literal for any other.

Takes what Visualize.Backend.resolve/1 takes: a module, :svg, :canvas or nil.

new(fields)

@spec new(keyword() | map()) :: t()

default/0 with the given fields replaced.

An unknown field raises KeyError; an empty series raises ArgumentError.

resolve(theme, slot, mode)

@spec resolve(t(), slot(), mode()) :: String.t() | number()

The slot's value: the stored literal, or a CSS custom-property reference that falls back to it (spec/08 §7.3).

Sizes are numbers under :literal and gain px under :css.

series_slot(theme, i)

@spec series_slot(t(), pos_integer()) :: atom()

The :series_k slot the positive index i cycles to over the theme's series.

slots(theme)

@spec slots(t()) :: [atom()]

The theme's slot names: :series_1 to :series_n, then the fixed slots.

stylesheet(theme)

@spec stylesheet(t()) :: String.t()

The .vis-theme-<name> CSS rule declaring every slot of the theme, one line each.

Include it in a stylesheet and put class="vis-theme-<name>" on any ancestor of the charts that should take the theme.