Nav item

Level 1. A row a person taps that GOES somewhere and draws NOTHING until it is pointed at: transparent, no edge, no corner, as wide as whatever holds it. 995 placements on 105 painted screens, three faces, 0 bare. Measured 2026-08-08 in a browser, at 390 and 1280, in both pointer branches.

995 placements 3 faces 899 links, 96 buttons 0 bare 1 face under the floor

What this page is for

The level page is the shelf and this page is the depth, and the two answer different questions. Comparing this atom against the other nine is work the shelf does, so it stays on the atoms page together with the level and the reason for it. Taking one component apart needs room the shelf does not have: the shelf gives every component one specimen, and one specimen cannot show three faces, two elements and a state that is reached from an ancestor.

So nothing here is repeated from the shelf, and the shelf keeps what a page of its own would only copy. A fact written twice drifts.

The atom, and it really is this short

Six declarations, and every one of them is an ABSENCE: no ground, no border, no corner, no underline. What is left is a full-width block with a pointer. The whole control is what happens when somebody points at it, which is the next section but one.

display:block is the fourth value this atom had been WEARING without writing down, and it is the reason the file says so out loud. width:100% is why this class exists as a row, and width does nothing to an inline box. The <button> element supplied inline-block for free, so the declaration worked for exactly as long as every one of these was a button, and stopped the moment the account dropdown's 365 became links. Measured on the day: 194x33 collapsed to 66x30, the row shrinking to its own text.

A value the element supplies is a value the component has not chosen. Two of the three faces here declared a display of their own, so the gap stayed invisible until a face that declared none crossed over.

Two elements, one drawing

elementplacementswhat it is
<a>899it goes to a screen, and the href is the destination
<button>96it opens a dialog: data-open="signin" on 32 logged-out screens, three slots each

The same atom, and the drawing does not know the difference. That is the correct answer for a bar where the Sign in slot sits beside three navigation slots and has to look like them, and it is also what made the missing display above a real defect rather than a tidy-up: the atom cannot rely on the tag when it stands in both.

The three faces

slot, 420 placements, the only navigation a phone has

Vault, darkDaylight, light

A mark over a label, 10px, centred, muted. The first one is the current tab. In the product aria-current sits on the <li> and the state is reached from an ancestor, so the specimen is the bar's own markup and not an imitation of it: <nav class="bottom-nav"><ul><li>, exactly as the 105 screens ship it.

It was three loose anchors in a bare <div> until 2026-08-09, and that is why this cell was broken. .nav-item is display:block;width:100%, and its own file says width:100% is the whole reason this class exists as a row. The width it fills is the <li>'s, and .bottom-nav li is flex:1 inside a ul that is display:flex. With no list around them each anchor filled the whole cell instead: 477px each, three of them wrapping onto three lines, and the current one squeezed to 41px because its wrapper <div> was a flex item that shrank to its text. A stand class standing in for a product container does not show the atom, it shows the atom with its arrangement removed.

One thing differs from the product and it is declared: the bar is display:none from 640px up and position:sticky, so the cell pins it visible and static through .tk-show-nav. That is the same bargain .tk-dlg makes for a dialog and the dock makes on vitrine.html. The ABSENCE is the bar's fact and it is shown where the bar is the subject, on bottomnav; here the subject is the slot, and a slot nobody on a desktop can see is not a specimen.

row, 365 placements, a line of a menu that drops out of the header

Vault, darkDaylight, light

Full width, left aligned, 11px, the strong ink because a menu is read rather than glanced at.

row-stack, 210 placements, a notification

It is the row with one declaration changed, and the declaration is the display. A menu line is one line; a notification is a title and a detail under it. The title and the detail themselves stay in components/header.css, because they are the row's CONTENT and not the control's face, the same way a card keeps its own title.

Which is why this is the one specimen on the page that stands inside its container, and it had to. Lifted out of .notif-drop the same markup renders 33 tall instead of 49: the <strong> and the <span> take their display:block from the dropdown's file, so the two parts sit on one line and the stack is not a stack. The boundary this page describes is visible in the specimen rather than only stated in it, and the honest way to show a control whose content belongs to somebody else is to borrow the somebody else.

It joined this file on 2026-08-06 and two values had held it back, both of which were measured to draw nothing. Its font-size was the browser's 16 where the row is 11, and nothing inside it reads a font size: it holds a <strong> and a <span>, both block, both with sizes of their own, and no bare text node between them. So it took the row's 11 and no glyph moved.

