Organism

Z1, the top bar

Node 0.2, and the only zone that is the same on every authenticated screen. 57 instances across 57 screens, and the class is in no html file: design/_shell.js injects the whole bar.

56px of --zone-top holding four things in a fixed order: the wordmark, the three navigation items, the annunciator, and one control. Because the generator owns it, an inventory taken from the screens has no header in it and does not notice. Stage 05 learned that and this stage still had to catch it: docs/inventory.md records z1 written down as 62 pages when the class is in none of them.

One control in the whole bar, and it is the way into the keyboard map. It rendered a literal question mark until stage 07, the one glyph in the shell that says nothing about what it opens. The accessible name is already Keyboard map, so replacing the visible character loses nothing. The mask paints at 1.5 here: it carried 1.8 in kit.css against a set that declares 1.5 everywhere in CSS, with no comment and no row in DESIGN.md, and design/kit/checks/icons.mjs found it.

Anatomy

The bench is the bar at the width of this column, which is narrower than the 1440 desk it is drawn for. Where it runs out of room it scrolls inside its own box rather than being squeezed.

dark, shipped

Harrier ?

FLEET|40 tenants|acts alone up to contain network at 3|1 moved down

R. Idrissi

light, the pair

Harrier ?

FLEET|40 tenants|acts alone up to contain network at 3|1 moved down

R. Idrissi

Behaviour at width

The bar wraps at every width, and half of a removed query is why. flex-wrap with vertical padding of --space-2 is the continuous version of the 1400px point stage 10 took out (Width), and measured from 1280 to 2560 the bar is 56px tall, which is --zone-top exactly. Below 1280 the gap drops to --space-3, min-height goes to auto, the padding tightens to --space-2 --space-3, and the navigation takes order:3 with flex: 1 1 auto and overflow-x:auto, so three items 119px wide scroll sideways rather than wrapping to a line of their own. width:100% there cost what the annunciator's cost: a bar three lines deep at every width under the point.

Variants

AxisValueUsesThe rule
sessionbase55signed in. Wordmark, three sections, the map, the annunciator, the analyst
.z1--out2the bar drops the navigation, the map and the annunciator. There is nothing to navigate to and no agent to report on, so a bar carrying three dead links would be furniture rather than a shell. What is left is the wordmark and one sentence
healthno values–Prohibited. The bar never changes with the connection. That is Z2’s entire job one zone below, and saying it in two places is two truths to keep in step
densityno values–Prohibited. 56px is already the smallest the bar goes at the desk, and below 1400 it wraps rather than shrinking, because the annunciator overflowed at 1280, the product’s declared minimum

When to use it

Never by hand. Every authenticated screen carries <header id="wf-z1"></header> and calls WF_SHELL, which fills it. The markup is written twice on purpose, here and in design/kit/shell.html, and if the two ever disagree the showcase is the specification and the generator is the bug.

Where she meets it. Six hours a day, on every screen, without ever looking at it on purpose. That is the argument for spending 56px on it and for the annunciator living here rather than on a configuration page: the fleet reading has to be at a glance, and a glance is what a top bar gets.

Rule and anti-rule

Do

Harrier?
LARKFIELD LOGISTICS|acts alone up to contain endpoint

Where she is, what she can reach, and how much rope Clerk has on the tenant in front of her. Three readings, one bar, no page to open.

Do not

Harrier
Live|last case 4s

Never the connection. That is z2, the strip directly underneath, which exists because a live indicator has to be there when everything is fine as well. Put it in the bar and there are two places to read the same fact and two places to leave it stale.

Limits

Rules of composition and context, which no anti-rule can carry: nothing else is the right component to take, and what is wrong is the count or the neighbour. Counted on the grey corpus at stage 09, and every one of them is a function in design/kit/checks/rules.mjs. Full table with sources on Architecture.

States

The bar itself has none. Two things inside it do, and they take the same one: point at either control.

dark, shipped

Harrier ? R. Idrissi
Harrier

