Configuration
Plumb reads plumb.toml from the working directory by default. Pass
--config <path> to plumb lint to override. A starter file is
written by plumb init; the canonical example is
examples/plumb.toml
in the repo.
This page is the full reference. Every section is optional. When you
omit a section, Plumb uses that section’s defaults. Rules with usable
defaults can still run — for example spacing/grid-conformance,
edge/near-alignment, and a11y/touch-target. Rules that need a
non-empty scale or token list skip when that input is empty.
Editor autocomplete
Generate the canonical JSON Schema and point your editor at the local file for inline validation and hover docs:
plumb schema > plumb.schema.json
VS Code with the Even Better TOML extension:
// .vscode/settings.json
{
"evenBetterToml.schema.associations": {
"plumb.toml": "./plumb.schema.json"
}
}
JetBrains: open Settings → Languages & Frameworks → Schemas and DTDs →
JSON Schema Mappings, and add plumb.schema.json against the file
pattern plumb.toml.
The schema is for editor association only. Do not add a $schema
field to plumb.toml or any JSON config file; Plumb rejects unknown
configuration fields.
Top-level shape
[viewports.<name>] # one or more required for real URLs
[spacing] # spacing scale and tokens
[type] # type scale, families, weights, tokens
[color] # named color tokens + ΔE tolerance
[radius] # allowed border-radius values
[alignment] # grid spec + near-alignment tolerance
[shadow] # allowed box-shadow values
[z_index] # allowed z-index values
[opacity] # allowed opacity values
[rhythm] # vertical-rhythm baseline and tolerance
[a11y] # contrast + touch target
[rules."<id>"] # per-rule overrides
[[ignore]] # selector-scoped runtime suppressions
If [viewports.*] is omitted, Plumb defaults to a single desktop
viewport at 1280×800. Real runs SHOULD declare every viewport
explicitly.
[viewports.<name>]
[viewports.mobile]
width = 375
height = 667
device_pixel_ratio = 2.0
[viewports.desktop]
width = 1280
height = 800
device_pixel_ratio = 1.0
Each named viewport is a snapshot target. width and height are CSS
pixels. device_pixel_ratio is optional; defaults to 1.0. The
viewport name appears in the rule output ([mobile], [desktop])
so name them after how you’ll read the report.
[spacing]
[spacing]
base_unit = 4
scale = [0, 4, 8, 12, 16, 24, 32, 48]
tokens = { xs = 4, sm = 8, md = 16, lg = 24, xl = 32, "2xl" = 48 }
| Field | Type | Default | Meaning |
|---|---|---|---|
base_unit | u32 | 4 | Grid base for spacing/grid-conformance. |
scale | [u32] | [] | Allowed discrete spacing values. Empty disables spacing/scale-conformance. |
tokens | {string => u32} | {} | Named aliases. Slash-prefixed names act as namespaces. |
Consumed by spacing/grid-conformance and
spacing/scale-conformance.
[type]
[type]
families = ["Inter", "system-ui"]
weights = [400, 500, 600, 700]
scale = [12, 14, 16, 18, 20, 24, 30, 36, 48]
tokens = { caption = 12, body = 16, heading = 24 }
| Field | Type | Default | Meaning |
|---|---|---|---|
families | [string] | [] | Allowed font-family values. Empty skips the family check. |
weights | [u16] | [] | Allowed font-weight numeric values. |
scale | [u32] | [] | Allowed font-size values in CSS pixels. |
tokens | {string => u32} | {} | Named font-size aliases. |
Consumed by type/scale-conformance.
[color]
[color]
tokens = { "bg/canvas" = "#ffffff", "fg/primary" = "#0b0b0b", "accent/brand" = "#0b7285" }
delta_e_tolerance = 2.0
| Field | Type | Default | Meaning |
|---|---|---|---|
tokens | {string => hex} | {} | Named palette colors. Slash-delimited names group by prefix in diagnostics. |
delta_e_tolerance | f32 | 2.0 | CIEDE2000 ΔE threshold for color/palette-conformance. |
Consumed by color/palette-conformance.
[radius]
[radius]
scale = [0, 2, 4, 8, 12, 16, 9999]
| Field | Type | Default | Meaning |
|---|---|---|---|
scale | [u32] | [] | Allowed border-radius values. Empty disables the rule. |
Consumed by radius/scale-conformance. The sentinel 9999 is the
conventional “fully rounded pill” value.
[alignment]
[alignment]
grid_columns = 12
gutter_px = 24
tolerance_px = 3
| Field | Type | Default | Meaning |
|---|---|---|---|
grid_columns | u32? | null | Number of grid columns, if you use one. |
gutter_px | u32? | null | Gutter width in CSS pixels. |
tolerance_px | u32 | 3 | Edge-clustering window for edge/near-alignment — elements off by 0 < delta <= tolerance_px get flagged. |
Consumed by edge/near-alignment.
[shadow]
[shadow]
scale = [
"none",
"0 1px 2px rgba(0, 0, 0, 0.05)",
"0 4px 8px rgba(0, 0, 0, 0.08)",
"0 12px 24px rgba(0, 0, 0, 0.12)",
]
| Field | Type | Default | Meaning |
|---|---|---|---|
scale | [string] | [] | Allowed box-shadow values. Each entry MUST match the exact string returned by getComputedStyle. Empty disables the rule. |
Consumed by shadow/scale-conformance.
[z_index]
[z_index]
scale = [0, 10, 100, 1000]
| Field | Type | Default | Meaning |
|---|---|---|---|
scale | [i32] | [] | Allowed z-index values. Negative integers are accepted. Empty disables the rule. |
Consumed by z/scale-conformance.
[opacity]
[opacity]
scale = [0.0, 0.5, 0.75, 1.0]
| Field | Type | Default | Meaning |
|---|---|---|---|
scale | [f32] | [] | Allowed opacity values in the closed range [0.0, 1.0]. Empty disables the rule. |
Consumed by opacity/scale-conformance.
[rhythm]
[rhythm]
base_line_px = 8
tolerance_px = 2
cap_height_fallback_px = 0
| Field | Type | Default | Meaning |
|---|---|---|---|
base_line_px | u32 | 0 | Vertical-rhythm grid step in CSS pixels. 0 disables the rule. |
tolerance_px | u32 | 2 | Pixels of drift allowed before a baseline is reported off-rhythm. |
cap_height_fallback_px | u32 | 0 | Cap-height value to use when the snapshot lacks font metrics. 0 keeps the rule’s built-in heuristic. |
Consumed by baseline/rhythm.
[a11y]
[a11y]
min_contrast_ratio = 4.5
[a11y.touch_target]
min_width_px = 24
min_height_px = 24
| Field | Type | Default | Meaning |
|---|---|---|---|
min_contrast_ratio | f32? | null | Optional stricter global floor for color/contrast-aa. The rule still keeps WCAG AA’s built-in 4.5:1 normal / 3.0:1 large defaults. |
touch_target.min_width_px | u32 | 24 | Minimum touch-target width per WCAG 2.5.8. Raise to 44 for AAA. |
touch_target.min_height_px | u32 | 24 | Minimum touch-target height. |
Consumed by a11y/touch-target and color/contrast-aa.
[[ignore]]
Selector-scoped runtime suppressions. Each [[ignore]] block silences
every violation whose CSS selector path matches selector exactly
(string equality only — Plumb does not run a CSS engine over the
snapshot). When rule_id is set, the suppression is constrained to
that single rule; when rule_id is omitted, every rule fired at the
selector is suppressed. Suppressed violations are partitioned out of
the report and counted under ignored rather than silently dropped.
[[ignore]]
selector = "html > body"
rule_id = "spacing/grid-conformance"
reason = "mdBook root padding is theme-controlled"
[[ignore]]
selector = "main > article"
reason = "vendor widget styles its own column"
| Field | Type | Default | Meaning |
|---|---|---|---|
selector | string | required | Exact SnapshotNode.selector path to suppress. |
rule_id | string? | null | Optional rule id (e.g. spacing/grid-conformance). When omitted, all rules at selector are suppressed. |
reason | string | required | Human-readable justification. Documents why the exemption is intentional. |
plumb lint --suggest-ignores emits one suggestion per active
violation in the same shape — pipe its output into [[ignore]] blocks
to converge on a clean baseline. See
--suggest-ignores for the full footer
format.
[rules."<category>/<id>"]
Per-rule overrides. Every rule is enabled by default at its declared severity.
[rules."spacing/grid-conformance"]
severity = "error"
[rules."edge/near-alignment"]
enabled = false
| Field | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Disable the rule entirely. |
severity | "error" | "warning" | "info" | rule-defined | Promote or demote the severity. |
Use plumb explain <category>/<id> (or the
Rules chapter) for per-rule docs.
Schema and plumb init
plumb init writes the starter plumb.toml shown above. Pass
--force to overwrite an existing file.
plumb schema prints the canonical JSON Schema on stdout. Pipe it to
disk and point your editor at it for autocomplete:
plumb schema > plumb.schema.json
The schema MUST round-trip: every field documented above appears in
the schema with the same defaults and the same constraints. CI checks
this against the rendered examples/plumb.toml.
Where to go next
- CLI — flags and exit codes.
- Rules — per-rule reference.
- Quick start — the five-minute path if you skipped it.