Young's Mind
Product Spec

Pulse — Mobile-First Responsive Redesign Plan

Owner: Nova (Design) · Line of execution: Ace (Engineering) via A2A Priority: High · Status: Plan v1.0 — ready for implementation Stack: Nuxt 4 + Vue 3 + Tailwind CSS v4 (@tailwindcss/vite) + nuxt-auth-utils

Pulse — Mobile-First Responsive Redesign Plan

Owner: Nova (Design) · Line of execution: Ace (Engineering) via A2A Priority: High · Status: Plan v1.0 — ready for implementation Stack: Nuxt 4 + Vue 3 + Tailwind CSS v4 (@tailwindcss/vite) + nuxt-auth-utils

Design lens: Every layout decision is framed as "what is the user doing, and does the interface remove friction?" Mobile is not a shrunk desktop; a 320px user gets a different information architecture, not a tighter squeeze.


1. Current State Audit — What Breaks on Mobile Today

🔴 Blocking / visibly broken

#LocationIssue
B1/account portfolio table5-column table in overflow-x-auto forces horizontal pan on 320px — no card fallback.
B2/watchlist table6-column table (Ticker/Price/Change/Volume/Strength/Chart) overflows; no responsive variant.
B3Dashboard brief portfolio tableSame 3-col table inside the Daily Brief card.
B4Sticky nav gapInline nav hidden <lg; hamburger + search crowding; SearchBox only in the hidden drawer (search is 2 taps away on mobile).
B5Podcast archive &lt;audio&gt; in flex rowOn 320px the row is ~300px+; number/title wrap under fixed-width audio; native controls ~10-14px (below 44px).
B6Research date rail horizontalWorks (overflow-x-auto) but buttons ~30px tall (below 44px) and no edge-fade to signal scrollability.

🟠 Degraded UX

#LocationIssue
D1Dashboard 2-col gridRail (watchlist/markets) stacks BELOW main on mobile — buried after 5 tabs of scroll.
D2Dashboard tabsoverflow-x-auto works, but small hit area, no scroll-snap, no edge-fade.
D3AI Research chat on /stock/tickerinput+Ask row can overflow 320px; input py-2 (<44px).
D4IndexStripHorizontal scrolls but no touch-action pan-x / edge fade — sticky on iOS.
D5/watch tilesgrid-cols-2 is good; tile tap area short.
D6Stock price herotext-3xl wraps; label stacking un-rhythmic on 320px.
D7SearchBoxFixed w-28/w-44 focus widths; in drawer only — not full-width on mobile.

🟡 Missing entirely

#Gap
M1No PWA manifest — can't install/offline.
M2No service worker / offline shell (nuxt.config has no PWA module).
M3No lazy image loading / explicit route code-splitting.
M4No pull-to-refresh — feed pages have buttons but no touch gesture.
M5No focused focus-visible states — relies on default/inconsistent ring.
M6No documented breakpoint token set.
M7Chat composer not pinned above iOS keyboard (page uses h-full).

2. Breakpoint Strategy

Tailwind v4 defaults. Mobile-first — base = mobile, sm/md/lg/xl = progressive enhancement.

TokenMin-widthGrid / layout intent
base320pxSingle column. Full-bleed cards, stacked forms/audio, mobile nav drawer, safe-area padded.
sm640pxTables → 2-col card grids; research rail stays horizontal; hero stacked.
md768px2-up card grids; tables show 3-4 cols; inline audio returns.
lg1024pxTHE breakpoint. Desktop inlined nav returns; 2-col page grids; hamburger disappears; search in header.
xl1280pxContent max-width; rail goes 340px; more whitespace.

Rules: every component designed at 320px first. Use dvh not vh. Respect env(safe-area-inset-bottom) on fixed bottom elements.


3. Component-by-Component Responsive Spec

3.1 AppHeader.vue 🔴 B4

