Group head
The small uppercase line that opens a section, with an optional figure at the far end. It is the second most repeated structure in the product after the subscription row, and it is what lets a screen carry twenty sections without a single heavy heading anywhere.
Anatomy
Two content slots, a heading and an optional figure, which is what makes it a molecule. Everything below is drawn by design/system/components/group-head.css through the same entry point a product screen loads, so this page cannot drift from the screens.
Streaming (4)
$54.96 a month.group-headthe molecule: a baseline-aligned flex row, space between, with a hairline under it. Written as adivwrapping a heading, or written on the heading itself:where(h1,h2,h3,h4)the heading slot, matched at any level. 12px, weight 600, uppercase, tracked out by 0.06em, in the muted tier.:where()contributes no specificity, which is what keeps this rule from outranking anything.subtotalthe figure at the far end. Same size and same tier as the heading, tabular, and never wrapping.plainthe modifier that takes the rule off and stands the block further from what follows:has(> .actions)the far slot holding a CONTROL instead of a figure, since 2026-08-20. Read off the content and not off a modifier, the way.summary:has(> .trust)and.wash:has(> .actions)already are. The row centres, wraps, and takes the gap between two blocks under it; nothing else changesborder-bottom--line-divider, decorative: it separates rather than identifies, so it owes no contrast thresholdalign-itemsbaseline, so a 12px word and a 12px number sit on one optical line rather than on two.centerin the one form that carries a control: a 48px button has no baseline worth sharing with a 12px label, and onbaselineit hung 18px below the hairline
It hosts no atom, and the figure is deliberately not one. .subtotal looks like a place for the Amount and is not: it never takes the amount's weight or its primary ink, because here the money is a fact about the group rather than the subject of the line. Two things that would be the same object at the same value are still two objects when their jobs differ, which is the naming rule in docs/architecture.md applied one level up from tokens.
Variants and sizes
Two axes, and the second one arrived on 2026-08-20. The first is about what stands UNDERNEATH: banded is the default and draws a rule, plain takes the rule off, and the choice is made by the thing below the head rather than by the importance of the section. The second is about what stands in the FAR SLOT: a figure, which is all it had ever held, or the section's own action. Nothing in the definition of this molecule ever said the far slot was a number; the subtotal was simply the only thing that had stood there.
| Rule Banded when what follows is a bare list or a set of fields, which have no edges of their own and need the head to close off the section above. Plain when what follows already draws its own edges, a grid of tiles or a row of chips: a hairline above a bordered grid is two lines saying one thing |
Measured | Where it stands |
|---|---|---|
Banded .group-head |
8px padding 2px margin 1px --line-divider |
34 of the 46 coloured occurrences, and every one of them a div wrapping a heading. Category groups on Home, the field groups and the plan line on Settings |
Streaming (4)$54.96 a month | ||
Plain .group-head.plain |
0 padding 16px margin no border |
9 coloured occurrences, and every one of them is written directly on a heading: h2 on Add a Subscription and its loading state, h3 on the guided reveal. Which is exactly the pair of screens that broke it |
Most tracked | ||
Carrying the section's action .group-head:has(> .actions) |
centred wraps at 320 24px margin |
3 coloured occurrences and 3 grey: the head of the list on Home and on its error and Save-focus states. Founder's decision, 2026-08-20, on a desktop row that held one outline button and 1370px of nothing beside it |
Your subscriptions | ||
| Content The figure appears when the group has one number that describes it. It is not "a total for every section": a section of fields or switches has nothing to sum, and an empty far end is correct there |
Measured | Where it stands |
|---|---|---|
| With a figure | 12px, tabular, nowrap | Home's category groups, and Settings, where the far end holds a plan name rather than a sum |
Software (4)$53.98 a month | ||
| Without | - | Settings' field groups. The heading takes the whole width and the row is still a flex row, so adding a figure later moves nothing else |
Your details | ||
| Loading | 104px and 90px | The loading states. The host owns the slot widths and the skeleton bar draws the bars. Two different widths on purpose: a column of identical bars reads as a progress meter, and a loading state is not progress |
There is no size axis and there must not be one. The head is 12px on every screen at every width, and that is what makes a group head recognisable as a group head from across a long page. A larger one is a page subject, which is the text block, and a smaller one is an eyebrow.
When to use it
A group head opens a section of like things: four streaming subscriptions, three preset tiles, the fields that make up your details. It is the quietest possible way to say "a new kind of thing starts here", and quiet is the requirement rather than a preference: Home carries five of them in a column and a person is meant to scan past four of them to find the one they came for.
Uppercase at 12px with tracking does the work a heavier weight would otherwise do. It is small caps behaviour without a second typeface, which matters because DESIGN.md forbids a display face by name: one family for the whole product, and a second family is how a calm interface starts sounding like two products.
The figure at the far end is context, in the plain money language sense: "Streaming (4)" and "$54.96 a month" together say what the group is and what it costs, and neither is useful alone. On Settings the same slot holds the word "Free", which is the same job with no arithmetic in it. Since 2026-08-20 it also holds a control, on the head of Home's list: "Your subscriptions" against "Add a subscription". That is the same sentence shape one step further - what the group is, and the one thing you can do to it - and it is why this needed no new component. What the slot may never hold is a SECOND heading: two headings on one line is a row with no subject.
The rule, and the anti-rule
Most tracked
Streaming (4)
An h2 and an h3, identical on screen. The document picks the level from the outline it needs, the component picks the look from the class, and the two never have to agree. This is what :where(h1,h2,h3,h4).group-head buys, and it costs no specificity at all.
Streaming (4)
That is what this component used to be, and the picture above is what it looked like when it broke: a browser-default heading, 24px and black, next to 12px uppercase muted everywhere else. It broke twice for the same reason, once on the form screen where the h2 IS the head, and again on the guided reveal where the head is an h3 inside a group, and both times on a screen that had already been accepted. A component whose typography depends on which heading level the document happened to need is not a component.
Music (2)
$22.98 a monthA label on a set of like things, with the one number that describes the set. It repeats down a screen without ever competing with the content it introduces.
We could not load your subscriptions
Nothing is lost. Try again in a moment.
When the heading is what the screen is about rather than a divider inside it, the component is the text block, written .textblock or .textblock.status. The tell: a group head is followed by items, a text block is followed by an action. Setting a page subject in 12px uppercase muted would leave a screen with no subject at all, which is exactly what a person arriving on an error state cannot afford.
States
None, and it was checked rather than assumed. A group head is a label on a section: it holds no anchor, no button and no disclosure on any of the 57 grey pages or the 57 coloured ones, 36 heads on one side and 41 on the other, and the sections it opens are not collapsible in this product. The one disclosure the app shell does have is a details.panel summary on the alerts screen, which is the panel organism and a different component with its own indicator. The loading form in the matrix above is content, not state: it is what the block holds while the figures arrive, and it belongs to the skeleton bar.
The technical half
| Reads | Which token | Growing from |
|---|---|---|
| the heading and the figure | --text-muted | --muted, 5.8:1 light and 6.4:1 dark. One tier for both, because they are one line |
| the rule underneath | --line-divider | --hairline-soft, decorative: 1.23:1 light is deliberate |
| the type step | --type-meta | primitive, straight: a size has nothing to override |
| gap, padding, margins | --space-16, --space-8, --space-2, --space-0 | primitive, straight |
| the two skeleton slots | 104px and 90px, literal | slot widths, which are not on the spacing scale. The host owns the slot, skeleton-bar.css owns the bar, and that file names these two rules as belonging here |
Three values moved and none was re-decided. The gap was 12px and is 16, the padding under the heading was 6px and is 8, the plain form's bottom margin was 12px and is 16. Nearest step, ties to the larger, straight from the founder's review of the foundations. Everything else measured identical before and after on the real Home screen: 12px, weight 600, 0.72em-equivalent tracking, the muted ink, the 2px bottom margin and the divider colour.
One rule of this component lives in another file, and it is named in both. .app .rgroup .group-head { margin: 0 0 8px } belongs to the merchant chip group, because it is that molecule positioning this one. It ties with .group-head.plain at 0-3-0 and is decided by source order, which the @import order settles by itself: alphabetically inside the molecule level, group-head.css comes before merchant-chip-group.css, so the host wins. Measured on this system: 8px, as the product renders today.
Lives in design/system/components/group-head.css. Stands on 17 grey pages in 43 places and on 18 coloured ones in 48, recounted 2026-08-20 by design/kit/screens/counts.cjs, which reads the places as well as the pages since that day. The colour is more than the grey: the five places past it are on screens the grey writes without a head. A sample of the coloured screens carrying it: Home and three of its states, Add a Subscription and its loading state, Guided Reveal, History and Trends and its loading state, Settings, Settings with no account, Alerts and its empty and loading states.
<div class="group-head">
<h2>Streaming (4)</h2>
<span class="subtotal">$54.96 a month</span>
</div>
<h2 class="group-head plain">Most tracked</h2>
<div class="group-head">
<span class="skel"></span><span class="skel"></span>
</div>