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

KeyDefaultPurpose
:font_family"Times, 'Liberation Serif', serif"Base text
:title_font_familysameFigure title
:label_font_familysameAxis labels, ticks
:title_font_size14Figure title px
:subtitle_font_size11Subtitle px
:axis_label_font_size11xlabel / ylabel px
:tick_label_font_size9Tick labels px
:legend_font_size10Legend text px

| :title_transform | :upcase | :none | :upcase | :downcase | | :title_letter_spacing | "0.05em" | Kerning for figure title|

Strokes & rules

KeyDefaultPurpose
:axis_stroke_width1.0Axis bars (when frame: false)
:frame_stroke_width1.0Plot-area frame
:grid_stroke_width0.4Grid lines
:grid_dasharray"2 3"Grid dashing
:series_stroke_width1.2Default series stroke weight
:tick_length5Tick length (px)

| :tick_direction | :in | :in | :out | :both | | :tick_stroke_width | 1.0 | Tick weight |

Layout toggles

KeyDefaultPurpose
:frametrueDraw a rectangle around the plot area
:bordertrueDraw a border around the whole canvas
:border_inset12Border offset from canvas edge (px)
:legend_frametrueFrame 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.4

If 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.