WEB-AG-5 QA Findings — Analysis
Executive Summary
Session 7 closed the WEB-AG-5 port of the Pride website to Next.js 16 App Router. Runtime acceptance passed: Lighthouse mobile Performance, LCP, CLS, and A11y targets met on /, /events, /cocktail-lounge; axe-core returned zero critical violations on every audited public page; keyboard traversal and focus rings pass on desktop + mobile viewports. One above-fold SSR-flash issue surfaced mid-session and was fixed by converting the first Reveal on five pages to the immediate variant. Build and TypeScript checks pass clean; repo-wide ESLint is not clean but carries only pre-existing errors outside the S7 change scope (tracked as WEB-META-2).
Five items are formally deferred to follow-up WEB-META tickets (lint, CSP img-src, brand-colour contrast, Safari browser verification, Footer TikTok). With those parked, WEB-AG-5 is closed.
Lighthouse (mobile, pre-lock run)
| Page | Performance | A11y | Best Practices | SEO | LCP | CLS |
|---|---|---|---|---|---|---|
/ | ≥ 85 | 100 | 92 | 100 | 464 ms | 0.00 |
/events | ≥ 85 | 98 | 92 | 100 | 260 ms | 0.00 |
/cocktail-lounge | ≥ 85 | 100 | 92 | 100 | 267 ms | 0.00 |
All three pages cleared the spec’s LCP < 2.5 s, CLS < 0.1, and A11y ≥ 90 gates comfortably. Best Practices landed at 92 across the board due to a single deferred violation (CSP img-src — see WEB-META-3 below).
axe-core
Zero critical violations on every audited public page. Zero violations on /events and /functions. Remaining serious count across the site: brand-colour contrast only — deferred as a WEB-META candidate (see Known Deferrals). No serious issues attributable to the App Router port itself.
Keyboard & Focus
- 37 focusable elements on
/; all rendered:focus-visibleon Tab with the project’s amber focus ring - Skip-link (
#main-content) surfaces on first Tab and scrolls correctly - Navigation dropdown + mobile drawer ARIA roles verified (
aria-haspopup,aria-expanded,role="menu", trap working) - Contact topic chips traversable by keyboard
- No focus traps or invisible focus on any route
SSR Flash Fix — Reveal immediate Prop
Above-fold Reveal wrappers were waiting on IntersectionObserver before animating in, producing a visible 100–300 ms opacity: 0 flash on initial render of below-hero sections. The fix: pass immediate to the first Reveal in each page so it animates on mount rather than on viewport intersection.
Pages updated in S7:
| Route | File | Line |
|---|---|---|
/cocktail-lounge | src/app/cocktail-lounge/page.tsx | 48 |
/functions | src/app/functions/page.tsx | 57 |
/artists | src/app/artists/page.tsx | 56 |
/about | src/app/about/page.tsx | 66 |
/find-us | src/app/find-us/page.tsx | 54 |
Home (/) hero already ships with immediate. Employment (/employment) doesn’t use Reveal (inlined Framer Motion variants).
Viewport-triggered Reveal on below-fold sections retained unchanged — they’re the correct pattern for scroll-in reveals.
Reduced Motion
The Reveal component (src/components/motion/Reveal.tsx:43) short-circuits to a plain tag when useReducedMotion() returns true, skipping Framer Motion entirely. Other motion components audited in S6–S7 follow the same pattern. The S7 immediate conversions don’t change reduced-motion behaviour — the useReducedMotion gate runs before the immediate branch.
Safari
Formally deferred. No webkit MCP available in the S7 session environment; visual verification of backdrop-filter, per-page blob border-radii (S6), and the Syne font on Safari was not performed this session. Chrome-based Lighthouse/axe runs are clean. Tracked as a WEB-META candidate.
Viewport Sweep (375 / 768 / 1440)
Sampled post-edit via the preview server on /cocktail-lounge: no horizontal scroll at any of the three widths. Prior sessions verified the full page set at these breakpoints.
Unsplash Scrub
grep -r "unsplash" src/ returns zero hits. All 7 Unsplash fallbacks replaced with repo-owned imagery per Step 8. Other spec grep gates (prideoffootscray, hello@prideof, Tailwind dark: prefix) also return zero true hits. The four dark: grep results in src/app/globals.css are CSS variable names (--color-primary-dark, --color-surface-dark) and a cautionary comment — not Tailwind prefix usage.
MotionWrapper Audit
MotionWrapper.tsx, AnimatedGrid.tsx, and SectionDivider.tsx deleted in S7. Grep for MotionWrapper|FadeInSection|StaggerSection|HoverCard across src/ returns zero hits — orphaned and removed cleanly.
Build / TypeScript / Lint
npx tsc --noEmit→ exit 0 ✓npm run build(Next 16, Turbopack) → exit 0 ✓npm run lint→ exit 1 (175 errors, 5 warnings) — accepted deferral
Lint Deferral Detail
All five S7-touched files lint-clean on scoped check. Repo-wide lint fails, but all 175 errors are pre-existing, in files S7 did not touch:
src/components/shared/PortableText.tsxsrc/lib/sanity/image.tssrc/lib/sanity/types.tssrc/lib/events/index.tssrc/lib/merch/index.ts- Plus several others (rule mix:
@typescript-eslint/no-explicit-any,react/no-unescaped-entities,react/jsx-no-undef)
Errors predate S1 of this migration. Count dropped from 179 at the start of S2 to 175 at end of S7 — the S7 a11y fixes incidentally cleared a few. Not a regression from the port.
The S7 spec acceptance criterion “npm run lint passes” was written against an assumed-clean baseline that didn’t exist — the same defect was retroactively corrected in S2 for a similar criterion. Resolution deferred to WEB-META-2.
Known Deferrals
| Ticket | Area | Notes |
|---|---|---|
| WEB-META-2 | Repo-wide ESLint cleanup | 175 pre-existing errors in non-S7 files; dedicated post-AG-5 resolution |
| WEB-META-3 | CSP img-src | Single Best-Practices point deduction on Lighthouse; needs CSP directive tuning for Sanity CDN + Instagram oEmbed |
| WEB-META-4 (candidate) | Brand-colour contrast | Remaining axe serious violations; decide whether to adjust tokens or formally accept the brand trade-off |
| WEB-META-5 (candidate) | Safari browser verification | Full visual pass on backdrop-filter, per-page blob radii, Syne font; requires webkit environment |
| Post-AG-5 chore | Footer TikTok link | Once the venue’s TikTok handle is confirmed, add it alongside Instagram/Facebook |
Session 7 Change Summary
- Unsplash fallbacks removed (7 files → repo-owned imagery)
Revealprimitive migration completed (replaced legacyMotionWrappereverywhere)AnimatedGrid.tsx,MotionWrapper.tsx,SectionDivider.tsxdeleted (no remaining callers)- Contact page phone placeholder swap
layout.tsxmain-content wrapper changed from<main>→<div id="main-content">now that every route’spage.tsxprovides its own<main>landmark (verified: 7/7 routes)- Input/textarea/select a11y — focus rings, ARIA labelling, describedby wiring
- Footer a11y — heading hierarchy, link labels
- Five above-fold
Reveal→immediate(SSR-flash fix, this session) public/axe.min.jsdeleted (551 KB test artefact)
Closure
WEB-AG-5 acceptance criteria are met or formally deferred with ticket pointers. The Next.js 16 App Router port is the live state. Any follow-up work lands under the WEB-META line.