Repository navigation
a11y: no visible skip link — #main-content landmark exists but 19 nav items precede content in tab order #17565
Description
Part of #12730.
Problem
autobot-frontend has the landmark but not the affordance. src/App.vue:536 renders <main id="main-content" …>, and src/App.vue:41 renders the main navigation — but there is no visible skip link, so a keyboard or screen-reader user tabs through the whole navigation on every page before reaching content. The phrase "Skip to main content" appears in 0 files; skip-link appears in 1.
The target already exists, so this is a missing ~10-line affordance rather than a structural change.
Approach
- Add a skip link as the first focusable element in
App.vue, pointing at#main-content. - Visually hidden until focused, then visible with the standard focus ring — the codebase already styles
focus-visiblein 102 files, so match that. - Translate the label in all 11 locales.
- Confirm the target receives focus:
#main-contentalready carries the landmark, and atabindex="-1"on it is what makes the jump actually move focus rather than only the scroll position.
Acceptance criteria
- A skip link is the first focusable element on every authenticated page.
- It is invisible until focused and clearly visible when focused.
- Activating it moves focus (not just scroll) to
#main-content— asserted in a test. - The label exists in all 11 locales.
Implementation Order
Wave: 1
Depends on: —
Unblocks: —
Evidence
src/App.vue:536 (<main id="main-content">) · src/App.vue:41 (main navigation, 19 nav destinations ahead of content in tab order) · 0 files containing "Skip to main content".
Found during a comparative GUI-standardisation audit — see docs/research/dashboard-shell-and-component-standardisation.md, gap G12.
Correction (2026-09-26): the component already exists — in the other GUI
This issue was filed after auditing autobot-frontend only. autobot-slm-frontend already has one:
autobot-slm-frontend/src/components/common/SkipLink.vue (label: "Skip to main content")
autobot-slm-frontend/src/components/common/SkipLink.stories.ts
So this is not "build a skip link", it is promote the existing one into libs/autobot-ui and adopt it in autobot-frontend — the same wire-in-don't-duplicate pattern as #14776/#14778. Building a second implementation would create exactly the kind of fork this umbrella exists to remove.
Revised approach:
- Promote
SkipLink.vue+ its story fromautobot-slm-frontendintolibs/autobot-ui, rendering off the token contract alone. - Leave a re-export at the old SLM path so its call site is untouched.
- Adopt in
autobot-frontend/src/App.vue, targeting the existing#main-contentat line 536. - Keep the
tabindex="-1"requirement from the original acceptance criteria — it is what makes the jump move focus rather than only scroll.
Revised acceptance criteria (replacing the first bullet above):
-
libs/autobot-uiexportsSkipLink;autobot-slm-frontendconsumes it via re-export, not its own copy. -
autobot-frontend/src/App.vuerenders it as the first focusable element. - Label translated in all 11 locales (SLM is not yet on all 11 — see Bring autobot-slm-frontend to all 11 locales #14781 — so the key must exist in the kit's locale surface, not only SLM's).
Depends on #14776 (the fork-retirement pattern this follows).
Activity
docs(research): dashboard shell and GUI-element standardisation audit (…
docs(research): dashboard shell and GUI-element standardisation audit (…
fix(theme): one themed source for notification colour, and a ratchet …
mrveiss commentedon Oct 4, 2026
Premise correction, checked against current
origin/mainbefore implementing.autobot-frontendhas not been without a skip link. Since 2026-02,src/App.vue(first child of#app, insidev-if="showAuthChrome") has rendered two hand-rolled skip links,#main-contentand#navigation, styled visible-on-focus by a local.skip-linkblock.What was actually missing:
nav.skipToContent/nav.skipToNavigationheld the English "Skip to content" / "Skip to navigation" in all 11 locales.<main id="main-content">has notabindex, so activating the link scrolled but left focus where it was.components/common/SkipLink.vue, with a hard-coded English default label, coexisted with the hand-rolled one.So the work is consolidation plus i18n plus the focus fix, not a new affordance. It is being delivered by the batch-3 frontend PR (kit
SkipLinkinlibs/autobot-ui, adopted by both consoles).feat(shell): the command palette is fed by the nav registry, and one …
fix(shell): review round -- safe-area clamp for the canvas context me…
mrveiss commentedon Oct 4, 2026
Delivered by #17971 (merge
90729b5999). Verified againstorigin/main. As corrected on this issue, the work was consolidating onto one kitSkipLink, not adding a first one.App.vue:3-7: the first children of#app, insidev-if="showAuthChrome".SkipLinkfrom@autobot/ui(libs/autobot-ui/index.ts:33). The SLM'scomponents/common/SkipLink.vueis now a thin wrapper over it..aui-skip-link:translateY(-200%)until:focus, thentranslateY(0)with the token focus ring.#main-content— asserted in a test.libs/autobot-ui/src/components/__tests__/SkipLink.test.ts, casemoves focus to a non-focusable landmark when activated(document.activeElementis the<main>,tabindex="-1").nav.skipToContent/nav.skipToNavigationare now translated in all 11; they were English in every locale before.common.skipLink.mainContentis lifted from them by the lifting rule.feat(shell): the command palette is fed by the nav registry, and one …