Skip to contentWolf-Rayet

Decision records

ADR-0096 An icon set, a grid, and no em dash in a headline

Accepted2026-09-16Phase 6

#Context

The docs site listed three foundations the system had not built: Grid, Icons and Pictograms. The foundation bodies record why they stayed as named gaps rather than being dropped: this system needs a grid and an icon set. ADR-0095 named those two as the clearest open build work.

Separately, the maintainer ruled out em dashes in copy and headlines: a reader takes them as a sign of machine-written text. The site's own template wrote one into every page title, every table's empty cell and every generated section label, and 108 headings in the records used one.

#Decision

#The icon set

@wolf-rayet/icons is a new package: fifty glyphs drawn from one geometry file. Each icon is lines, circles, rectangles, polylines and paths on a 24-unit square, inside a live area from 2 to 22, with one stroke width, round caps and joins, and no fill. A generator writes a typed module, one SVG file per icon and a receipt. Icon renders a glyph in currentColor at 1em, so it takes the colour and size of the text it sits in.

An icon is not a component. It holds no allocation, declares no level and paints nothing the budget prices apart from the element that carries it. It is hidden from assistive technology unless given a label, because an icon beside a word repeats the word.

Icons name places, objects and actions, never urgency. The generator refuses any name containing a shape from the attention vocabulary (triangle, octagon, check, bar, chevron, tail) or a word about attention (alert, warning, error, bell and the rest listed in the receipt). It refused one of the first fifty, chart-bar, which is now chart-column.

Every icon is measured in a browser. icons:test draws the set in Chromium and holds each glyph's geometry inside the live area with getBBox, so a curve is judged by where it lands. It was seen failing on a planted point before it was trusted, and it writes its verdict as a receipt. It runs in the budget workflow and the battery. The package's lib/ and svg/ are committed output, rerun by build:check.

#The grid

The token generator emits a layout grid: 4, 8, 12 and 16 columns at sm, md (42rem), lg (66rem) and xl (82rem), a 99rem maximum, and gutter and margin that name spatial tokens so density moves them. css/grid.css sets --wr-grid-columns, --wr-grid-gutter, --wr-grid-margin and --wr-grid-max, redefined inside one media query per breakpoint, because a media query cannot read a token. receipts/grid.json records every breakpoint in both densities.

A column may not be narrower than target-min at the narrowest width a breakpoint admits (20rem for the first), in either density. The generator refuses a grid that breaks it; six columns at 20rem was refused before the rule was trusted. The narrowest column in the shipped grid is 3.641rem against 3.375rem.

The docs site uses both: its page width and margins are the grid's, and its links to other sites carry the external-link icon. The Grid and Icons foundation pages now show the grid at the window's own width and the whole set with each icon's meaning.

#No em dash

  • Content lint gains a ninth rule, em-dash, for every string a

component ships.

  • docs:check-site gains a nineteenth check: no page title, heading or

table header on any page carries one. It failed on 469 headlines before the template and the records were changed.

  • **The template's separators are colons, its page titles use a middle dot,

and an empty cell says "none".** The 108 record headings use a colon. Record bodies keep the prose they were written in; new prose is written without.

#Rejected

  • Adopt an existing icon library. Its names and meanings would come from a

system that lets an icon say "warning", which is the one thing this set may not say.

  • Make Icon a roster component. It would need a level and a slot it does

not have, and every check that follows from them would be describing something that is not there.

  • Breakpoints as tokens. A media query cannot read one; stating them once,

in the generator's config, is the honest version.

  • Layout classes in the token tier. ADR-0017 keeps the tier to custom

properties; composing the grid is a few lines a reader writes.

  • Rewrite every em dash in every record. The records are history, and the

rule is about what a reader meets as copy and as a headline.