Mobile: [Pulse logo] ... [🔍 search-icon toggle] [☰ hamburger]

  • Search → collapsible full-width row under header bar (1 tap to search), not only in drawer.
  • Hamburger h-11 w-11 (already 44px ✔). Drawer items min-h-[48px] ✔ + SVG icons + active left-accent.
  • lg+: existing inline nav + full SearchBox + date pill + account (unchanged).

3.2 Dashboard index.vue 🔴 B3 / 🟠 D1,D2

  • Mobile reorder: rail ABOVE main on mobile via CSS order (2-col only at lg): &lt;div class="order-2 lg:order-1"&gt;main&lt;/div&gt; &lt;div class="order-1 lg:order-2"&gt;rail&lt;/div&gt;
  • IndexStrip pinned under header all sizes.
  • Tabs: add scroll-snap-x snap-mandatory, min-h-[44px], edge-fade mask.
  • Portfolio table (B3): <sm → stacked holding cards (ticker/price/chg% per line). Table sm+.
  • News/MA50/movers: already stacked flex rows ✔ keep.

3.3 Research research.vue 🔴 B6

  • Mobile: date rail ON TOP, horizontal swipe, edge-fade mask + scroll-snap.
  • Date buttons min-h-[44px] px-3.5.
  • Brief tables: stacked cards <sm (same as B3/B1).
  • lg+: existing lg:grid-cols-[220px_1fr] vertical rail (unchanged). Keep Show more/less.

3.4 Podcast podcast.vue 🔴 B5

  • Episode row mobile-first: flex-col gap-3 sm:flex-row sm:items-center; audio w-full sm:w-64.
  • Native audio too small on iOS → wrap with custom ≥44px play row, full-row tap toggles play (aria-live); native controls on md+.
  • Latest episode card: same stacking (emoji + meta + full-width audio).
  • Keep playbackRate=1.25 ✔.

3.5 Chat chat.vue 🔴 M7

  • Root h-[100dvh]; composer sticky bottom-0 + pb-[max(0.5rem,env(safe-area-inset-bottom))] (above iOS keyboard + home indicator).
  • Textarea py-3, send btn h-11 w-11, suggestion chips py-3.
  • Bubbles max-w-[88%] (base) → sm:max-w-[78%].
  • Persist draft on route change.

3.6 Account account.vue 🔴 B1

  • Sidebar → horizontal segmented tab strip (<lg), vertical at lg: &lt;nav class="flex gap-1 overflow-x-auto lg:flex-col lg:overflow-visible"&gt; + min-h-[44px].
  • Portfolio table (B1): <sm stacked cards; sm+ table.
  • Sign-in modal: max-h-[90dvh] overflow-y-auto for tiny landscape.

3.7 Watch / Watchlist 🟠 D5 / 🔴 B2

  • /watch tiles: grid-cols-2 sm:3 md:4 lg:5 + min-h-[92px].
  • /watchlist table (B2): <md → card stack (ticker/price/change/volume/strength/Chart→). Table md+.
  • Sort tabs min-h-[44px].

3.8 Stock ticker 🟠 D3

  • AI Research input flex-1 min-w-0 + py-3; button px-3 sm:px-4.
  • Chat panel max-h-[50vh] mobile.
  • Price hero text-2xl sm:text-3xl.

4. Tailwind Utility Classes To Use

Layout/order: grid-cols-1 grid-cols-2 sm:grid-cols-3 lg:grid-cols-[1fr_320px] lg:grid-cols-[220px_1fr] lg:grid-cols-[260px_1fr] · order-1 order-2 lg:order-1 lg:order-2 · flex flex-col sm:flex-row sm:items-center · sticky top-0 bottom-0 · h-[100dvh] min-h-[100dvh] · pb-[max(0.5rem,env(safe-area-inset-bottom))]

Touch targets: min-h-[44px] min-h-[48px] h-11 w-11 py-3 px-4

