Components

Plot

A number, drawn in space. The system could already draw an isometric object and it could already set a figure in Geist Mono; it had no way to make one describe the other. Every column here is an extruded lattice cell — the cube from the icon set, pulled up by its value — standing on the 2:1 ground plane the whole brand is constructed on.

The column plot

Up to five values in a series. The drawing is the data: no axis, no legend, no grid of reference lines. Every column is labelled with its own number, which is what makes the figure readable rather than merely persuasive, and one column may be lit.

  1. Roh31 %
  2. Einheit52 %
  3. Zeitzone68 %
  4. Abtastung84 %
  5. Modell97 %
Anteil verwertbarer Signale je Aufbereitungsstufe. Erst nach der Modellierung steht die Datenbasis, auf der ein Zwilling überhaupt rechnen kann.
<figure class="cf-plot">
  <ol class="cf-plot__set">

    <!-- one <li> per value. --v is the value as a fraction of the tallest -->
    <li class="cf-plot__col" style="--v:.31">
      <span class="cf-plot__key">Roh</span><span class="cf-plot__val">31 %</span>
      <svg class="cf-plot__draw" viewBox="0 0 40 140" fill="none" aria-hidden="true">
        <path class="cf-plot__cell" d="M20 120 40 130 20 140 0 130Z"/>
        <path class="cf-plot__rise" d="M0 130V10"/>
        <path class="cf-plot__rise" d="M40 130V10"/>
        <path class="cf-plot__rise cf-plot__rise--near" d="M20 140V20"/>
        <path class="cf-plot__cap" d="M20 0 40 10 20 20 0 10Z"/>
      </svg>
    </li>

    <!-- the one lit column, if the figure has one -->
    <li class="cf-plot__col cf-plot__col--lit" style="--v:.97">…</li>

  </ol>
  <figcaption class="cf-plot__caption">Anteil verwertbarer Signale je Aufbereitungsstufe…</figcaption>
</figure>
PartRequiredWhat it is
.cf-plotyesA <figure>. Owns --plot-u and --plot-rise, and is the scroll subject the growth is timed against.
.cf-plot__setyesAn <ol> — the values are a series and their order carries meaning. A flex row pitched 4u.
.cf-plot__colyesOne <li>, one value. Carries --v: the value as a fraction of the tallest column, 0 to 1.
.cf-plot__col--litnoGives that column's cap the glow. The fill is a paint server the column carries in its own <defs>, because lime is never a flat fill and a CSS gradient cannot be an SVG fill — see Colour. At most one per screen.
.cf-plot__keyyesWhat the column is. Mono, 11 px, uppercase, --text-secondary. Sits under the cell.
.cf-plot__valyesThe number, with its unit. Mono, tabular, --text-primary. Rides the cap and travels with it.
.cf-plot__drawyesThe drawing. Five paths, always the same five, always the same viewBox — nothing in it is per-value. aria-hidden.
.cf-plot__cellyesThe lattice cell the column stands on. 1-4 dashed, --border-default.
.cf-plot__riseyesA vertical edge. Two of them, plus one --near that starts half a cell lower.
.cf-plot__capyesThe top face. The same rhombus as the cell, lifted by --v.
.cf-plot__captionyesA <figcaption>. What the figure means, in words. See below — this one is genuinely required.
PropertyDefaultWhat it does
--v0Per column. The value as a fraction of the tallest column. Registered as <number>.
--plot-uclamp(16px, 3.4vw, 32px)The half-width of a cell, and the unit every other measure is built from. Set it on .cf-plot to resize a figure. px, not rem — this figure is sized in the system's layout unit, not the reader's type size, so it does not grow with a browser's default font-size setting the way the floor briefly did.
--plot-risecalc(var(--plot-u) * 6)How tall a column at --v:1 stands. Changing it alone will desynchronise the number from its cap — see the note on the two rises.
--plot-grow1Set by the scroll animation, read by every part. 1 is the finished chart, which is also what everything that cannot animate gets.

How it is constructed

Nothing in the plot is a new shape. The cell is the lattice rhombus, the column is the icon set's cf-cube extruded past one unit, the dashes are the 1-4 ghost line type, the numbers are the treatment .cf-stat already gives every figure in the system, and the lime is the light layer doing exactly the job the manual gives it.

The row is level, and that is an isometric decision rather than a retreat from one. Every direction in the isometric ground plane slopes on screen: the two axes run at +26.57° and −26.57°. Marching columns along either one costs a unit of drawn height per column, and the first build of this component did exactly that — on five columns rising 31 → 100 the recession ate almost the entire climb, and the last column, more than three times the first, was drawn a hair lower on screen. A chart that reads flat when its data triples is not a stylistic preference; it is a broken instrument.

