v19 — Progressive Disclosure + Toolbar Hierarchy
The problem
The toolbar opened as a flat wall of ~15 buttons at identical visual weight, with six separate export actions (PNG, SVG, FASTA, FASTQ, QUAL, Consensus FASTA) and a Print button all sitting in a row beside zoom/pan/fit/strand controls. To a first-time visitor — or even to a returning user — the signal-to-noise ratio was terrible: almost everything was the same shade of grey pill, nothing indicated what mattered most, and the export actions dominated the toolbar real estate without offering any primary/secondary hierarchy.
Three independent design critiques (from the cross-agent review and the competitor research in v17) all flagged the same root issue: "toolbar mixes shorthand, export verbs, and view controls in one flat strip — no grouping, no disclosure, no progressive hierarchy." This unit fixes that.
What changed
Labelled role=group clusters
The toolbar is now divided into four labelled role="group" clusters, each with a
small all-caps label rendered via .controls-group__label:
- View — Zoom +, Zoom −, ← Pan, Pan →, Fit, 5′→3′
- Export — a single Export ▾ toggle button that reveals the dropdown
- Edit — ↩ Undo, ↪ Redo
- Search / Q-trim / Mixed — unchanged sub-panels, now labelled
Each cluster is visually separated by a border and subtle raised background, creating a clear spatial grouping that works in both light and dark themes via design tokens.
Export ▾ dropdown menu
All seven export actions (PNG, SVG, FASTA, FASTQ, QUAL, Consensus FASTA, and Print / Save as PDF)
are collapsed behind a single Export ▾ toggle button. The toggle has full ARIA
semantics: aria-haspopup="menu" and aria-expanded (toggled on open/close).
The dropdown itself carries role="menu" and each item has role="menuitem".
Keyboard behaviour:
- Escape closes the dropdown and returns focus to the toggle.
- Clicking outside the export group closes the dropdown.
- Triggering any export action closes the dropdown automatically.
- Individual export items are not Tab-reachable when the menu is closed — only the Export ▾ toggle sits in the Tab order, reducing keyboard noise for users who don't need exports.
setControlsDisabled update
setControlsDisabled now skips individual export dropdown items (they are managed as
a unit via the toggle button). The toggle itself is disabled during loading and enabled once a
trace is resident — matching the all-or-nothing semantic that makes sense for an export menu.
setConsensusFastaButtonState and setPrintButtonState still target
individual items inside the dropdown by data-action, so their fine-grained logic
is preserved.
New exported function: setExportMenuOpen
setExportMenuOpen(controls, open) is the single source of truth for the dropdown's
open/closed state. It flips aria-expanded on the toggle and toggles the
hidden attribute on the dropdown div.
Tests added
tests/e2e/toolbar-hierarchy.e2e.test.ts adds nine genuine behavioural assertions:
- Toolbar has labelled View, Export, Edit groups (checks
data-groupattributes and visible label text). - No flat export buttons at the top level of the toolbar — the dropdown is hidden by default.
- Export toggle opens the dropdown; all 7 export menuitems are enumerated by exact name.
- Escape closes the dropdown.
- Clicking outside the export group closes the dropdown.
- After the sample auto-loads, the export toggle is enabled.
- After loading a user trace, all view group buttons and the export toggle are enabled.
- Export menu toggle is Tab-reachable; individual export items are NOT reachable while the menu is closed.
- Export toggle has
aria-haspopup="menu"andaria-expandedreflects the open state.
All existing export tests were updated to open the export menu before clicking individual export actions.
The downloadContent, downloadFastaSequence, downloadFastaContent,
downloadFastq, and clickPrint helpers in six test files were updated to
first click Export menu and then the specific menuitem.
Accessibility
- Every group has an
aria-labelon itsrole="group"element. - The export toggle has
aria-haspopup="menu"andaria-expanded. - The dropdown uses
role="menu"withrole="menuitem"children. - Escape closes the dropdown and returns focus to the toggle button.
- The group labels are
aria-hidden="true"decorative labels — the group'saria-labelis the accessible name. - All existing
:focus-visiblerings apply to the new toggle button.
Light/dark theme
All new CSS uses existing design tokens exclusively:
--color-surface, --color-surface-raised, --color-border,
--color-border-strong, --color-accent-bg, --color-accent-border,
--color-brand. No hard-coded colours were added. The dropdown box-shadow uses
rgba(0,0,0,…) which adapts naturally across themes.
Preserved features
Zero export logic was changed. All data-action attributes on the export buttons
are unchanged, so the action dispatch in TraceViewer.ts required no modification
to the export handlers themselves. The undo/redo, search, Q-trim, and mixed-base controls are
structurally unchanged — they are now wrapped in labelled groups but otherwise identical.
Next
The remaining acceptance criteria from the UI redesign issue (#19): ORF/restriction as a visual track, automated a11y contrast checks, and the README/discoverability improvements.