The set
Eighteen symbols · every mark is an authored path, arc or circle · no raster, no glyphs, no transforms
The row test
all eighteen at 24 px, one line — the only test that matters
Read as a row rather than one at a time, the mechanical marks sit at the same optical weight as the organic ones because every group uses the same stroke, the same corner radius and the same 2-unit minimum feature. Nothing in the set is filled; density is carried by line rhythm alone.
Grid & construction
the rules every mark obeys
- Grid24 × 24 units. One unit is one pixel at the base size; everything else is derived from it.
- Live area20 × 20, centred. Nothing is drawn outside 2 → 22, so no mark ever clips at a viewport edge.
- Stroke1.75 units, round cap, round join, on every path in every icon. No group is heavier than another.
- Corners2-unit radius on every rectangle; triangle apexes are rounded with the same 2-unit arc.
- OvershootKeyline circle is ⌀18.25, keyline square 17.25. A circle drawn to the same nominal size as a square reads smaller, so it overshoots by 1 unit — 5% of the live area.
- DensityNo element shorter than 2 units, no more than 24 path commands per symbol. Fewer, longer curves beat many short segments.
- WeightStroke width lives in
--ico-sw, read by.ico. The sprite itself carries no colour and no weight.
Size ramp
16 · 20 · 24 · 32 · 48 · 96 px — same artwork throughout
The smallest feature anywhere in the set is 2 units, which is ≈1.3 px at 16 px — large enough to survive a 1× display. Only one symbol is dense enough to ship a second form, and it is shown below.
Small-size form
one variant, one rule
Anemometer — standard · 24 px and up
Anemometer — small form · 16–20 px
The three-cup rotor is the only place in the set where a diagonal arm and a 1.6-unit cup arc crowd each other. Below 24 px it is redrawn on the orthogonal axes: three spokes instead of four, no hub circle, cups opened to a readable size. Same silhouette, half the commands.
Light & dark
one sprite, one colour source — currentColor
Paper · ink on light
Chart · ink on dark
No second copy exists. Every symbol is stroked in currentColor, so a
single color declaration re-inks the whole set — including inside the dark panel,
which only redefines its own custom properties.
In context
a station card, using fourteen of the eighteen
Swell warning — 4.1 m predicted for 03:40, above the 3.5 m watch threshold for this anchorage.
Usage
drop the sprite in once, reference by id
<!-- 1 · the sprite, once per document -->
<svg class="sprite" aria-hidden="true" width="0" height="0">
<symbol id="i-swell" viewBox="0 0 24 24"><title>Swell</title> … </symbol>
</svg>
<!-- 2 · decorative: hidden from assistive tech -->
<svg class="ico" aria-hidden="true"><use href="#i-swell"></use></svg>
<!-- 3 · meaningful: named, with the value in the label -->
<svg class="ico" role="img" aria-label="Swell height, 2.4 metres">
<use href="#i-swell"></use>
</svg>
/* one rule drives every icon in the set */
.ico{
--size: 1.5rem;
width: var(--size); height: var(--size);
fill: none; /* nothing in the set is filled */
stroke: currentColor; /* re-inked by an ancestor's color */
stroke-width: var(--ico-sw, 1.75); /* weight set from CSS, not the path */
stroke-linecap: round;
stroke-linejoin: round;
}
Accessibility
two rules, applied per instance
aria-hidden="true" — the icon repeats what the adjacent text already
says. No role, no label, no duplicate announcement.role="img" plus aria-label — the icon is the only carrier
of the reading, so it names itself and states the unit.Each <symbol> also carries a <title> with the
mark’s canonical name, so a direct <use> is never anonymous and the sprite
stays self-documenting. Where an icon is both decorative and interactive, the label lives on
the <button> and the sprite stays hidden.