Current, and why the colour never answers a pointer

The slot's ink is the current-tab signal and nothing else. So the ground answers the pointer here and the colour does not, and that is a decision with a phone behind it: if pointing at a slot pulled its label toward brass, the one thing the bar says at a glance would be said by two different things, and a hover can stick after a tap on a touch screen, so the wrong slot would go on claiming to be current until something else was touched.

[aria-current="page"] is written with the attribute alone rather than with the bar's class. Being the current item is the atom's state wherever the atom stands, and naming .bottom-nav here would be this file holding a region's name.

The order of four rules is load bearing. Each hover is written at the face's own two-class weight so it can reach the slot a person is standing on: at .nav-slot:hover it would be (0,2,0) and would die under the current-page rule at (0,3,0), on exactly the tab that gets tapped most. The press is last and at the same weight as the hovers, so a tie is broken by source order and a held control shows the press rather than the hover.

Two faces answer the pointer differently and agree on the press

facewornhoverpress
.nav-slot420--tint-hover, a quiet neutral wash--bg-pressed, one rule for both, and it is the system's one pressed ground
.nav-row365--tint-brass-09, the wash the rest of the header uses

Neither is a majority and both have an argument, so both are kept and the question is written down rather than settled by whoever edited last. The bar is the only navigation a phone has and its brass is spoken for by the current tab; the menu is a header surface and the header is brass. docs/backlog.md 45.

The measured boxes, and the face that is under the floor

Read on 106 screens at 390 and at 1280, and again with touch emulation on and matchMedia('(pointer:coarse)') asserted true before every read.

faceworn3901280with a finger
.nav-slot42094x550x0, the bar is a phone control94x55, unchanged
.nav-row365194x33194x33194x33, unchanged
.nav-row-stack210258x49258x49258x49, unchanged

365 rows stand 33 tall with a finger on them, and nothing in the system lifts them. The one touch floor in components/base.css names fourteen families and .nav-item is not one of them, which was not a decision: the floor was written from the six per-file lists that existed, and no file had ever written a floor for this atom. 33 clears WCAG 2.5.8 AA at 24x24 and fails 2.5.5 AAA and this product's own standard at 44.

It is not a one-line fix, which is why it is a backlog row rather than an edit made here. Five rows at 44 make the account dropdown 55px taller, and that is a layout decision about a panel rather than a floor. docs/backlog.md 54.

The slot at 55 and the stack at 49 are both above the floor already, and neither got there from a rule: the slot is a mark over a label with 8px of padding, and the stack is two lines. A box that clears a floor by accident clears it until somebody edits the padding, which is the argument for the floor naming the family rather than the exception.

Two rows that are NOT this atom

The sub-category row, .subcat button, 234 placements. It measures as a hairline PILL at 380 and a 10px full-width row above 860: an edge and a corner at both widths, which is neither this atom's rest face nor one modifier away from it. It was moved out of chip on the strength of the desktop rule alone, which is the same half-measurement that put the social mark in the wrong atom. docs/backlog.md 46 holds it, with both widths written down.

The bar, the dropdown and the list are not here either. components/bottomnav.css keeps the bar, its sticky position, the groove along its top edge, the dividing rules between slots and the centring on the <li>; components/header.css keeps the panel, its shadow, its corner and where it opens. Neither says what the control inside it looks like any more, and that sentence is the test a migration passes or fails.

The rule

A face is a modifier, and a region is a place. What differs between these three is a direction, a size, an ink and an alignment, and all four are the control. Where the control sits, what holds it and how that thing is lifted off the page are the region's, and the region is allowed to keep exactly those.

The state comes from above. Being current is not something this control can know about itself, so the selector reads an ancestor and names no region while doing it.

The anti-rule

Never let the element supply a value. A display that arrives from <button> is a value the component did not choose and cannot keep, and it left 365 rows collapsed to their own text the day they became links.

Never move the ink to say two things. The slot's colour is the current tab. A hover that also moves it makes the bar say the same thing twice and, on a touch screen where a hover sticks, say it wrongly.

Never name the container in the control's file. The moment this file writes .bottom-nav it stops being an atom and starts being a second copy of the bar.

Never file a row here on one width. Two rows in this product look like this atom at 900 and do not at 380, and both were nearly filed here on the desktop reading alone.

Where the rest of it is

The level and its reason: the atoms page. The stylesheet and the argument for every value: components/navitem.css. The regions that hold it: components/bottomnav.css and components/header.css. The open questions: docs/backlog.md 45, 46 and 54.