← Back to devlog

2026-07-05 — v28 workspace delight + responsive + state-persistence polish

The v27 shell restructure gave us the right bones — hero chromatogram, collapsible tabbed sidebar, two-column layout. This pass adds the finish that makes it genuinely frictionless: motion that respects user preferences, a narrower density that fits more tool surface without crowding, a sidebar that gracefully adapts at tablet widths, and a permalink that actually reproduces the exact workspace layout the sender was looking at.

What changed

Motion / prefers-reduced-motion

A @media (prefers-reduced-motion: reduce) guard now zeros out transition and animation durations across the entire workspace shell subtree (.app-shell, descendants, and pseudo-elements). The existing guards for the clone-screen were already in place; this pass extends the same discipline to the full workspace shell so users who opt out of motion get a completely still UI, not a partially still one.

Density refinements

Narrow-viewport (tablet) refinements

At ≤ 900 px the sidebar already stacks below the hero. This pass tightens the UX:

Permalink state persistence for sidebar UI

Before this pass, copying a link and sending it to a colleague would restore the trace state but drop them onto the default Inspect tab regardless of which tab the sender had open. Now PermalinkStateV1 carries an optional ui field:

ui?: { sidebarOpen: boolean; activeTab: 'inspect' | 'map' | 'analyze' | 'share' }

getPermalinkState() reads the live DOM to capture sidebar-open state and active tab; applyPermalinkState() restores both when hydrating from a hash. The sidebar toggle and tab clicks now call schedulePermalinkPersist() so the hash updates immediately on interaction — no reload required to keep the link current. Links without a ui key (old links) continue to work and default to sidebar-open + Inspect tab.

Tests

tests/e2e/workspace-polish.e2e.test.ts contains ten genuine behavior assertions:

Zero capability regressions

The entire change is additive: new CSS media query blocks, a new optional field on the permalink schema, and two lines in applyPermalinkState. Every existing test anchor, control ID, and E2E selector is unchanged.