Components

Arrival & Progress

The sibling of the error and empty state: that one stands in for content there will never be, this one for content that is on its way. A thing that has not arrived is drawn as itself, one rung down the presence ladder — its own contour at the 1-4 line, with one light crossing it. No grey slabs, no spinner, no script.

The plate reads twice

Line types catalogues the four lines the way the top half of the Formsprache > Linien plate does: four specimens side by side, equals, each with a job. The bottom half of the same plate is a different drawing and nothing here had implemented it. One object — a rhombus with an ellipse inside it — appears four times on one dotted axis, solid and small at the left, 1-4 and huge at the right.

Four line types, one figure. Read left to right it is not a specimen sheet at all: it is a thing coming into being, or going out of it. That is the second register the four types carry, and it only ever becomes legible when the same object is drawn more than once — which is why no static page has needed it and every state does.

Manual plate Formsprache > Linien: four line types above, and below them one rhombus drawn four times on a dotted axis, from solid at the left to 1-4 at the right.
assets/source/manual/shape-line-types.jpg — the register in the lower half is the one this component implements.
RungTokenLineMeans
3--presence-presentsolidhere. The object, drawn.
2--presence-near2-1all but here — held for a state that is a claim about the object, not about the wait.
1--presence-faint1-2construction: a line that explains the drawing rather than being it.
0--presence-absent1-4not here yet. Every placeholder on this page.
An object may move along the ladder, never across it. A thing that is not here yet is the same drawing as the thing that is, one rung down — same box, same size, same place. The moment a pending state is drawn as a different shape, the reader is being shown an object that will never arrive, and the fill has beaten the contour. This is also the whole argument against the grey slab: it is not a low-presence drawing of anything.
Two registers, no conflict. A line type has a use — where it is allowed to appear, which is what Geometry lists — and, when one object is drawn twice, a rank. --presence-absent was already doing this job under another name: .cf-plot__cell, the ghost footprint that says where a column stands rather than what it measures. → tokens.css 8a

The ghost

A placeholder is the arriving thing's contour and nothing above it in the material stack: no fill, no light of its own, no text. A line of copy that is not here yet is an empty box the height of its own line — not a filled bar, which is a line of type that has arrived and is unreadable.

The band that crosses it is a member of the colour system, not a detail of this component. It is the foil's own three chromatic stops at 18 % alpha, swept at 26.57° on a 2.4-second loop — the one part of the light family that is transparent and the one place a continuous loop is honest. Why those three stops, why 18 % when the contrast ceiling is 52 %, and why the movement is a colour decision rather than a motion one: Foundations · Colour → The foil, moving.

Beiträge werden geladen

<div class="cf-arrive">
  <div class="cf-arrive__ghost" aria-hidden="true">
    <span class="cf-arrive__plate" style="--arrive-h:9rem"></span>
    <span class="cf-arrive__line" style="--arrive-w:.94"></span>
    <span class="cf-arrive__line" style="--arrive-w:.88"></span>
    <span class="cf-arrive__line" style="--arrive-w:.52"></span>
  </div>
  <p class="cf-arrive__status" role="status">Beiträge werden geladen</p>
</div>

Anatomy

PartIsNotes
.cf-arrive the block Column, --space-3 between the drawing and the word.
.cf-arrive__ghost the drawn half Carries aria-hidden="true" and the travelling light. The status line is its sibling, not its child, for both reasons.
.cf-arrive__line one line of type Height is 1em × --leading-relaxed, so it tracks whatever type it stands in for. --arrive-w (0–1) gives copy its ragged right edge.
.cf-arrive__plate a card, a photo, a figure --arrive-h is the height the arriving thing will have. Give it the real one — a placeholder of the wrong size is a layout shift with extra steps.
.cf-arrive__object an isometric object The lattice's own 96 × 48 rhombus: the ghost of an illustration is the ground it will stand on. SVG, because a transform on a bordered box scales its contour.
.cf-arrive__status the word Mono label, role="status". Not optional — see below.

The object

Where an illustration or a figure is arriving rather than a block of copy, the placeholder is the lattice cell itself.

Diagramm wird berechnet

<svg class="cf-arrive__object" viewBox="0 0 96 48"><path d="M48 0 96 24 48 48 0 24Z"/></svg>

The light

One band crosses the ghost at --angle-b, above the contour — the light layer is layer 5 and the contour is layer 4, so this is the manual's own stacking order and not a glow bolted on top. It is the one place in the system a continuous loop is honest: something genuinely is still happening. The period is --arrive-period, 2.4 s, twice the slowest duration the system otherwise animates, because a fast loop reads as impatience.

