| Technical |
apps/web/src/app/page.tsx imports Footer but never renders it — dead import and noise for bundlers/linters. |
Low |
Remove the unused Footer import. |
| Technical |
layout.tsx sets body to bg-[#fdf8f0] while index.css applies body { @apply bg-background text-foreground } — theme tokens and dark mode diverge from marketing chrome. |
Medium |
Prefer className="... bg-background" (or a dedicated --cream CSS variable in :root) so light/dark stay coherent with ModeToggle. |
| Technical |
dashboard.tsx logs customerState to the console in production builds. |
Medium |
Delete console.log / console.warn or guard with process.env.NODE_ENV === "development". |
| Technical |
Subscription CTAs only call console.warn — no navigation, toast, or disabled UI; users perceive broken controls. |
High |
Until Polar returns: disabled + tooltip or inline text (“Billing is not available yet”), or toast.info(...). |
| Technical |
consultations-client.tsx recomputes min/max day bounds inside .map() for every calendar cell (42× per render). |
Medium |
Hoist once per render: const minSelectableDay = useMemo(...); const maxSelectableDay = useMemo(...); |
| Technical |
sign-in-form.tsx passes width="250" / height="125" as strings to next/image. |
Low |
Use numeric props: width={250} height={125}. |
| Technical |
page-layout.tsx duplicates Header/Footer but is unused anywhere in apps/web. |
Low |
Remove file or refactor routes to use it intentionally (avoid double chrome with root layout). |
| A11Y |
Home contact overlay (page.tsx) is a plain div — no role="dialog", aria-labelledby, focus trap, or Escape to close. |
High |
Use Radix Dialog / AlertDialog or implement WAI-ARIA dialog pattern + useEffect focus restore. |
| A11Y |
Treatment modals (treatments-page.tsx, landing/treatments.tsx) same overlay gap; close button only, no dialog semantics. |
High |
Same as above; ensure first focusable inside panel on open. |
| A11Y |
consultations-client.tsx: <label> text is not associated with controls (htmlFor / matching id missing on inputs, select, textarea). |
Medium |
Example: <label htmlFor="fullName"> + <input id="fullName" ... />. |
| A11Y |
Calendar month navigation buttons show only ‹ / › with aria-hidden on glyphs — control has no accessible name. |
Medium |
Add aria-label="Previous month" / "Next month" on the Buttons. |
| A11Y |
footer.tsx: “Instagram” and “Facebook” are <span>s, not links — screen readers and keyboard users hit a dead end. |
Medium |
Use <a href="..." rel="noopener noreferrer"> with real URLs (match hero Facebook link). |
| A11Y |
VisualsContent hero image uses alt="logo" — vague for assistive tech. |
Low |
Match branding: alt="Precisione logo" (same as header). |
| A11Y |
Decorative Google SVG in auth buttons should be hidden from AT. |
Low |
Add aria-hidden="true" on the <svg>. |
| A11Y |
user-menu.tsx: DropdownMenuItem wrapping a full Button for “Sign Out” is an awkward composite; email row is focusable menu item without action. |
Medium |
Use DropdownMenuItem with onSelect for sign-out; render email as DropdownMenuLabel or plain text node. |
| A11Y |
mode-toggle.tsx: Moon icon uses absolute positioning but trigger Button is not relative — icon may misalign; ensure stacked icons live in relative container. |
Low |
Add className="relative" to the icon Button. |
| A11Y |
“Scroll down to meet the team” uses animate-pulse — can disturb users with vestibular/motion sensitivity. |
Medium |
Wrap in @media (prefers-reduced-motion: reduce) { animation: none; } or Tailwind motion-reduce:animate-none. |
| UX |
Modals (treatments, home) do not close on backdrop click; users expect click-outside dismiss (common pattern). |
Medium |
onClick on backdrop stopPropagation on panel; close when backdrop receives direct click. |
| UX |
Footer social labels look like links but do nothing — Mom Test drop-off for “find us on social”. |
High |
Real URLs + external link affordance. |
| UX |
Dashboard primary actions give no feedback — highest drop-off for logged-in users exploring “Pro”. |
High |
Align with recommended technical fix (copy + disabled or real flow). |
| UX |
Consultations: date picker popover stays open until a day is picked; no explicit “Cancel” and easy to leave orphan overlay on small screens. |
Low |
Add dismiss control + click-outside + Escape. |
| Visual |
sign-up-form.tsx “Already have an account?” uses text-indigo-600 — breaks OKLCH primary/foreground system used elsewhere. |
Low |
Drop custom indigo; use variant="link" default (primary token). |
| Visual |
Desktop nav uses label.toUpperCase(); mobile drawer uses sentence case — inconsistent brand voice. |
Low |
Choose one convention (title case recommended for readability). |
| Visual |
Mix of arbitrary text sizes (text-[11px], text-[0.68rem]) vs Tailwind scale — weak alignment to 4/8px rhythm. |
Low |
Standardize on text-xs (12px) minimum for body/supporting copy. |
| Performance |
Large next/image fill usages (e.g. doctor cards) should set sizes to reduce wrong intrinsic loads on mobile vs desktop. |
Medium |
e.g. sizes="(max-width: 768px) 100vw, 50vw" on Image fill. |
| Performance |
Home IntersectionObserver effect captures slides from first render; if children change, cleanup may be incomplete. |
Low |
Store sectionRef children in ref updated on layout, or observe in callback ref pattern. |
Synced from Winnow plan Quality Assessment and UI/UX mastery (
c7b3c344-85c6-40eb-823e-c4e6d3f81bb6).Section: Validation Checklist
Status at sync: open
Task key:
validation-checklist/0/visual-dark-mode-on-key-pages-after-background-token-fixDetails
Current Scope
apps/web(Next.js) primary user journeys: marketing home, treatments listing + modals, consultations booking, login/sign-up, dashboard shell.apps/nativedeep dive (separate pass); remote DB changes.Notes
docs/performance-optimization.md.Audit findings (structured table)
Repo paths verified under
/Users/bastionii/Documents/Github/precisione/apps/web/. Date of audit: 2026-05-04.apps/web/src/app/page.tsximportsFooterbut never renders it — dead import and noise for bundlers/linters.Footerimport.layout.tsxsetsbodytobg-[#fdf8f0]whileindex.cssappliesbody { @apply bg-background text-foreground }— theme tokens and dark mode diverge from marketing chrome.className="... bg-background"(or a dedicated--creamCSS variable in:root) so light/dark stay coherent withModeToggle.dashboard.tsxlogscustomerStateto the console in production builds.console.log/console.warnor guard withprocess.env.NODE_ENV === "development".console.warn— no navigation, toast, or disabled UI; users perceive broken controls.disabled+ tooltip or inline text (“Billing is not available yet”), ortoast.info(...).consultations-client.tsxrecomputesmin/maxday bounds inside.map()for every calendar cell (42× per render).const minSelectableDay = useMemo(...); const maxSelectableDay = useMemo(...);sign-in-form.tsxpasseswidth="250"/height="125"as strings tonext/image.width={250}height={125}.page-layout.tsxduplicatesHeader/Footerbut is unused anywhere inapps/web.page.tsx) is a plaindiv— norole="dialog",aria-labelledby, focus trap, or Escape to close.Dialog/AlertDialogor implement WAI-ARIA dialog pattern +useEffectfocus restore.treatments-page.tsx,landing/treatments.tsx) same overlay gap; close button only, no dialog semantics.consultations-client.tsx:<label>text is not associated with controls (htmlFor/ matchingidmissing on inputs,select,textarea).<label htmlFor="fullName">+<input id="fullName" ... />.‹/›witharia-hiddenon glyphs — control has no accessible name.aria-label="Previous month"/"Next month"on theButtons.footer.tsx: “Instagram” and “Facebook” are<span>s, not links — screen readers and keyboard users hit a dead end.<a href="..." rel="noopener noreferrer">with real URLs (match hero Facebook link).VisualsContenthero image usesalt="logo"— vague for assistive tech.alt="Precisione logo"(same as header).aria-hidden="true"on the<svg>.user-menu.tsx:DropdownMenuItemwrapping a fullButtonfor “Sign Out” is an awkward composite; email row is focusable menu item without action.DropdownMenuItemwithonSelectfor sign-out; render email asDropdownMenuLabelor plain text node.mode-toggle.tsx: Moon icon usesabsolutepositioning but triggerButtonis notrelative— icon may misalign; ensure stacked icons live inrelativecontainer.className="relative"to the iconButton.animate-pulse— can disturb users with vestibular/motion sensitivity.@media (prefers-reduced-motion: reduce) { animation: none; }or Tailwindmotion-reduce:animate-none.onClickon backdropstopPropagationon panel; close when backdrop receives direct click.sign-up-form.tsx“Already have an account?” usestext-indigo-600— breaks OKLCH primary/foreground system used elsewhere.variant="link"default (primary token).label.toUpperCase(); mobile drawer uses sentence case — inconsistent brand voice.text-[11px],text-[0.68rem]) vs Tailwind scale — weak alignment to 4/8px rhythm.text-xs(12px) minimum for body/supporting copy.next/imagefillusages (e.g. doctor cards) should setsizesto reduce wrong intrinsic loads on mobile vs desktop.sizes="(max-width: 768px) 100vw, 50vw"onImage fill.IntersectionObservereffect capturesslidesfrom first render; if children change, cleanup may be incomplete.sectionRefchildren in ref updated on layout, or observe in callback ref pattern.Sidequests (nested under timeline)