2026-07-05 — v29 front-door polish: empty-state delight, loading feedback, narrow-mobile operability
Three small, cohesive improvements surfaced by the v28 UX-gallery assessment — bundled in one PR to avoid parallel UI merge conflicts. Each change has genuine exact-value behavior tests and clears the mandatory UX-gallery gate.
1. Empty-state dropzone delight
The old empty state said "Load a Sanger trace" — a generic imperative that gives no spatial cue about where to drop a file or how the data is handled. The updated copy makes three things immediately clear:
- Title: "Drop an .ab1 or .scf file to start" — the accepted formats and the interaction verb (drop) are in the headline where the user's eye lands first.
- Secondary copy: "Your sequencing trace opens entirely in-browser — nothing is uploaded." — the privacy wedge is now primary content, not buried in a ribbon.
- Sample CTA: "✨ Try the sample" — matches the language a first-time user would use. "Load sample" read like a developer command.
-
Drag hint: "or drag & drop anywhere in this box" — "this box" ties the
hint to the visible dashed rectangle, which already has
border: 2px dashed.
A data-testid="empty-state-dropzone" attribute was added to the
#empty-state div so test selectors can target the empty-state dropzone
unambiguously without coupling to the CSS class name.
The .dragging class — added by the existing dragover handler and
removed on dragleave / drop — is verified by a test that dispatches
both events and asserts the exact class toggle.
2. Loading feedback with file name
Previously the loading banner read "Loading sample.ab1…" for the sample and "Loading filename…" for user files — both technically functional but tonally flat. The updated phrasing is "Loading your trace… filename":
- "Loading your trace" signals intent and ownership — the user sees their file acknowledged before the parse completes.
- The file name follows the ellipsis so a user with multiple tabs open can see at a glance which trace is being parsed.
Tests assert the exact string "Loading your trace… sample.ab1" is present in
#loading-text while the sample loads (routed with a 800 ms artificial delay),
and that #loading-banner is hidden once the trace is ready and
#status contains "Loaded sample.ab1".
3. Narrow-mobile keyboard / touch operability
The v28 assessment flagged a coverage gap: the narrow-mobile project ran only the UX gallery capture spec. Core controls were exercised visually but not behaviourally.
This pass documents and tests the intended design:
-
Touch-first, keyboard-also: every CTA in the empty state is a native
<button>or<label>element, so Enter/Space keyboard activation works without any special handling — browsers give it for free. -
44 px touch targets: both the "Choose file" label and "Try the sample"
button meet the WCAG 2.5.5 / Apple HIG 44 px minimum. Tests assert
getBoundingClientRect().height ≥ 44. -
touch-action: noneon the canvas: the chromatogram canvas already carried this attribute (set by the renderer); a test now asserts it explicitly so any future regression is caught immediately. -
Keyboard operability on desktop: tests assert that
#sample-load-btncan be focused and activated via Enter, and that the sidebar toggle can be reached via Tab and operated via Enter — consistent with the existing workspace-shell keyboard tests.
The narrow-mobile Playwright project now includes front-door-polish.e2e.test.ts
in its testMatch so these assertions run against the 360×640 viewport in CI.
Mobile-incompatible tests (those that rely on keyboard Tab model or setInputFiles)
carry a test.skip(isMobile, …) guard with an explicit explanation of the intended
touch-first behaviour.
UX assessment
Is this great UX? Yes, with one caveat. The three changes close real first-impression gaps: the drop-target is now spatially self-explanatory, the loading banner acknowledges the user's file by name, and the touch targets meet the platform standard. The privacy wedge is now front-and-centre rather than in a dismissible ribbon that many users never read. The keyboard/touch operability story is now tested and documented rather than assumed.
What to improve next: The empty-state icon (🧬) is emoji — rendering varies
across platforms and is invisible to screen readers in contexts where aria-hidden
strips it. An SVG illustration would give pixel-perfect cross-platform appearance and could
carry a meaningful aria-label. Additionally, the "Choose file" label still looks
like a link rather than a button on first glance — a proper outlined button style (matching
the "Try the sample" secondary button) would make the affordance explicit. These are tracked
for the next design-system pass.
Tests
tests/e2e/front-door-polish.e2e.test.ts contains 11 genuine behavior assertions
across three describe blocks:
-
Empty-state dropzone (4 tests): exact title text, sample CTA text +
drag hint,
data-testidpresent, dashed border style, drag-over class toggle. - Loading feedback (4 tests): loading text contains "sample.ab1", exact "Loading your trace… sample.ab1" string, banner appears then clears, file-load banner names the file.
- Narrow-mobile operability (5 tests): sample button is a native button, both CTAs ≥ 44 px, canvas touch-action: none, keyboard focus/Enter activates sample load, keyboard Tab/Enter toggles the sidebar.
Zero capability regressions
All changes are additive or text substitutions. No selectors used by existing tests were
removed — #empty-state, #loading-banner, #loading-text,
#sample-load-btn, and .dropzone are all preserved. The UX gallery
capture spec is unaffected: it asserts on element visibility, not text content.