Action row

The row a screen's actions stand in. It draws nothing at all: no fill, no edge, no ink, no size. Three declarations wide, on 42 of the 57 pages, and the most useful thing about it is a rule it does not have.

Molecule41 grey pages61 rows1 formno states

Anatomy

One zone, one slot, and everything inside it belongs to the button. It is a molecule because it hosts an atom, not because it has parts of its own. Everything below is drawn by design/system/components/action-row.css and button.css, through the same entry point a product screen loads.

Behaviour at width

Fluid, and it has no point and no threshold. flex-wrap: wrap is the whole of it: the actions sit in a row while they fit and wrap when they do not, at whatever width that happens to be. Nothing switches, so there is nothing to switch at the wrong moment. See Width.

Variants and sizes

One form, and that is the finding rather than an empty matrix. The consolidation compared .actions and .secondary declaration by declaration and found them identical but for a margin, so they are one component with one rule, and step 6 took the second name off. There is no emphasis axis (the buttons carry that), no size axis (the buttons carry that too), no alignment axis and no count axis: a row holds one, two or three buttons and reads the same way each time.

What follows is therefore not a matrix of variants. It is the margin, which is the one thing the two names do not share, and it is here because the reason it is still two lines is worth reading.

The margin

A gap between blocks belongs to the HOST and not to the thing being spaced. These two lines are the same block spacing seen from two different containers, and step 6 is where they stop being two

Value Where it stands
Pushes down .actions 0 0 16px 55 rows on 36 pages. It sits above what follows it: the error's recovery button, the detail screen's cancel action, the gate's way through
Pushes up nav.actions 24px 0 0 6 rows on 6 pages, all of them under a list: Home and its two states, Alerts and its empty state, Data and privacy. Its anchors carried .btn.compact, which had become byte-identical to .btn: the size modifier folded away when the founder adopted the two scales, and step 6 took it off the markup
A third margin - Three containers override the margin instead, each in its own file: .card.prose .actions and .panel .gate .actions reset it to zero, because a row that is the last thing in a card must not push the card's own padding open, and .rstep .actions gives it a 16px top margin because inside a reveal step the action follows the number rather than leading the block

Two of those three overrides land on the same step, and it is worth saying that the spacing grid did not cause it. .card.prose .actions and .panel .gate .actions are both zero, and both were zero before the 8px grid was adopted as well: they are one rule written twice under two names, saying "the last block in a card takes no bottom margin". That is one line for the organism round, not two, and it is the same finding as the merge on this page one level up. The third, .rstep .actions, was 18px and folds to 16px, which is a step away from the other two and stays a step away.

Nothing else in the system needs to know how many buttons are in the row. Wrap and not scroll, and not a forced stack either: two buttons at 360px fit, three do not, and the third drops to a second line at the same gap rather than being squeezed. It is also why the button carries white-space: nowrap. The row is allowed to break between buttons and never inside one.

When to use it

Wherever a screen offers something to do. It is the second most repeated thing in the product after the button itself, and its job is entirely negative: to put the actions in a line, at a known distance from each other and from the block above, so that no screen has to decide any of that for itself. On a screen whose job is to look rather than to act, the row still appears and holds two quiet outline buttons: Home carries exactly that and nothing louder.

The zone rule lives here in practice. One filled accent per zone, and an action row is usually the zone: "Cancel this subscription" is filled and stands alone; "See what Pro adds" and "Maybe later" stand together and neither is filled, because the screen is not recommending either. If two buttons in one row both want the accent, the screen is asking two questions.

The rule, and the anti-rule

The row positions, the button paints

Three declarations: flex, wrap, gap. Every pixel a person sees is button.css, so changing how a button looks anywhere changes it here too, and nothing in this file can hold an old version of it.

Never style the anchors inside it
.secondary a { padding: 0 14px; border: 1px solid ...; font-size: 13.5px }

That rule was written at stage 07, and it is the clearest example in the whole stylesheet of the defect this stage exists to catch. The anchors in .secondary rendered as browser links, the reflex was to give the host a rule, and the rule then set padding, border, background, size and colour: not layout, but a second button declared under another name. Its values came back byte for byte identical to .btn.compact. The screens were moved onto .btn.compact instead, and the comment on .secondary had said so before the rule was ever written; the founder's review of the foundations then folded .btn.compact into .btn, so the anchors are a plain button today.

Use an action row

Two or more real commits, side by side, at the level of the screen or of a block inside it. Each one is a button, one of them at most is filled, and the row itself has no opinion about which.

Use a wash block instead
We could not refresh just now. Try again

A recovery offered inside a calm sentence that is explaining what happened belongs to the wash block and its own inline link, not to an action row holding a button. A filled petrol button inside an explanation shouts over the explanation. And a row of navigation is neither: four destinations that change which screen you are on are the tab bar, which is chrome and is fixed to the viewport.

Limits

U1, composition. One filled action per zone, and the row's second action takes .btn without .primary. A screen may carry three action rows, and the counter says it does; it may not carry three filled buttons.
U6, composition. A .textblock.status comes with a way out in the same container, and this row is usually it. The two exceptions are named in the rules: both are system waits.
U7, context. No action row on a wait screen. Full table with sources and how to check: Usage rules.

States

None, and that is a finding rather than a gap. An action row is not interactive: it is a flex container with no fill, no edge, no radius and no target of its own, so a pointer that appears to be over it is over a button or over nothing. Drawing four cells here would document something the product cannot do. Every state a person actually sees in this component is on button.html, which is where somebody looking for them will go anyway.

The technical half

ReadsWhich tokenGrowing from
the gap between buttons, and between wrapped lines--space-8primitive: a spacing has nothing to override
the space below the row--space-16primitive. .actions only
the space above the row--space-24primitive. On nav.actions only, and it moved to app-shell.css at step 6, because a margin between blocks is the host's
every colournonethe shortest token list in the system, and it is the point of the component. It reads no colour role at all

Lives in design/system/components/action-row.css. Stands on 43 grey pages, 64 rows, and on 44 coloured ones, 67 rows. A sample of the coloured screens carrying it: Subscription Detail, Home (as a <nav>), Add a Subscription, the Pro gate, Upgrade, Guided Reveal.

<div class="actions"> <a class="btn primary" href="...">Cancel this subscription</a> <a class="btn" href="...">See what Pro adds</a> </div> <!-- on 6 pages; it wore the second name .secondary until step 6 --> <nav class="actions" aria-label="More"> <a class="btn" href="...">Add a subscription</a> <a class="btn" href="...">See your trends</a> </nav>