Spaces:
Running
UI Fix Plan — carepath-omega.vercel.app (scribe/frontend/)
Plan authored by Fable 5 after reviewing the live deploy and source. Executor: Opus 4.8.
Scope: the Vite/React app in scribe/frontend/ only. Do the fixes in order. Keep diffs minimal — no redesigns, no new dependencies.
How findings were verified (and how you re-verify)
The live site was screenshotted and probed with the repo's own Playwright
(scribe/frontend/node_modules/playwright-core, headless Chromium) at 1440/1100/900/800/390px widths.
Re-run any probe the same way, e.g.:
// node, from scribe/frontend/: require('playwright-core'), launch chromium,
// newPage({ viewport }), goto https://carepath-omega.vercel.app/ (or local `npm run preview`)
Verify locally against npm run dev/preview in scribe/frontend/ — do not deploy to verify.
Do NOT chase these (verified non-issues)
Full-page/element screenshots of this site produce artifacts because of the sticky nav and GSAP scroll animations. Already confirmed fine:
- Hero "Audio → SOAP" strip overhanging its card (
.hero__route--scribe { right: -3% }) is an intentional accent.document.scrollWidth - clientWidth === 0at all tested widths — no horizontal overflow anywhere. - Grey band across the bottom of the safety bento in screenshots = GSAP scrub timeline
(
scribe/frontend/src/landing/useLandingMotion.ts:45-68) caught mid-state. Renders correctly live. - Scribe chapter body text is complete in source (
scribe/frontend/src/content/strings.ts:340); the "truncation" seen in captures was the sticky nav bar overlaying a line during scroll-stitching. - Marquee is 48px tall and animates correctly; "clipped text" in captures is the same stitching artifact.
P0-1: Lead contact path is dead on production
Symptom: Footer "Liên hệ chương trình thí điểm" renders href="mailto:" (no recipient).
Submitting the pilot lead form opens a recipient-less mail draft and then shows the success-ish
"mail opened" status. The site's only conversion path silently goes nowhere.
Root cause: Neither VITE_LEAD_ENDPOINT nor VITE_LEAD_EMAIL is set in the Vercel project.
submitLead() falls through to buildLeadMailto(payload, email = "")
(scribe/frontend/src/leads.ts:88-130). The deploy gate scribe/frontend/scripts/validate-deploy-env.mjs only
validates VITE_API_BASE + VITE_CONSOLE_URL, so the build passed anyway
(scribe/frontend/vercel.json runs npm run validate:deploy && npm run build).
Fix (all three parts):
scribe/frontend/scripts/validate-deploy-env.mjs: require at least one lead channel — error if bothVITE_LEAD_ENDPOINTandVITE_LEAD_EMAILare empty. Extendscribe/frontend/scripts/validate-deploy-env.test.mjsaccordingly (npm run test:deploy-env).- UI guard, belt-and-suspenders:
scribe/frontend/src/leads.ts/scribe/frontend/src/LeadForm.tsx: if no endpoint and no email,submitLeadmust not openmailto:and the form must show the existing error status (labels.failedpath) instead of "mail opened". Never report success for a no-op.scribe/frontend/src/LandingPage.tsx:476-478: don't render the footer mailto link whenVITE_LEAD_EMAILis empty (render the text non-linked, or drop it).- Existing tests mock
leadEmail/endpointprops (scribe/frontend/src/LeadForm.test.tsx,scribe/frontend/src/leads.test.ts) — add the empty-config case.
- Tell the user to set
VITE_LEAD_EMAIL(and optionallyVITE_LEAD_ENDPOINT) in the Vercel project settings — the value is a business decision; do not invent one. The code fix must be correct with or without it.
Accept when: with no lead env vars, footer shows no dead link and form submit shows an error,
not success; npm run test and npm run test:deploy-env pass; with VITE_LEAD_EMAIL set the
mailto contains the recipient.
P0-2: Mobile nav menu opens off-screen — links unusable on phones
Symptom: At 390×844, opening the hamburger shows a white panel whose link labels are
invisible. Measured panel box: x = -108, width = 304 — a third of it (including all left-padded
label text) is past the left viewport edge. At 900px the panel floats detached mid-nav
(x = 225) under the centered button.
Root cause: scribe/frontend/src/styles.css:2267-2277 — .site-nav__menu > div is
position: absolute; right: 0 anchored to .site-nav__menu (position: relative, line 2248),
i.e. to the 2.6rem hamburger itself, which sits mid-nav between the brand and the VI/EN toggle.
The 19rem-wide panel extends leftward from there.
Fix: Anchor the panel to the nav bar, not the button. .site-nav is position: sticky
(styles.css:129-141) and therefore already a containing block for absolute descendants — the
smallest fix is to drop position: relative from .site-nav__menu (line 2248) and give the
panel a right offset matching the nav padding (right: max(1.5rem, calc((100vw - var(--container)) / 2))
at ≥761px; 1rem-ish at ≤760px — mirror the nav's own padding values). Keep
width: min(19rem, calc(100vw - 2rem)).
Accept when: at 320, 390, 760, and 900px widths the open panel's bounding box is fully inside the viewport, flush near the right edge below the bar, and every link label is visible.
P0-3: Menu never closes after navigating
Symptom: Tap hamburger → tap "An toàn" → page scrolls to the section but the <details>
panel stays open, covering the content just navigated to. Verified: .site-nav__menu[open]
still present after link click.
Root cause: Native <details> doesn't close on child anchor clicks
(scribe/frontend/src/LandingPage.tsx:60-63).
Fix: In LandingPage.tsx, close the details when a menu link is clicked (e.g. onClick on
the wrapping div or each link that clears the open attribute via a ref). Keep it a few lines —
no state library, no outside-click handler unless it's free.
Accept when: tapping any menu link scrolls to the section AND the panel is closed.
P1-1: Evidence section headline wraps 2 words × 6 lines on desktop
Symptom: At 1440px, "Xem cách mỗi sản phẩm giữ điểm cần duyệt ở đúng chỗ." renders 64px in a
379px-wide column → 6 cramped lines. Measured: {w: 379, fs: 64px, lines: 6}; still 4 lines at
1100px. Inside the carousel the slide h3 (up to 2.6rem in a ~270px column,
.evidence-slide col minmax(13rem, 0.65fr), styles.css:2062-2094) wraps ~5 lines the same way.
Root cause: .evidence grid gives the intro minmax(16rem, 0.55fr) of a 74rem container
minus up to 8rem gap (styles.css:2049-2056), while .section-intro h2 uses the global
clamp(2rem, 4.5vw, 4rem) scale (styles.css:1615-1618) sized for full-width intros.
Fix: Scoped override, not a layout rework — e.g.
.evidence .section-intro h2 { font-size: clamp(1.8rem, 2.2vw, 2.5rem); } and similarly cap
.evidence-slide__copy h3 (~`clamp(1.4rem, 1.8vw, 1.9rem)). Alternatively rebalance the column (0.55fr → 0.75fr`) plus a smaller cap — pick whichever reads best live, but the acceptance bar
is below. Don't touch other sections' headings.
Accept when: at 1280–1600px the evidence h2 wraps ≤3 lines with ≥3 words per line, and each slide h3 wraps ≤3 lines; ≤1023px layouts (already fine) unchanged.
P2 (do only after P0/P1; each is a 1–2 line change)
- Sticky nav dissolves into the page while scrolling.
--nav-bg≈ page beige at 0.92 alpha with only a hairline border (styles.css:129-141), so the floating VI/EN pill + hamburger appear to sit directly on content text mid-scroll (most visible on mobile). Add a subtlebox-shadow(or slightly stronger bottom border) to.site-navso the bar reads as a surface. - Marquee labels are 11.84px (
.marquee__track span,font-size: 0.74rem, styles.css:780-791) — uppercase microtext below the 12px floor; bump to0.78rem. - Demo controls wrap ragged on mobile: in
.demo__controls(scribe/frontend/src/demo/DemoPlayer.tsx:251, styles for it in styles.css) "Phát lại" wraps to an orphan row at 390px. Make the wrap intentional (consistent gap; buttons full-width or evenly split at ≤760px — match.product-chapter__actions' existing pattern at styles.css:2429-2433).
Verification (run all from scribe/frontend/)
npm run lint,npm run test,npm run test:deploy-env— all green.npm run build— green WITH lead env vars; FAILS with a clear message when both lead vars are missing (new validator rule).npm run devfor visual checks.- Playwright probe (as above) against local dev at 390 / 760 / 900 / 1440px:
menu panel fully in-viewport, closes on navigate; evidence h2 line counts within budget;
scrollWidth === clientWidth(no new horizontal overflow at any width). - Both languages (VI default, EN toggle) and
#/scriberoute still render —App.test.tsx,LandingPage.test.tsx,ScribeTool.test.tsxcover regressions. - The build's
check-diacritics.mjsruns insidenpm run build— any copy you touch must keep Vietnamese diacritics intact.
Out of scope
Backend/scribe/carepath, the HF-space console (external VITE_CONSOLE_URL target), interpreter/frontend/
(separate app), content rewrites, redesigns, new sections, dependencies.