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
FLEET|40 tenants|acts alone up to contain network at 3|1 moved down
R. Idrissilight, the pair
FLEET|40 tenants|acts alone up to contain network at 3|1 moved down
R. Idrissi.z1the bar.min-height:var(--zone-top), a flex row on--bg-page, and the one line it draws is the edge underneath it.wordmarkthe product’s name at--size-lg, weight 600, tracking −.01em. It is a word and not a mark, and it opens nothingnava bare element selector inside.z1, holding the three MVP sections. Each item isnavitem, and Clients arrives with cluster 7.spacerflex:1 1 auto. A place rather than a thing, and it is what puts everything after it on the right.kmapthe first of the two controls. A bordered box painting a 16px mask, withfont-size:0so the character inside it never renders and the accessible name still does.themethe second control, and the only one in the product that changes how the console is read. The same box as the map trigger to the pixel, and the only difference is that it is abuttonrather than a link, because it goes nowhere. It paints thesunin the dark theme and themoonin the light one, and the pair on the bench above is what proves it: the attribute is read as an ancestor, so the light half shows the moon while this page stays dark.annunthe annunciator, its own molecule. Its accessible name follows the state, because with a tenant selected it is that tenant’s latitude and with nothing selected it is the fleet’s.z1--out .notemono at--size-xs, and the only text the bar carries when there is nothing to navigate to
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
| Axis | Value | Uses | The rule |
|---|---|---|---|
| session | base | 55 | signed in. Wordmark, three sections, the map, the annunciator, the analyst |
.z1--out | 2 | the 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 | |
| health | no 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 |
| density | no 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
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
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.
- R5. No shell before authentication, and no annunciator on a console that cannot read Clerk.
States
The bar itself has none. Two things inside it do, and they take the same one: point at either control.
dark, shipped
This page is all that answered. The console is not running.
light, the pair
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
| Role | Surface | Where on the component | Dark | Light |
|---|---|---|---|---|
--bg-page | fill | the ground of the bar, the same ground as the screen behind it | ||
--text-primary | ink 4.5:1 | the wordmark | ||
--text-secondary | ink 4.5:1 | the map trigger at rest, and the signed out sentence | ||
--text-hover | ink 4.5:1 | the map trigger under the pointer | ||
--line-edge | line, exempt | the edge under the bar, through --rule-edge | ||
--line-separator | line, exempt | the map trigger’s border at rest, through --rule-hair | ||
--line-hover | line 3:1 | that border under the pointer | ||
--zone-top | structure | the 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.
| Parameter | Every value it takes | What it decides |
|---|---|---|
current | queue, shift, log | which 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 |
strip | live, arriving, reconnecting, stale, clerkdown | the 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 parts | whose 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
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.