Step-forward link

The only control in the product that points down. It carries a person from one step of the guided reveal to the next, and the next step is further down the same page rather than on another one. Two instances, one screen, and that screen is the one the whole product exists to deliver.

Atom1 grey pageone form3 states, both themes

Anatomy

One zone, one label and one glyph, which is what makes it an atom. Everything below is drawn by design/system/components/step-forward-link.css through the same entry point a product screen loads, so this page cannot drift from the screens: there is no second copy of the CSS here to drift from.

The empty alt slot, and why it is the most useful line in this file

content: "\2193" / "" is a string and then an alternative text, and the alternative is deliberately empty. Without it the arrow is part of the generated content and lands in the accessible name, so a screen reader announces "See what they are, down arrow". The arrow is a picture of the direction, not a word of the sentence, and a person who cannot see it gains nothing from hearing it named. The empty slot is the one place in CSS that can say "decorative" about generated content, and this system says it here and once more on the alert's forward arrow.

How it degrades is worth knowing rather than guessing at. A browser that does not parse the alt slot drops the whole declaration, because an invalid declaration is discarded entirely: the link keeps its label, its underline, its floor and its target, and loses only the arrow. That is the right thing to lose, and it is the reason the arrow is a decoration in the stylesheet rather than a character in the markup, where nothing could have been done about it.

Variants and sizes

The step-forward link has one form, so there is no matrix to draw. Both instances render identically: 13.5px, body ink, a 44px floor, a hairline underline at a 3px offset and a muted arrow 6px after the label. The census measured .next as one of the sixteen families with a single value on every property, at both viewports.

What could vary, and does not

Each empty cell is a prohibition with its reason rather than a form nobody has drawn yet. This control has one job on one screen, and every axis it might grow is an axis that would make it a different component

Values Where it stands
The one form .next 13.5px / 44px
↓ 6px after
guided-reveal, twice: after the count and after the categories. Both are coloured
a direction axis - Forbidden with a reason. The arrow points down because the destination is down, on the same page. A right-pointing version would be a link to another screen, which is a different promise, and the product already has one: the alert item's own forward arrow
an emphasis axis - Forbidden with a reason, and it is the One Voice Rule. Petrol is spent on the primary action, the current selection and the trust line. A filled step-forward link would be a fourth claim, and on the reveal it would compete with the monthly total, which is the largest thing on that screen by decision
a size axis - Forbidden with a reason. Size is set by the container everywhere in this system, and this control has exactly one container: a reveal step. One container, one size

When to use it

This is D1 drawn: gradual reveal with a paired action, count first, then the categories, then the total. The decision is that a person meets their subscriptions one step at a time and that every step has its own way on, so that nothing arrives before they asked for it. The link is what "asked for it" looks like. It is small on purpose: the step above it is the event, and the way forward is a quiet offer under it rather than a second thing competing for the same attention.

Its two labels are in voice/docs/microcopy.md and are typed there as links, while every committing action on the same screen is typed as a button: "See what they are", "See the monthly total". Both are second-person Tendd labels naming what will appear, not what the person will do to the interface. The voice round changed the first one from "Show me what they are" for exactly that reason: "Show me" is the person speaking, and the product reserves that voice for the share card alone.

The rule, and the anti-rule

Use a step-forward link

The destination is further down the page a person is already on, and the tap reveals rather than commits. Nothing is saved, nothing is sent, nothing is charged, and nothing is left behind: the two steps above stay where they are and can be read again.

Use a button instead

When the step leaves the screen, it is a Button. The reveal itself shows the line: two step-forward links between the three steps, then a filled button at the end, because that one goes to Home. The comment in the markup says why the last one is alone: "A menu of next steps at the emotional peak is a second decision nobody asked for."

Standalone, and it takes the floor

It stands on its own line as the action of its step, so the 44px target applies to it in full, and it has it. The census found 21 links across 10 pages that fall under 44px because nobody remembered; this one was written with the floor in it.

Never inside a sentence
Can't find it? Add it by hand and fill the details yourself.

A link inside running copy is the quiet inline link, which is exempt from the floor because a 44px box inside a paragraph would tear the line spacing apart. Putting this component there would put a 44px inline-flex box and a downward arrow in the middle of a sentence, and the arrow would be pointing at the rest of the sentence.

Limits

U13, context. ADVANCE. The arrow points down, because the link goes further down the same page rather than to another one, so on hover it advances down by one nudge. Everything in this product that moves travels exactly one --nudge, which is 2px, and nothing anywhere travels two. Hover and release run at --dur-state (150ms) on --ease-arrive; a press runs at --dur-press (90ms), because it answers a finger that is already down. The full language and the live specimens are on Motion; the table with sources is Usage rules.

States

Three states, and they exist because the link is interactive. Each one is a token override rather than a style of its own: no hex and no number appears inside :hover, so the dark theme costs three lines in tokens.css rather than forty edits across the components.

The fourth state does not exist, and its absence is a decision. This is an a with an href to an anchor on the same page, and an anchor has no disabled attribute. A step of the reveal a person may not reach yet is not drawn dim, it is not drawn: the reveal renders the steps it has. Faking it with a class the product does not carry would document something the product cannot do, which is the same defect as inventing a variant.

Rest, live

The three, in both themes

The states are described, not photographed. Founder's decision, 2026-08-11: the snapshot strip went from every page in this kit, 120 images across 18 pages. They were framed as measurements rather than illustrations, and that framing was honest, but the bill came due all session: a value moves, and every picture of it is quietly stale until somebody re-shoots it. A stale picture is worse than no picture, because it looks checked. What is left is what a picture never carried anyway: the state, and the token it reads. The resting component is live and interactive above, so hover it, tab to it, and read the value off the real thing rather than off a photograph of it.

Restlight
--text-body on the label, --text-muted on the arrow, --line-container under both
Restdark
the same three tokens, the other half of each pair. The label holds 10.4:1 on the dark surface against 10.2:1 on the light one
Hoverlight
--text-action, and it takes the label, the arrow and the underline at once. Three declarations, one token
Hoverdark
petrol climbs the scale in the dark theme rather than mirroring, so the hover is lighter here and darker on light: a hover moves away from its ground, and the ground swapped ends
Focus visiblelight
--color-focus, 6.23:1 as a line. The ring wraps the whole 44px box, not the text, because the box is the target
Focus visibledark
--color-focus, 6.82:1. Had the pair been left for later this ring would have been the light petrol at 1.9:1: in the file, invisible on screen
Retake, and this is the whole recipe, because there is no node Playwright in this repo and a command that does not run is worse than no command: serve the repo, then for theme in (none, data-theme="dark" on <html>): goto /design/kit/step-forward-link.html move the pointer away -> screenshot #live-rest -> screens/step-forward-link-rest-<theme>.png hover #shot-rest -> screenshot #live-rest -> screens/step-forward-link-hover-<theme>.png focus #shot-rest, press Tab, assert :focus-visible -> screenshot #live-rest -> screens/step-forward-link-focus-<theme>.png Six files and not eight: there is no disabled state to shoot, for the reason above. The states are reached the way a person reaches them: a real pointer and a real Tab. Step 9 checks these files for a byte shift, because a snapshot nobody re-takes freezes silently, and a frozen snapshot is worse than none: it looks checked.

Visited is not styled and will not be. Both targets are anchors on the page a person is already reading, so every one of them is visited within seconds of arriving, and a visited colour would turn the reveal grey behind a person as they walk down it. The one screen this component stands on is the screen whose whole job is that walking down it feels good.

The technical half

ReadsWhich tokenGrowing from
its label--text-body--slate
the arrow--text-muted--muted. One tier down from the label: the glyph is a hint about direction, not a word
the underline at rest--line-container--hairline. Decorative, so it carries no threshold, which is exactly why it may be this quiet
label, arrow and underline on hover--text-action--petrol
the focus ring--color-focus--petrol
the floor--tapprimitive, straight: geometry has nothing to override

One judgement in this file, named rather than buried. On hover the underline reads --text-action, an ink role, and not a line role. At rest it is quieter than the text above it, so it is decoration and takes --line-container; on hover the product paints it with exactly the same petrol as the label, so it stops being a separate line and travels with the ink. The only petrol line role in the system is --line-selected, whose declared job is the current selection, and a hovered link is not a selection. The value is identical either way, so the name is the whole difference. It is not a local call. Every link in this product underlines the same way, hairline at rest and the ink colour on hover, including the thirteen rules the census found doing one job, so the quiet inline link component meets this same question when it is built and should answer it the same way.

Lives in design/system/components/step-forward-link.css. Stands on 1 grey page, twice. Coloured screen carrying it: Guided Reveal. The registry's count of 2 is the number of links, not of pages.

<a class="next" href="#step-2">See what they are</a> <a class="next" href="#step-3">See the monthly total</a>