The fix is still a lattice vector. One step forward on the first axis plus one step back on the second is (2u, +u) + (2u, −u) = (4u, 0) — exactly horizontal on screen, and a point of the lattice like any other. The columns sit 4u apart on one level line, every one of them on a real cell, and cap height is once again proportional to the value alone. It is also the only spacing available: lattice points that share a screen y are 4u apart and nothing closer qualifies, so the 2u of air between two columns is derived rather than chosen.
Each column is its own SVG, and that is about contour weight. A 1 px hairline drawn as a CSS border inside a scaled box is scaled with it; SVG with vector-effect: non-scaling-stroke is not, which is the answer this system already reached for the process illustrations (→ Motion). Building the shafts out of CSS hairlines and the rhombi out of SVG would put visibly different line weights side by side in a brand whose first rule is the contour. So the drawing is one medium, and the layout — where each column stands — is the other.
Two rises have to agree, and only one of them is a token. The drawing rises 120 user units inside a 140-unit viewBox; the number rides on --plot-rise, which is 6u. They are the same distance because 20 user units is u — the box is 2u × 7u against a 40 × 140 viewBox — but CSS cannot check that. Change --plot-rise without changing the viewBox and the number will float away from its own cap. Resize with --plot-u, which moves both.
The value is a fraction, not a measurement. --v is the value divided by the tallest column in the figure, so the tallest column is normally 1. Dividing by a round ceiling instead — 100 %, or the target — is the better choice whenever that ceiling is meaningful, because then the empty air above a column means something too. Say which you did in the caption.

What makes it readable

A chart is the one component where the picture can be perfectly rendered and the component still fail. Four rules, and none of them is optional.

RuleWhy
The text is the data, and there is no second copy of it Each <li> carries its key and its value as real text, so the figure reaches the accessibility tree as an ordered list of "Roh 31 %", "Einheit 52 %", … The drawing adds nothing the text does not already say, which is why it is aria-hidden. No visually hidden table, no duplicated markup that can drift out of step with the picture.
Every column is labelled where it stands Direct labelling rather than a legend, which is what lets a reader take a number off the figure at all. It is also why the plot needs no axis: an axis exists to let you estimate a value, and the value is already printed.
Lime is emphasis, never a category The lit column is the one the caption is about. Because the caption names it in words, nothing is encoded in colour alone. Never use lit / unlit to mean two kinds of thing — that is a legend, in a component that has no legend, carried by a colour a third of readers cannot separate from its neighbours.
The caption states the message, not the mechanics "Anteil verwertbarer Signale je Aufbereitungsstufe. Erst nach der Modellierung steht die Datenbasis…" — the claim, then what it rests on. A caption that only names the axes has left the reader to do the interpretation the figure was drawn to save them.

Contrast: the contours are --border-strong (black) and the cell is --border-default, both on the page wash. Values are --text-primary; keys are --text-secondary, 5.9:1 on CF-Grau — never --text-muted, which is 2.0:1 there and is the mistake documented on Colour. The lime cap is a fill inside a black contour, so the 3:1 a UI shape owes is carried by the contour and not by the lime.

Growth

The columns rise out of the plane as the figure arrives, scrubbed from scroll position rather than played on a timer — the same posture as .cf-iso (→ Motion). One registered number, --plot-grow, is animated on a view timeline; the shaft's scaleY, the cap's translateY and the number's own top all read it, so the three cannot fall out of step with each other.

This is the one thing in the system that needs @property. An unregistered custom property is a token, not a number: it cannot be interpolated, so without registration the growth would have to be three separate animations that happen to agree. Registration also buys the failure mode. --v is declared <number>, so --v: 42% or a typo resolves to the initial 0 and the column is drawn at nothing — where an unregistered property would make the transform invalid at computed-value time and draw the column at full height, which is a chart that is confidently wrong. The range is clamped on top of that, because a registered number still accepts a 4.

Every fallback is the finished chart. No support for view timelines, prefers-reduced-motion: reduce, and print all leave --plot-grow at its registered 1. Nothing about this figure is hidden behind an animation: a chart that stays empty until a timeline says otherwise is a chart some readers never see. The growth also finishes inside the first third of the figure's cover range, so a plot near the bottom of a short page reaches full height rather than stopping short.

Do and don't

DoDon't
Use it for a short series a reader should remember — three to five values. Reach for it for a dataset. Five columns is the ceiling at which the figure still fits a 375 px screen, and it is also the ceiling at which a picture is still worth more than a table.
Print every value on its column. Drop the numbers and let the drawing carry the comparison on its own.
Light the column the caption is about, if any. Light two. One lime moment per screen, and this figure is competing with everything else on the page for it.
Put it where a figure belongs — inside an article, beside a claim. Stack two plots in one section. Two isometric ground planes at different heights read as one broken plane.
Resize with --plot-u. Resize with width, transform: scale() or a changed --plot-rise. The first two thin the contour; the third desynchronises the numbers.

Still open