This page is all that answered. The console is not running.

light, the pair

Harrier ? R. Idrissi
Harrier

This page is all that answered. The console is not running.

The map trigger takes ink and line together. --text-hover on the glyph and --line-hover on the border, and this rule is one of the two --line-hover was read off in the first place. The other is the toast’s dismiss.

The theme control takes the same two, and it was written by copying the rule rather than by inventing one. That is the argument for it being the same box: two controls in one bar that respond differently to the same gesture read as two systems. Neither response moves anything, because both borders are already drawn at rest and only their colour changes.

Focus is global and it is not redeclared here. The three navigation items each carry their own current state, which belongs to navitem, and the bar does not know which of them is which.

Motion

Response, 120ms, the browser's own ease. What moves is a boundary and ink, and it is on the KEYBOARD TRIGGER rather than on the bar: a transition on an ancestor does not reach the property that changes. Named one by one rather than as all, because all animates what nobody ordered and drags the layout properties in behind it. Never a size and never a position: those make the browser recalculate the layout of the page on every frame. Under prefers-reduced-motion it is 1ms, and this component does nothing to make that happen: it reads var(--dur-fast) and the token is redefined once. Full reasoning on Motion.

What it reads, and where it stands

RoleSurfaceWhere on the componentDarkLight
--bg-pagefillthe ground of the bar, the same ground as the screen behind it
--text-primaryink 4.5:1the wordmark
--text-secondaryink 4.5:1the map trigger at rest, and the signed out sentence
--text-hoverink 4.5:1the map trigger under the pointer
--line-edgeline, exemptthe edge under the bar, through --rule-edge
--line-separatorline, exemptthe map trigger’s border at rest, through --rule-hair
--line-hoverline 3:1that border under the pointer
--zone-topstructurethe 56px the bar is never shorter than

The three parameters, and their whole vocabulary

There is no fourth. A value outside these lists falls back rather than failing, which is why they are written down here: a screen calling strip:'degraded' would render the live strip and nobody would notice.

ParameterEvery value it takesWhat it decides
currentqueue, shift, logwhich of the three navigation items is aria-current. Three, because the MVP has three; Clients arrives with cluster 7 and the fleet never gets one
striplive, arriving, reconnecting, stale, clerkdownthe annunciator's state and its sentence. Everything but live renders is-degraded, and the sentence is written into the shell rather than into the screen so that five screens cannot disagree about what stale means
annun'fleet', or an object with lead and partswhose latitude the strip is reading. With nothing selected it is the fleet's; with a tenant selected it is that tenant's, and the accessible name follows the state because one fixed name would be false in one of the two

A screen that draws no shell calls nothing. The five sign in states have no wf-z1 element at all, and the two full outage states write class="z1 z1--out" by hand with no strip under it. That is rule R5 on Architecture, and it is checkable: a screen with a door has no .z1, a screen with .z1--out has no .z2.

Copy this

<!-- on a screen, this is all there is. WF_SHELL fills it --> <header id="wf-z1"></header> <script>WF_SHELL({ current:'queue', strip:'live', annun:'fleet' });</script> <!-- and this is what it injects --> <header class="z1"> <span class="wordmark">Harrier</span> <nav aria-label="Sections"><a href="queue.html" aria-current="page">Queue</a>…</nav> <span class="spacer"></span> <a class="kmap" href="keyboard.html" aria-label="Keyboard map">?</a> <p class="annun" aria-label="…"><b>FLEET</b>…</p> <span class="mono dim">R. Idrissi</span> <button class="theme" type="button" aria-label="Switch to the light theme">Theme</button> </header>

The label on the theme control is not in the markup you copy, it is written from the state. design/_shell.js subscribes to design/system/theme.js and rewrites both the accessible name and the title on every change, from the same value that picks the glyph. That is why the drawing and the words can never disagree: one state writes both. The button is not rendered at all on a page that did not load theme.js, because a control that cannot change anything is worse than no control.

Where it stands