Hatching is how BLAND replaces color. Every filled region — bar, area — takes a pattern instead of a fill color. Patterns stay distinguishable after photocopy, on grayscale printers, and for readers with color vision deficiency.
The preset gallery
Bland.Patterns.preset_cycle/0 returns these in the order the renderer
walks when a series doesn't name a :hatch explicitly. The ordering is
tuned for maximum visual separation at each step: light hatches
alternate with darker ones, orientation alternates too, and solid-fill
presets sit at the ends of the list.
| Preset | Description |
|---|---|
:solid_white | Paper color (use as a foil behind darker series) |
:diagonal | 45° rules, medium spacing |
:anti_diagonal | 135° rules, medium spacing |
:horizontal | Horizontal rules |
:vertical | Vertical rules |
:crosshatch | Both diagonals — high visual weight |
:dots_sparse | Small dots on a wide grid |
:grid | Square grid (horizontal + vertical, not crossed) |
:brick | Running-bond brick pattern |
:dots_dense | Small dots on a tight grid |
:zigzag | Repeating zigzag line |
:dashed_h | Interrupted horizontal dashes |
:diagonal_dense | Tightly-packed 45° rules |
:checker | 8×8 checker tiling — high weight |
:solid_black | Full fill |
Usage
Pick by name:
Bland.bar(fig, ["A", "B", "C"], [3, 5, 2], hatch: :crosshatch, label: "set 1")Or let the renderer cycle:
# No :hatch given → :solid_white, then :diagonal, then :anti_diagonal, …
fig
|> Bland.bar(cats, run_a, label: "A")
|> Bland.bar(cats, run_b, label: "B")
|> Bland.bar(cats, run_c, label: "C")Choosing patterns
Some practical guidance, gathered from cartography and technical drafting conventions:
- Lighter patterns (dots, sparse lines) for larger regions. A
big bar with
:crosshatchwill visually dominate everything else on the plot. - Orient hatching differently for adjacent regions.
:diagonalnext to:anti_diagonalreads clearly even in a greyscale photocopy. - Reserve solid fills for emphasis. Solid black draws the eye immediately; use it for the one series you want the reader to see first.
- Keep stroke weight consistent across fills. The default pattern library draws at 1 px, matching BLAND's default series stroke weight — change one and you should change the other.
Custom patterns
Call Bland.Patterns.define/4 with a DOM id, tile size, and SVG body:
# A pattern of tiny horizontal tick marks
Bland.Patterns.define("my-ticks", {6, 6},
~s|<line x1="0" y1="3" x2="6" y2="3" stroke="black" stroke-width="1"/>|
)To use a custom pattern, insert the pattern's <defs> into the
generated SVG yourself and reference it via fill="url(#my-ticks)".
The high-level :hatch option on Bland.bar/4 expects a preset atom, so
custom patterns are an "eject into raw SVG" escape hatch.
If you find yourself reaching for the escape hatch often, open an issue — the preset set is meant to grow.
Stroke dashes
Lines don't have a fill, but they still need to be distinguishable.
BLAND ships six dash presets, cycled by Bland.Strokes.preset_cycle/0:
| Preset | Dasharray |
|---|---|
:solid | (none) |
:dashed | 6 3 |
:dotted | 1 3 |
:dash_dot | 6 3 1 3 |
:long_dash | 12 4 |
:fine | 2 2 |
:dash_dot is the classic centerline / phantom line from mechanical
drafting — useful for reference traces that should visually sit
behind the primary curves.
Override with a raw SVG dasharray if you need something off-preset:
Bland.line(fig, xs, ys, stroke: "4 2 1 2")Markers
Scatter and marked lines pull from Bland.Markers.preset_cycle/0:
:circle_open, :square_open, :triangle_open, :diamond_open,
:cross, :plus, :circle_filled, :square_filled,
:triangle_filled, :diamond_filled, :asterisk, :dot.
The ordering is alternating-open-then-filled so two adjacent series look distinct by shape and fill.
Bland.scatter(fig, xs, ys,
marker: :triangle_filled,
marker_size: 5,
label: "trial 3"
)A full reference example
cats = ["Q1", "Q2", "Q3", "Q4"]
fig =
Bland.figure(size: :a5_landscape, title: "Pattern reference")
|> Bland.axes(ylabel: "count")
|> Bland.bar(cats, [10, 12, 15, 11], label: ":diagonal", hatch: :diagonal, group: 1)
|> Bland.bar(cats, [8, 14, 16, 13], label: ":crosshatch", hatch: :crosshatch, group: 2)
|> Bland.bar(cats, [12, 11, 14, 17], label: ":dots_sparse", hatch: :dots_sparse, group: 3)
|> Bland.bar(cats, [9, 13, 12, 15], label: ":horizontal", hatch: :horizontal, group: 4)
|> Bland.legend(position: :top_left, title: "Fill")
Bland.write!(fig, "patterns.svg")