feat(ui): calmer motion + first-paint primitives across paramant frontend#26
Merged
Merged
Conversation
Improves the calm "windstil" feel of the design system without breaking its brutalist tone: - design-system: add --duration-slow, --ease-soft, --ease-out tokens; swap harsh blink-step status pulse for an eased opacity+scale pulse; add page-enter / rise-in entrance utilities with stagger delays; add subtle press feedback (translateY 1px) on .btn:active; give :focus-visible a small offset transition; lift help-card on hover with arrow nudge; add scroll-padding-top so anchor targets clear the sticky 56px navbar (auto under prefers-reduced-motion). - nav: animate desktop dropdown reveal (fade+rise+scale); slide-in mobile drawer; max-height transition on mobile accordion groups; CSS-driven hamburger cross via [aria-expanded="true"] (replaces inline JS transforms); animated underline on .nav-link via ::after. - nav.js: drop inline span style writes now that CSS owns the hamburger state.
… + indeterminate Audit found several "map too far zoomed out at start of nav" UX moments: the dropzone reads as a cold rectangle on first visit, dashboard tables show only "—" for new users, and "Preparing…" states leave users staring at frozen 0% bars. These additions are opt-in primitives that surfaces can adopt page by page. - Dropzone: subtle 3.6s lime breathe ring on idle (pauses on hover/focus/drag), distinct cobalt 4px ring on .drag-over so the active drop target is unmissable. - .empty-state component (with .is-compact variant) — glyph + title + hint + actions slot; replaces walls of em-dashes in zero-data tables with an inviting "what to do next" surface. - .skeleton + .skeleton-line (sm/md/lg) + .skeleton-block — calm shimmer for loading placeholders; the dashboard's flickering height:28px placeholder divs can adopt this directly. - .progress-indeterminate — slim 3px bar with a sliding cobalt segment for "Preparing…" / "Connecting…" states with unknown ETA. - Reduced-motion: skeleton stays grey (not transparent), indeterminate bar pins to a visible position, dropzone breathe stops.
…ss key pages Wires the new design-system primitives into the three pages flagged as worst offenders for "starts too zoomed-out" UX. dashboard.html: - stat-card placeholders now use .skeleton lines instead of flickering opacity:.3 height:28px divs (reads as loading, not as broken) - delivery table loading state shows progress-indeterminate + skeleton rows in place of "Loading..." - delivery table zero-data shows .empty-state with title, hint and a "Run E2E test" CTA so new users have a clear first move - audit log gets the same treatment (skeleton on load, .empty-state with explanatory hint when data has loaded but is empty) get.html: - "Preparing..." no longer leaves the user staring at a 0% bar — an indeterminate sliding bar shows up first; once a real percentage arrives, setStatus swaps in the determinate progress-bar drop.html: - camera failure (NotAllowedError / NotFoundError / non-secure context) no longer silently hides the scanner box; it shows an .empty-state explaining the cause and pointing at the manual-link fallback below
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three commits that tighten the "windstil" feel of the paramant frontend without breaking its brutalist tone. The brief was: improve usability via animations, visuals, and how navigation behaves — and address pages that open "too zoomed-out" (no orientation, sparse forms, frozen 0% bars, silent failures).
1. Calmer motion across nav, status & cards (
f49c85d)--duration-slow,--ease-soft,--ease-out.blink-stepswapped for an eased opacity+scalestatus-pulse.translateY(1px)press feedback.:focus-visiblegets a small offset transition..page-enter+.rise-in(withdelay-1..5stagger).scroll-padding-top: 72pxso anchor links clear the sticky 56px navbar (auto under reduced-motion).display:blockflits; mobile drawer slide-in; mobile accordion uses max-height transition; hamburger cross is now CSS-driven via[aria-expanded="true"](inline JS span transforms removed); active link gets an animated underline via::after.2. First-paint primitives in the design system (
d76f3b9)Opt-in components for surfaces that currently feel empty or stuck:
.drag-over..empty-state(+.is-compact) — glyph + title + hint + actions slot for zero-data surfaces..skeleton+.skeleton-line(is-sm/is-md/is-lg) +.skeleton-block— calm shimmer for loading placeholders..progress-indeterminate— 3px bar with sliding cobalt segment for unknown-ETA states.prefers-reduced-motionwith sensible static fallbacks.3. Wired into the worst offenders (
d36e8b0)dashboard.html: stat-cards use skeleton lines instead of flickeringopacity:.3divs; delivery table shows indeterminate bar + skeleton rows on load and an.empty-statewith a "Run E2E test" CTA when zero deliveries; audit log gets the same load + empty treatment.get.html: "Preparing…" no longer shows a frozen 0% bar —setStatus()swaps an indeterminate sliding bar in until a real percentage arrives.drop.html: camera failure (NotAllowedError/NotFoundError/ non-secure context) replaces the silentdisplay:nonewith an.empty-stateexplaining the cause and pointing at the manual-link fallback.Files touched
frontend/design-system.css— tokens, motion utilities, dropzone, empty-state, skeleton, indeterminate, motion-safety updates.frontend/nav.css— animated dropdown/drawer/accordion/hamburger/active-link.frontend/nav.js— drop the inline span style writes (CSS owns hamburger state now).frontend/dashboard.html— wire skeleton + empty-state into stat-cards / delivery / audit.frontend/get.html— wire indeterminate bar into Preparing state.frontend/drop.html— wire empty-state into camera failure path.Not in this PR (intentional)
.empty-state/.skeletontoaccount.html,auth/*,admin.html— those need copywriting decisions first..page-enter/.rise-inutilities are defined but not yet applied to<main>blocks across the 48 marketing pages.Test plan
/dashboardwithout an API key — login form renders unchanged./dashboardwith a valid key — stat-cards show shimmering skeletons until the first poll, then real numbers./dashboardwith no transfers yet — delivery table shows the empty-state with the "Run E2E test" CTA; clicking it triggers the existing E2E flow./dashboardwith no audit events — audit log section shows the empty-state hint./getlink on a slow connection — confirm the indeterminate bar slides during "Preparing…" and is replaced by the determinate bar once decryption progress is reported./dropin receive mode, deny camera permission — confirm the black box is replaced by an explanatory empty-state./send— confirm dropzone shows the subtle lime breathe ring on idle, switches to a cobalt ring during a drag-over.prefers-reduced-motion: reduce— skeletons stay grey (not transparent), indeterminate bar pins to a visible position, dropzone ring stops, dropdown/drawer/page transitions instant.Generated by Claude Code