Swipe rails: overflow-x-auto scroll-snap-x snap-mandatory snap-start · [-webkit-mask-image:linear-gradient(to_right,black,black_90%,transparent)] · touch-pan-x overscroll-x-contain

Focus/a11y: focus-visible:outline focus-visible:outline-2 focus-visible:outline-brand-500/60 · aria-selected:bg-brand-600/15 aria-selected:text-brand-300 · scroll-mt-24

Tables→cards pattern: grid grid-cols-1 gap-2 md:hidden (cards) + hidden md:table (table)


5. Migration Steps (Phased, Non-Breaking)

PhaseScopeShip signal
P0PWA manifest + offline shell (@vite-pwa/nuxt); focus-visible + safe-area CSS foundationbuild passes; /site.webmanifest; Lighthouse PWA ≥90
P1Header mobile search + SVG icons + drawer polish; chat 100dvh + keyboard-safe composer; bump send/textareasearch 1 tap at 320px; composer above keyboard
P2Table→card sweep (Dashboard, Account, Watchlist, Research) via shared componentsno horizontal pan on 320px in those spots
P3Research rail abovetrail order + edge fade + tap targets; channel rails scroll-snap; Podcast audio full-width + play rowsclean vertical 320px flow on all 6 pages
P4Dashboard rail-above order; min-44px sweep; focus-visible; WCAG contrast reviewLighthouse a11y ≥95; tab through site
P5Route code-split audit; lazy images; pull-to-refresh on /, /watch, /newsLighthouse Performance ≥85 mobile

Each phase is an isolated commit → git revert <phase> for rollback.


6. Testing Checklist

Devices

320px (SE/fold), 390px (iPhone Pro, portrait+landscape), 360px (Android), 600-820px (tablet), 1280/1920 (desktop).

Interaction

  • All tap targets ≥44px
  • Swipe date rail/index strip/tabs: fade + snap work
  • Pull-to-refresh on /, /watch, /news
  • Chat composer above iOS keyboard + home indicator
  • Hamburger→drawer→Esc/backdrop closes cleanly
  • Mobile search: 1 tap→expands→focused→navigates

Browsers

Safari/iOS 16+, Chrome/Android, Samsung Int; Chrome/Firefox/Safari/Edge desktop; audio playback on iOS Safari + Android Chrome (autoplay restrictions).

A11y (WCAG AA)

  • focus-visible on all interactives
  • Screen reader: nav landmark, drawer aria-expanded, audio labelled, tables caption/scope
  • Color contrast muted on surface ≥4.5:1
  • prefers-reduced-motion respected
  • axe + manual touch-target audit

Performance (Lighthouse, mobile 4G)

  • LCP <2.5s · code-split routes · PWA installable + offline · non-blocking hydration

7. Risk & Reversibility

DecisionAlternativesTrade-offChosenReversible
Table→card <mdkeep horizontal scroll all sizessimplest vs best UXshared component dual renderEasy
Rail-above on mobilekeep current bottom railchange vs buries watchlistCSS order onlyEasy
PWA now vs laterlateradditive/unblocks installnow (P0)Easy
Pull-to-refreshskip/customcustom must not fight scrollPhase 5 feed pages onlyMedium

Cost: CSS/component only — NO API/server/data changes.


8. Handoff to Ace

Dispatch per phase: "Implement per designs/responsive-redesign.md section {n}. Nuxt 4 + Tailwind v4, additive + revertible. Verify npm run build + 320px iOS emulation sweep. Commit feat(pulse): {summary}. Report diff + Lighthouse."


9. Design Tokens (locked, no change)

surface #09090b · card #101014 · card2 #16161c · muted #a1a1aa · foreground #e4e4e7 · brand mint scale · Inter · square radius (2026-08-13 lock)


Reviewed end-to-end against live codebase. Design rationale: single-column mobile flows, thumb-reachable primaries, swipe-native lists; desktop stays dense + untouched. — Nova

Copyright © 2026