Search

Level 1. A field that holds a query, a mark that says what the box is, a control that empties it, and the body that answers. Written 2026-08-16 as a page you went to, rebuilt 2026-08-17 as a control you use: below the RAIL rung it is a sheet over the screen, at the rung and above it stands in the header itself. The interesting number on this page is the one that decided the rung, and it is 69.

14 classes 108 screens 2 faces, 1 rung 25 events

It was a destination and it is a control

Search shipped on 2026-08-16 as three pages reached by a magnifier in the header. Measured on event-feed-search.html before it was touched, and the three defects are one defect wearing three faces.

wherewhat was measured
390, pointer coarsethe header is 61 tall, the category rail runs 77 to 143, the h1 lands at 184 and the field lands at 221. A person taps a mark at the top right and gets a 5,661px document that is the feed they just left, with the box they asked for as the FOURTH block on it
the same pageno autofocus anywhere in the file, so the keyboard does not open either: the tap costs a navigation and buys a box you still have to tap
1280, product 1060the field lies flat inside the plate at y=238, 922 wide, under an h1, while the header keeps a 36px magnifier at x=1092 whose href is event-feed-search.html. On all three search pages. On the results page that is a link to the page you are on that throws your query away

A page is where you go to read something; a control is what you use where you are. Nothing about a query is about a document, which is why the answer now appears in the surface the question was asked in, and the three pages keep the job a page is for: a full grid, a link somebody can send, and a state a reader can arrive at cold.

The rung is a measurement, not a preference

The free middle of the header row, between the end of .left and the start of .utility, signed in:

widthfreewhat it means
640 DESK69pxthe TIGHTEST width on the whole ladder, because DESK turns on the balance pill, the heart and How it works at the same rung that first gives the row any width
760 DETAIL137pxstill not a field
900 RAIL277pxthe first width where a field exists. 900 is 56.25rem, a rung this ladder already has
1060437pxfluid from there: flex:1 1 auto with min-width:0, so no second rung is needed to widen it
1380757px

Between DESK and RAIL the mark stays and opens the sheet, which is the honest answer for a width with no room rather than a compromise. Measured at 320, 360, 390, 639, 640, 641, 759, 760, 761, 899, 900, 901, 1000, 1140, 1280 and 1600 on both engines: exactly one way in at every width, 0 sideways scroll. min-width:0 is not tidiness: an <input> reports a default intrinsic size of 20 characters, and without it the utility cluster leaves the right edge on the narrow half of the branch.

The field, in its three states and both themes

Vault, darkDaylight, light

25 events, every one currently open

25 events, every one currently open

3 events for "election"

3 events for "election"

0 events for "tennis"

0 events for "tennis"

Empty, holding a query, and holding one that matches nothing. The third differs from the second in the count and in nothing else, which is the point: the field does not turn red when a search fails, because a search that finds nothing is not an error and the product has no error tone to spend on it anyway.

The clear button is not in the markup when the field is empty. A control that clears an empty field is a 44px target that does nothing, so the script adds and removes it with the value. The stylesheet also carries .search-field:has(.search-input:placeholder-shown) .search-clear{display:none}, which is not the mechanism: it is what makes a stale one harmless if a screen is ever drawn with the button and no value in it. :placeholder-shown is the one state selector that reads the VALUE.

The body is one body, and it stands in two containers

The sheet and the desktop panel show the same markup: the groups are written ONCE per screen, in the sheet, and the panel takes a copy of them the first time it opens. Written twice they would be two lists of five categories and four popular events in one file, and the day one of them gains an event the other is wrong and nothing says so. Nothing inside a group carries an id for exactly that reason: a clone of an id is a duplicate id.

partwhat it iswhy
.search-tilesa two-column grid of the five category chipsthe cell is fixed here and the chip is not touched. .chip-nav is inline-flex and white-space:nowrap, sized by its content, which is right in the rail it was written for and wrong in a grid cell. This file names its own li and that li's child, never .chip-nav: a container may decide the SIZE of the box it hands out and never the face of what stands in it
.search-rows.nav-item.nav-row.nav-row-stack, the notification dropdown's two-line rowa result row IS that row with different content, so it is not a third row face. What this file sets is the same half header.css sets for .notif-drop: the type, because a notification's second line is a sentence at 10 and a result's is a category and a figure at 11
the rows' negative marginmargin:0 calc(var(--space-12) * -1)the first letter of a result sits on the same vertical line as the group label above it while the hover ground still runs wider than the text. Without the pull the row reads as indented under its own heading, which is the one thing a list of answers must not look like
<mark>the matched substring, brass, no ground<mark> arrives with a UA background that is a raw yellow in both themes and belongs to no role here. The ground comes off and the brass does the marking: it is the one ink in this system whose whole job is "this is the thing you asked for". The mark is drawn on the ESCAPED string and the index is taken from the original, so a question carrying an & highlights in the right place
.search-seeallthe seam to the results pageit carries the count, because the count is what decides whether a person needs the page at all, and it is the LAST thing in the body so the seam is where a reader runs out of rows rather than above them