The cap on the light is 0.18, and contrast is not what chooses it. The band lightens the contour it crosses, which is the one thing here that could fail in frames too short to catch in a screenshot — so it was measured rather than assumed. Composited the way CSS actually composites, in sRGB, the whitest stop over the blackest contour on CF-Grau gives 9.51:1 at 0.18 and 6.76:1 at 0.28; the 3:1 floor is not reached until 0.52. Sampled off rendered pixels across eight frames of one period in Chromium, the worst inked pixel on the shipped ghost measures 9.57:1. --arrive-light-peak is therefore a decision about restraint — light stays a moment and does not become a surface — with 0.52 as the ceiling it must never cross. → tokens.css 10
Under prefers-reduced-motion the band stops at 50 % — the middle of the object, which is the same designed still the swinging foil and the field's pool of light fall back to. The ghost stays: nothing is moving, and the thing is still not here yet. In print the light goes and the ghost prints, because the ghost is where the thing will be. In forced colours every background image is dropped, so the placeholders are redrawn as borders — the only paint that survives.

Progress — the ladder, traversed

A wait with a known end is one line changing type as it is crossed: solid behind the head, 1-4 ahead of it, and the head itself the lattice cell filled with light. Nothing here is invented for the component — it is the plot's lit cap standing on the rail the ladder already describes.

Telemetriedaten werden importiert

42 %

<div class="cf-progress" style="--progress:.42">
  <p class="cf-progress__label" id="prog-import">Telemetriedaten werden importiert</p>
  <p class="cf-progress__value">42 %</p>
  <div class="cf-progress__rail" role="progressbar" aria-labelledby="prog-import"
       aria-valuemin="0" aria-valuemax="100" aria-valuenow="42"></div>
</div>
PartIsNotes
--progress the value, 0–1 Registered with @property as a number, so a stray unit is the initial value rather than a rail drawn at full width. clamp() catches a 2 or a −1.
.cf-progress__rail the line Two background tiles, no overlay: the traversed run solid and pinned left, the untraversed run 1-4 and pinned right. The box is 8 px so the head has room; the line in it is 1 px.
.cf-progress__rail::after the head An 8 × 4 rhombus — the lattice cell at UI scale — filled lime with the plot's own glow.
role="progressbar" on the rail, not the block A progressbar's children are presentational: put the role on the block and the label and the value stop being readable. The rail is named by aria-labelledby pointing at the label.
The head is the screen's lime moment while it runs, and there is only ever one. Two progress bars on one screen is two lights; the second thing waiting is a list, not a second bar. Everything else in the component is black on the wash.

When the end is not known

A head that travels and resets is a claim about how far along the work is, made by something that does not know. So the indeterminate rail has no head: it is 1-4 end to end, and one segment of lit line passes along it — anchored at neither end, entering off the left and leaving off the right, never growing from zero, so there is no reading of it under which it is measuring anything. Drop aria-valuenow with the head: the two say the same thing and only one of them is visible.

The segment is drawn in --gradient-foil-ink, the shadow half of the foil — the one member of the family that is legible on this surface, since every stop of the lit half is above OKLab L 0.82 and lands at about 1.1:1 on CF-Grau. The band that crosses the ghost was tried here first and is wrong: spread at --angle-b over an 8 px box it puts almost no ink on the one pixel that is the line, and a wait indicator nobody can see is not one. Ghost line against lit line is a difference in line type, which is the difference this whole component is built on.

Bericht wird erzeugt

<div class="cf-progress cf-progress--indeterminate">
  <p class="cf-progress__label" id="prog-export">Bericht wird erzeugt</p>
  <div class="cf-progress__rail" role="progressbar" aria-labelledby="prog-export"></div>
</div>

Which one, and when

WaitDrawWhy
under ~1 s nothing A placeholder that appears and leaves inside a second is a flash, and the announcement arrives after the content it announces. Leave the space empty and let the thing land.
1–10 s, shape known .cf-arrive The reader can see what is coming and where it will sit, which is the whole benefit a placeholder has over a spinner.
1–10 s, shape not known .cf-progress--indeterminate One line and a word. Do not guess a shape — a ghost of the wrong thing is worse than no ghost.
over ~10 s .cf-progress Past about ten seconds a reader stops waiting and starts wondering, and wants a gauge rather than a rhythm.

What assistive technology gets

Do / don't

DoDon't
Draw the placeholder at the size and position of the thing that is coming. Draw a generic stack of bars. It is not a low-presence drawing of anything, and it moves the layout when the real content lands.
Fill the placeholder with nothing. Contour before fill. Fill it grey and slide a lighter grey band over it. That is the loading state's version of the bar beside a block — it says "waiting" without saying anything about the object.
Give the block one role="status" line. Ship the drawing alone, or put the status inside the aria-hidden half.
Let the head of a determinate rail be the screen's lime moment. Put lime anywhere else in the block — not in the ghost, not in the label, not in the rail.
Use the indeterminate rail when the end is unknown. Animate --progress on a timer to look busy. The value is a claim; only the thing doing the work may make it.
Replace the ghost with the object at the solid line. Cross-fade the two, or leave the ghost under the arrived content. The ladder is a move between rungs, not a dissolve.

Who sets it

Nothing in this system is dynamic, so no shipping page carries a state from this file today — the same way patterns/news.html renders page 1 of 11 flat and the server owns the paging. What the component fixes is the drawing and the contract, so that whoever adds the behaviour has nothing left to decide: