ArenaSkipLink.prompt.md
September 6, 2026 · View on GitHub
Arena skip link, the one control on a page that exists for a keyboard alone. The link lets a reader step over everything repeated on every screen and land in the content. Standalone, OnPush, signal I/O.
The host is taken out of layout with display: contents, so the anchor inside it is what the page
sees and <arena-skip-link> itself occupies nothing.
<arena-skip-link label="Skip to content" />
<app-header />
<arena-side-nav ariaLabel="Sections">…</arena-side-nav>
<arena-main>
<router-outlet />
</arena-main>
Members, in contract order and under this layer's own names. * marks a required one.
| Member | Form | Type | Default | What it is |
|---|---|---|---|---|
label* | primitive | string | The words a reader reads when the link appears. Required, and guarded at runtime after trimming, the shape ArenaSideNav.ariaLabel carries for the same reason: this is text a person reads and nothing can derive it, and the guard trims first because the value it exists to catch is a present and useless one rather than an absent one, which the type already refuses. There is a defensible default in English and it is deliberately not given, because a default in one language is a wrong answer everywhere else and it is wrong silently. |
Place it first, above everything it exists to skip. Placement is the one of the four details the link cannot do for itself. Placement decides whether the link works at all. Tab order follows the document, so a skip link written under a nav of nine destinations is reached on the tenth Tab. By then the reader has already been through everything the link was going to save them. Write it as the first element of the shell.
Arena owns the other three. Arena keeps the link out of sight until the link takes focus. The link becomes visible the moment it does. Opacity does that rather than mounting. The link stays reachable by Tab at every moment, and nothing appears or disappears from the tree. The link is fixed at the top of the page, on the layering slot directly above nav. A link that lands under a sticky header is a link nobody can read. And the region it points at is focusable programmatically, which arena-main carries: an
anchor pointing at a container the platform will not focus scrolls the page and leaves focus
behind.
The link points at the page's arena-main, by an id Arena writes on both sides. There is nothing to coordinate at the call site and no id to pass. A page has one main landmark. A page with a skip link and no arena-main has a link to nowhere. Nothing can detect that from inside either component, so it is the one thing to check by hand.
Do / Don't
- Do say where the reader lands, in the application's own words. "Skip to content" and "Skip to the order list" are both right; the second is better on a screen whose content has a name.
- Do write exactly one per page, next to the one
arena-main. - Don't put it inside the header or the nav to "keep the shell tidy". Inside either, it is reached after them and skips nothing.
- Don't give it a
classto make it visible for testing. Press Tab, which is the state a reader actually meets.
By hand, in real Chromium: run bun run demos and open
/frameworks/angular/components/layout/arena-skip-link/ArenaSkipLink.demo.generated.html:
- With the page freshly loaded, the first Tab reveals it and nothing else has focus before it.
- The link draws over whatever is beneath it rather than pushing the page down.
- Enter moves focus into the main region, and the Tab after that continues from inside it.
- A second Tab away from it makes it invisible again with no layout moving.
The rules of the language hold in the code you write from this page. An Arena component is not a styling surface, so put no class of your own on it. Read every value through its token, never a raw colour and never a bare 16px. Never wrap it in your router's own link. arena-to-prod --audit reports these three in your sources. The rest are in ../../../../../skills/design/SKILL.md, which marks the ones it reports.