A theme is a plain map of typographic and geometric defaults. Every figure carries one, and every renderer lookup reads through it — meaning that shifting "the whole plot should feel more like a blueprint" is a single line change.
Built-in themes
:report_1972 (default)
- Serif body text (Times-stack)
- Thin black rules, framed plot area, framed legend
- Inward-facing tick marks
- Page border around the full canvas
- Uppercase titles with letter-spacing — the look of a NASA-contractor report typeset on an IBM Selectric
:blueprint
- Monospace body text (Courier-stack)
- Thicker strokes (1.4 px axes, 1.4 px series)
- No plot frame — just L-shaped axes
- Dense page border
- Evokes pencil-on-graph-paper working drawings
:gazette
- Georgia serif body
- Larger tick and axis labels
- Mixed-case titles
- Framed plot area, no page border
- Newspaper-science-column feel
Applying a theme
Pass a preset atom to Bland.figure/1:
Bland.figure(theme: :blueprint)Or a map to override on top of a preset:
Bland.figure(theme: Bland.Theme.merge(:report_1972, %{
title_font_family: "IBM Plex Serif",
grid_dasharray: "1 6"
}))The theme map
Bland.Theme.default/0 is the full reference for what keys exist. The
ones you'll reach for most:
Typography
| Key | Default | Purpose |
|---|---|---|
:font_family | "Times, 'Liberation Serif', serif" | Base text |
:title_font_family | same | Figure title |
:label_font_family | same | Axis labels, ticks |
:title_font_size | 14 | Figure title px |
:subtitle_font_size | 11 | Subtitle px |
:axis_label_font_size | 11 | xlabel / ylabel px |
:tick_label_font_size | 9 | Tick labels px |
:legend_font_size | 10 | Legend text px |
| :title_transform | :upcase | :none | :upcase | :downcase |
| :title_letter_spacing | "0.05em" | Kerning for figure title|
Strokes & rules
| Key | Default | Purpose |
|---|---|---|
:axis_stroke_width | 1.0 | Axis bars (when frame: false) |
:frame_stroke_width | 1.0 | Plot-area frame |
:grid_stroke_width | 0.4 | Grid lines |
:grid_dasharray | "2 3" | Grid dashing |
:series_stroke_width | 1.2 | Default series stroke weight |
:tick_length | 5 | Tick length (px) |
| :tick_direction | :in | :in | :out | :both |
| :tick_stroke_width | 1.0 | Tick weight |
Layout toggles
| Key | Default | Purpose |
|---|---|---|
:frame | true | Draw a rectangle around the plot area |
:border | true | Draw a border around the whole canvas |
:border_inset | 12 | Border offset from canvas edge (px) |
:legend_frame | true | Frame the legend box |
Examples
A more formal report look
formal = Bland.Theme.merge(:report_1972, %{
title_transform: :none,
title_font_size: 18,
grid_dasharray: "1 4",
grid_stroke_width: 0.3
})
Bland.figure(theme: formal, title: "Figure 3-7. Flux density vs wavelength")Minimal — no frame, no grid, no border
minimal = %{
frame: false,
border: false,
grid_stroke_width: 0,
tick_direction: :out
}
Bland.figure(theme: minimal)
|> Bland.axes(xlabel: "n", ylabel: "f(n)", grid: :none)
|> Bland.line([1, 2, 3, 4], [1, 4, 9, 16])Blueprint with a darker paper tint
paper = Bland.Theme.merge(:blueprint, %{
background: "#f4f2e8" # aged-paper cream
})
Bland.figure(theme: paper)(Yes, BLAND lets you set a non-white background — but for true paper
output, leave it "white".)
Theme introspection
iex> t = Bland.Theme.get(:blueprint)
iex> t.font_family
"'Courier New', 'Liberation Mono', monospace"
iex> t.series_stroke_width
1.4If you're building a house style for a project, define it once as a
plain map module attribute and pass it to every Bland.figure/1 call.