Two containers, and only one of them is new geometry

The sheet is a frame and not a skin. Every other face in dialog.css is a skin over one geometry: .win-dialog and .loss-dialog repaint a head, .signin-dialog re-pads a body, and all of them are the same 92 per cent-wide centred plate. This one changes the geometry and keeps the skin, because a search on a phone is not a message laid over a screen, it is the screen, and the plate's grain, gradient and bevels are what keep it inside the Vault while it is that.

The panel spans the field it hangs from, both edges set from the same box. That is the one geometry that cannot repeat the defect header.css recorded for its two dropdowns: those are pinned to the RIGHT edge of a control at the right end of the band, and a left offset written later took 55 per cent of one of them off a 390px screen.

The panel arrives rather than appears. 4px down, the same travel and the same direction the two header dropdowns use, multiplied by --motion so reduced motion keeps the panel and loses the movement. It is an animation and not a transition because the panel is hidden when it is shut and a display change has nothing to interpolate from.

Why the field is not a variant of input

the differencewhy it is anatomy
the mark is INSIDE the control.amount-cur is a SIBLING of the field, because a currency mark is not part of the value: input.css argued that out and moved the $ out of the input's own value to get the keyboard right. A magnifier is the opposite. It is not a value at all, it says what the box IS, and it sits inside it the way a placeholder does
the value is a QUERY, not a quantityevery declaration in input.css that is about the value is about a number: the display face, --text-18 and --text-30, inputmode="decimal", the spin buttons stripped, the mono figure. This field holds words in the body face at reading size, and it is the only field in this product that does
it carries a control of its owna field a person can empty needs something to empty it with. No other field here does, because a bet amount is replaced rather than cleared, so .search-clear is a STATE the input family has no vocabulary for

type="search" is the role and the decoration is the price. The type is what makes this a searchbox in the accessibility tree instead of a plain textbox. Both engines draw furniture on it for free: WebKit gives it a rounded UA field and its own cancel button, Chromium gives it a cancel button too. The component draws its own clear control, at 44px and in the system's ink, so both UA versions come off by name rather than by hoping appearance:none reaches them.

What it does share it shares through the system and not through a copy: the --bg-well ground, --border-field, the 44px floor from base.css, and the two-question focus input.css argued for. Those are the same because they are read from the same roles, which is what a token system is for.

The catalog is a second copy, and it says so out loud

Every other script in the painted tree is inline and per-screen, which is right for a thing that is ABOUT that screen: the sub-category rail reads the cards standing on the page it is in. Search is the opposite. It answers from every screen in the product about events that are on none of them, so the answer cannot be read out of the document it is asked in, and a copy of the catalog in each of 108 files is 108 places for it to drift.

assets/search.js holds it, loaded exactly the way assets/icons.js is and for the same measured reason: a script tag resolves from disk and from a server alike, where a fetch over file:// has no origin to match. The catalog is extracted, not written: taken from the painted tree by walking every <article class="card"> for its question, category, sub-category, lead figure and volume, plus the one question that is only ever an <h1 class="ed-q">. 25 events.

The cost is written in the file rather than discovered later: if the tree grows an event and the catalog is not re-taken, search will not find it. That is what a second copy costs, and naming it is the only thing that keeps it from being a surprise.

Rebuilding the surface exposed a page that was already lying. event-feed-search-results.html filtered a 12-card subset and printed "2 events for election" while the catalog matches 3. The two result pages carry all 24 cards now, so the seam's count and the page it points at cannot disagree.

The rule

A filter narrows a list you are browsing; a search finds a thing you already have in mind. That is why the field is not in the feed head beside Sort and How often: that head already carries two category selectors using the same five words, and a third control doing a fourth job would be worse than the defect it fixed.

The mark stays an <a href>. The sheet is script; the page is not. The link keeps working when the script does not, and it is the same three pages the IA specified, so nothing was thrown away to get here.

One query, one field. At the RAIL rung the page's own in-plate field is display:none, because the header carries one and two fields for one query on one screen is the same defect as the magnifier that linked to the page it stood on.

The count is mono because it is a figure a person reads twice, before and after typing, and a figure that changes has to be a figure both times.

The anti-rule

Never give search a navigation slot. The IA settled this before the control existed: four destinations, and finding a thing is not one of them.

Never colour a search that finds nothing. Zero results is an answer, not a failure, and red in this product means NO.

Never put the field in the header below 56.25rem. 69px is not a field, it is a field-shaped hole where the balance pill used to be.

Never let the seam's count and the results page disagree. They did, for one day, because the page filtered half a catalog.

Where the rest of it is

The level and its reason: the atoms shelf. The stylesheet: components/search.css, and the sheet's frame in components/dialog.css. The behaviour and the catalog: assets/search.js. The field it is not a variant of: input. The row it borrows: nav item. The chip its tiles are: chip. The band it stands in: header. The block under a search that finds nothing: state-block. The decision and the date: ia/docs/sitemap.md and docs/decisions.md.