Components
Annotation
Naming a part of a drawing. The system could already mark a found word and it could already draw an object; it had nothing in between — no way to say which part of an object you are looking at. Fifteen isometric objects ship with construction nodes on them, a black dot at a vertex, and not one of those dots said anything. This is the layer that gives them words.
The callout
An anchor on the drawing, a leader that is one lattice step, and a note standing on its own rule. Every annotation is drawn in contour; the one the reader is on is lit.
- Lit face
- Side face
- Lattice cell 1 u
<figure class="cf-annot-fig"> <!-- the drawing. It fills the figure, so --annot-x / --annot-y are fractions of its own viewBox and nothing has to be converted --> <svg class="cf-iso" viewBox="0 0 640 400" fill="none" aria-hidden="true">…</svg> <ul class="cf-annot-set"> <!-- x = 200 / 640, y = 180 / 400 — the vertex the note points at --> <li class="cf-annot cf-annot--ne cf-annot--lit" style="--annot-x:31.25%; --annot-y:45%"> <span class="cf-annot__label">Lit face</span> </li> <!-- with an optional measure after the note --> <li class="cf-annot cf-annot--se" style="--annot-x:68.75%; --annot-y:75%"> <span class="cf-annot__label">Lattice cell <span class="cf-annot__value">1 u</span></span> </li> </ul> </figure>
| Part | Required | What it is |
|---|---|---|
.cf-annot-fig | yes | A <figure>. The positioning context and the container the layer is measured against. Give it no padding and no aspect ratio of its own — the drawing has to fill it exactly, or the percentages stop meaning viewBox fractions. |
.cf-annot-set | yes | A <ul> laid over the drawing. Owns --annot-u and is the scroll subject the leaders are drawn against. pointer-events: none, so it never sits between the reader and anything below it. |
.cf-annot | yes | One <li>, one note. Carries --annot-x and --annot-y: where on the drawing it points, as a percentage of the figure. |
.cf-annot--ne --se --nw --sw | yes | Which way the leader runs. One of the four, always — the default is --ne. |
.cf-annot--steep | no | Puts the leader on the steep isometric, 63.43°, instead of the shallow 26.57°. Composes with all four directions. |
.cf-annot--lit | no | Fills the anchor cell with the light ramp. At most one per figure, and only when a figure has a subject. |
.cf-annot__label | yes | The note. Mono, 11 px, uppercase, --text-primary, standing on its own rule — that rule is the shoulder the leader runs into. |
.cf-annot__value | no | A measure after the note. Tabular figures, --text-secondary. Goes inside the label so both stand on the one rule. |
| Property | Default | What it does |
|---|---|---|
--annot-x / --annot-y | 50% | Per annotation. Where the anchor sits, as a fraction of the figure — which is a fraction of the viewBox, so read the coordinate off the drawing and divide. |
--annot-u | clamp(0.875rem, 3.2cqi, 1.75rem) | The lattice unit the leader is measured in. Reads the container, not the viewport: the same drawing in a narrow column gets a shorter leader. |
--annot-mx / --annot-my | 2 / 1 | The step, in lattice units. The two sanctioned values are (2, 1) and (1, 2); the angle is read off them. |
--annot-node | 0.5rem | The anchor cell's width before the isometric transform squashes it. |
--annot-draw | 1 | Set by the scroll animation, read by the leader and the label. 1 is the finished annotation, which is also what everything that cannot animate gets. |
Four directions, two slopes
A leader may run in eight ways and no others. Technical drawing has required for a century that a leader be neither horizontal nor vertical, so the reader can tell it from the object it points at; this brand sanctions four angles, two of which are oblique. The two rules land on the same drawing.
- nw
- ne
- sw
- se
- nw steep
- ne steep
- sw steep
- se steep
atan2(--annot-my, --annot-mx). (2, 1) resolves to 26.565° and (1, 2) to
63.435°: the only two obliques the brand owns, and the only two a leader can be. A
leader that is off the lattice is not expressible.
This is the first place the system states an isometric angle as the ratio it has always been rather than as a rounded degree. It is worth saying what that buys and what it does not: 26.57° against 26.565° is 0.005° and no screenshot will ever show it. What it buys is that the two numbers cannot come apart — change the step and the slope follows, where before a leader could be moved onto a different step and keep the old angle. Trigonometric functions have been Baseline since March 2023 and
sqrt() since December 2023; both literals stay in place as the fallback,
because an angle is not a place to find out a browser is old.
scaleX(±1) ahead of its rotation, so --nw is
--ne reflected rather than a separately stated −153.435°. Four
directions, one angle, and no arithmetic anywhere that could put the left pair on a
slope the right pair is not on.
How it is constructed
Nothing here is a new shape. The anchor is the lattice cell, the leader is a lattice step, the shoulder is the label rule the section header and the prose note already use, and the light is the light layer doing the job the manual gives it.
bottom: u × my × −sy — puts the underline at the end of the leader
in all four directions, which is why the four directions need no separate label rules
between them.
--iso-transform, so the mark at 8 px is the same 2:1 rhombus the ground
is tiled with at 120 — not a dot, and not a 45° diamond, which is what a square turned
45° would give and what the isometry is not. It is drawn out of border
rather than a fill for the reason the radio's mark is: forced-colours mode discards
background-color and maps border colours into its own palette. In forced
colours the lit rung loses its ramp and every annotation is drawn identically, which
is correct — the lit rung is emphasis, and emphasis is exactly what that mode is
allowed to drop. That mode also gets the finished annotation rather than a
frame of one: it exists to guarantee legibility, and an opacity ramp is the one thing
it cannot guarantee.
--annot-u reads cqi off the figure, so the leader is a
proportion of the object it points at. A 640-unit drawing in a 400 px column and the
same drawing full-bleed get leaders that look the same length relative to what they
are labelling, which a viewport unit cannot do.
Two rungs: contour, and the one you are on
This is the found state one scale up. There it is a word: every match carries the ground line, the current one carries light behind it. Here it is a part of an object: every annotation is drawn in contour, and the one the figure is about carries the light in its anchor cell.
That settles the lime budget without an exception, the same way the found state does. One lime moment per screen is a rule about composition, and a figure with six annotations composes one lit anchor and five contoured ones. It also settles what the lit rung may mean: it is the subject, never a category. Two lit anchors is a legend carried by a colour, in a component that has no legend.
What makes it readable
| Rule | Why |
|---|---|
The notes are a list, and the drawing is aria-hidden |
Each annotation is real text in a real <li>, so the figure
reaches the accessibility tree as a list of the parts it names. There is no
second copy of that text to drift out of step with the picture — the same
posture as the plot.
|
| Below 28 rem of figure the layer becomes a legend | A callout needs clear ground to point into and a 320 px drawing has none. Rather than overlap two notes, the set flows under the figure as a row of marks and words. The label rule goes with the leader: an underline with no line running into it is not a label rule, it is decoration — and at that size it would be mistaken for a link. |
| Nothing is carried by the lit rung alone | The lit anchor marks the subject the caption is already about in words. Never use lit and unlit to mean two kinds of thing. |
| Contrast is carried by the contour |
Labels are --text-primary (11.9:1 on the wash at its darkest end)
and the leader and anchor are --border-strong, black — well past
the 3:1 a UI contour owes. The lime in a lit anchor is a fill inside a black
contour, so it carries none of that figure.
|
The leader draws itself
As the figure settles, each leader grows out of its anchor and its note fades up with
it. One registered number, --annot-draw, is animated on a view timeline
and read by both, so the line and the word cannot fall out of step.
The range opens at 45 % of the figure's cover — after .cf-iso has
arrived (5–30 %) and after its own construction nodes have faded up (35–50 %). The
drawing is finished before it is labelled, which is the order the two things happen in
on paper. It is scrubbed from scroll position rather than played on a timer, so
scrolling back up un-draws the leaders, and every path that cannot run it —
no @property, no view timeline, reduced motion, print — lands on the
finished annotation.
Do and don't
| Do | Don't |
|---|---|
| Point at a feature the drawing actually draws — a vertex, an edge, a face. | Point at empty space, or at a part the object does not distinguish. A leader that terminates on nothing is a line that is neither an edge, a division nor a label rule. |
| Keep it to two or three notes on one object. | Label every vertex. An annotated drawing that needs eight notes is a diagram that has not been simplified yet. |
| Send the leader into clear ground. | Run a note across the object it is labelling, or across another leader. The component places what it is told to place; where there is room is a drawing decision. |
| Set the note in one or two words. | Write a sentence. The label is the mono face at 11 px uppercase; prose belongs
in the <figcaption>. |
| Light the anchor the figure is about, if the drawing has not already spent its light. | Light two, or light one on a drawing that already carries a lit element. |
Still open
-
No plate in
assets/source/manual/draws a callout. The 22 plates in the curated slice cover the icon system, the line types, the materials, the lattice and the colour system, and none of them annotates a drawing. Everything here is therefore derived from parts the manual does establish — the lattice cell, the two oblique angles, the label rule, the light layer, the presence ladder — and from drafting convention where the manual is silent. If the full archive holds a plate that annotates an object, that plate wins. - The pending rung is not drawn. The presence ladder would say a note that has not arrived is the same note one rung down — the leader in 1-4 rather than solid. Nothing dynamic needs it yet, and a state with no consumer is a drawing nobody has looked at.
- No multi-segment leader. Drafting allows a leader to turn once on its way to the note. Two lattice steps in series would stay inside the language, and nothing has needed the reach yet.
- Placement is authored, not solved. The component knows nothing about what is under a note. A collision detector would need to measure rendered geometry, which needs a browser — the same boundary the checkers draw between what a script can settle and what it cannot.