Icon system · 18 marks · v1.0

Tidewatch

One hand across three alphabets — organic weather, mechanical instruments and interface controls — drawn on a single 24 grid for coastal tide and weather stations.

weight is a CSS variable, never a path attribute

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

Station 07 — Cape Ferrierupdated 4 min ago
2.4 mSwell · 9 s
18 knWind · SW
1.82 mTide · rising
14.1 °CWater temp
1004 hPaPressure · falling
6.2 mmRain · 1 h
WaxingMoon · 34% lit
40%Cloud cover

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

3 stations above threshold
aria-hidden="true" — the icon repeats what the adjacent text already says. No role, no label, no duplicate announcement.
1.82 m
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.