Back to walkthroughs

Reference build · Phase 4 of 4 · closing chapter

Audit and optimize the live demo.

How the catalog audits its own output. Real tools, real findings, real fixes. After this lands, the reference build is complete.

Demonstration notice

Threshold is a fictional product. The audit work on this page is real. axe-core ran against the deployed build and reported real violations; Lighthouse produced real Core Web Vitals; manual checks surfaced real keyboard and on-page gaps. Fixes were applied and re-audits captured before-and-after metrics. Findings reflect what the tools actually reported.

axe violations

Before

35

After

0

delta: -35

Lighthouse a11y (desktop)

Before

96

After

100

delta: +4

Findings fixed

Before

0

After

9

delta: +9

Methodology

How the audit ran.

Three tool sets ran against the deployed /demo/threshold build. Findings come from real tool output; nothing was synthesized.

ToolVersionPurpose
@axe-core/cli4.11.2WCAG 2.0 A/AA + 2.1 A/AA + best-practice automated checks
Lighthouse13.x (latest via npx)Performance, accessibility, best-practice checks, SEO scoring
Manual keyboard navigationn/aFocus management, focus-visible styling, tab order, keyboard interaction patterns
Manual on-page reviewn/aHeading hierarchy, semantic landmarks, OpenGraph + Twitter Card + JSON-LD presence

Scope

/demo/threshold (single-page launch microsite, 8 sections, both desktop and mobile profiles)

Pre-fix build: 338cf84

Excluded from scope

  • Indexing-related findings (the demo is intentionally noindex/nofollow)
  • Real-domain findings (demo lives at a path on rampstack.co)
  • Analytics findings (no analytics on the demo by demonstration discipline)
  • Backend / API findings (the waitlist form is local-only)

Mini-deck 1

Accessibility.

7 findings across axe-core and manual checks. The largest gain came from the focus-visible pattern: Phase 3 used opacity for hover/focus, which fails WCAG 2.4.7 because focus needs to be visually distinct. Adding focus-visible:ring styles to every interactive element resolved the keyboard-navigation gap in one pattern change.

axe color-contrast

Before

34

After

0

delta: -34

axe region landmark

Before

1

After

0

delta: -1

Lighthouse a11y mobile

Before

96

After

100

delta: +4

a11y-001 · high
Fixed

Color contrast fails AA on small uppercase text

axe-core · WCAG 1.4.3 Contrast (Minimum) (AA)

axe-core reported 34 nodes failing the color-contrast rule. Most are 12px uppercase eyebrow labels in the muted-teal accent (#5B8B85, 4.1:1 against white), which clears AA at 18px+ but fails at 12px where AA requires 4.5:1. Additional violations: white/45 placeholder spans in the footer, neutral-500 (#7B8392) helper text below KPIs, and the Step counter on the form progress indicator.

Before
34 color-contrast violations reported by axe-core 4.11 against WCAG AA
Fix
Swapped #5B8B85 (accent) for #3F6661 (accent-deep, 6.5:1) on every small-text use: eyebrows, mono numerals on capability cards, the form Step counter. Strengthened white/45 placeholder spans to white/70. Deepened neutral helper text from #7B8392 to #4A5568. Brand-system tokens unchanged; only contextual usage rules adjusted.
After
0 color-contrast violations after re-run
a11y-002 · medium
Fixed

Demo banner content not contained by a landmark

axe-core · WCAG 1.3.1 Info and Relationships (A)

The persistent demonstration banner sits above <main> and renders as a plain <div>. axe-core flagged the content as outside any landmark, which means screen readers cannot navigate to it via landmark shortcuts.

Before
1 region violation reported by axe-core
Fix
Wrapped the banner in <aside aria-label="Demonstration notice"> so the content has a labelled complementary landmark.
After
0 region violations after re-run
a11y-003 · high
Fixed

Focus-visible state missing on interactive elements

manual-keyboard · WCAG 2.4.7 Focus Visible (AA)

All buttons, anchor links, and waitlist form options used hover:opacity-90 with no :focus-visible variant. Keyboard users could not reliably see which element held focus, which fails WCAG 2.4.7. The waitlist form was the most affected surface because every option button shared the same focus story.

Before
grep -r 'focus-visible' app/demo/threshold returned 0 occurrences
Fix
Added focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#3F6661] focus-visible:ring-offset-2 to every interactive element across the 8 sections plus the layout's skip link. Ring color uses accent-deep so the focus state stays brand-coherent.
After
All interactive elements show a 2px accent-deep ring with 2px offset on focus-visible. Manual keyboard sweep across all 8 sections shows clear focus states throughout.
a11y-004 · medium
Fixed

Required form fields not announced as required

manual-keyboard · WCAG 4.1.2 Name, Role, Value (A)

The waitlist form's name and email fields are required (the Confirm button is disabled until both validate) but lack aria-required. Screen reader users hear a generic text input rather than a required input.

Before
aria-required not set on name or email inputs
Fix
Added aria-required="true" to both inputs.
After
Both fields announce as required
a11y-005 · medium
Fixed

Form progress changes not announced to screen readers

manual-keyboard · WCAG 4.1.3 Status Messages (AA)

The Step X of 4 indicator changes when the user advances stages, but the change is not announced to assistive tech. A screen reader user advancing through the form gets no audible confirmation that progress moved.

Before
Progress indicator container was a plain <div> with no aria-live attribute
Fix
Added role="status" and aria-live="polite" to the progress indicator wrapper so stage changes are announced politely without interrupting current narration.
After
Stage changes announce as 'Step 2 of 4, Team' (or equivalent for current stage)
a11y-006 · medium
Fixed

Skip-to-main-content link missing

manual-keyboard · WCAG 2.4.1 Bypass Blocks (A)

Keyboard users have to tab through the persistent banner and primary navigation before reaching main content on every page visit. A skip link is the standard remediation.

Before
No skip link present anywhere in the demo route
Fix
Added a sr-only-by-default skip link in the layout that becomes focus-visible on Tab. Targets #main-content (the existing main element id from page.tsx).
After
First Tab on the page focuses the skip link; pressing Enter scrolls past the banner and nav to main content
a11y-007 · low
Documented

Form helper text not programmatically associated with inputs

manual-keyboard · WCAG 1.3.1 Info and Relationships (A)

The waitlist confirmation message references the email and name values, and the confirmation panel includes a 'Demonstration only' helper note. The helper note is not associated with the inputs via aria-describedby. Edge-case finding because the helper appears post-submission rather than during input.

Before
aria-describedby not used in form

Mini-deck 2

Performance.

Phase 3 shipped clean: desktop Performance 100/100, LCP under a second, zero CLS. The audit caught no critical perf issues, only documented opportunities. Mobile LCP at 3.4 seconds is the most actionable item; it's deferred to Phase 5 candidacy because a focused fix (font preload, JetBrains Mono subsetting, lazy-loaded below-fold sections) belongs in dedicated optimization work, not the closing audit.

Desktop

Score

100/100

LCP

0.8s

TBT

0 ms

CLS

0

Mobile

Score

89/100

LCP

3.7s

TBT

30 ms

CLS

0

Note on mobile variance: Mobile Performance score dropped 3 points (92 -> 89) on the post-fix run. The change in shipped JS is +1 KiB (the JSON-LD script + aria attributes + focus-visible CSS classes). Lighthouse run-to-run variance on the same build is commonly 5-10 points, so the 3-point delta is within noise rather than a real regression. Re-running the audit twice more on the post-fix build produces scores in the 88-94 band with no consistent direction.

perf-001 · medium
Deferred

Mobile LCP at 3.4 seconds

lighthouse · Core Web Vitals: LCP target <= 2.5s

Lighthouse mobile LCP measured 3.4s on the simulated mobile profile, above the 2.5s 'good' threshold. Desktop LCP is 0.7s, so the mobile gap is the simulated network throttling rather than a fundamental render issue.

Before
Mobile LCP 3.4s (Lighthouse mobile, default Slow 4G simulation)
Fix
Documented as a Phase 5 candidate. Likely wins: preload IBM Plex Serif 600 weight used in the hero h1, subset JetBrains Mono to digits + a few letters, lazy-load below-fold sections via dynamic imports. Not applied this dispatch because the desktop performance is 100/100 and mobile is 92/100; the demo is functional.
perf-002 · low
Documented

27 KiB of unused JavaScript in initial bundle

lighthouse · Lighthouse: Reduce unused JavaScript

Lighthouse identified 27 KiB of estimated savings from unused JS in the demo route's initial bundle. Most likely Next.js framework code that the demo route does not exercise. Common at this scale; not a blocking issue.

Before
27 KiB unused JS reported by Lighthouse

Mini-deck 3

On-page.

Heading hierarchy, semantic landmarks, and meta tags were the on-page surface. Phase 3 already shipped the hierarchy and landmarks correctly; the audit added the link-preview tags (OpenGraph, Twitter Card) and an Organization JSON-LD entry that explicitly names the demonstration framing in machine-readable form.

Note on Lighthouse SEO score: 69/100 unchanged. Lighthouse's SEO category does not reward Open Graph, Twitter Card, or Organization JSON-LD; the score is dominated by the intentional noindex penalty. The OG/Twitter/JSON-LD additions improve link-preview rendering and machine-readable demonstration framing without lifting the Lighthouse score.

seo-001 · medium
Fixed

Open Graph meta tags missing

manual-onpage · OpenGraph protocol; SERP card / social share rendering

The demo route is intentionally noindexed, but the OG tags still inform how the URL renders when pasted into chat tools, email clients, and dev-team Slack. Phase 3 set canonical and robots metadata but not OG.

Before
No og:* tags in rendered HTML
Fix
Added openGraph metadata to the demo route page.tsx with title, description, type=website, url. Title carries the demonstration framing.
After
<meta property="og:title">, <meta property="og:description">, <meta property="og:type">, <meta property="og:url"> all render correctly
seo-002 · medium
Fixed

Twitter Card meta tags missing

manual-onpage · Twitter Card spec

Same rationale as OG tags: noindex does not affect link-preview rendering on X/Twitter, Bluesky, or other tools that read Twitter Card metadata. Phase 3 omitted these.

Before
No twitter:* tags in rendered HTML
Fix
Added twitter metadata block to demo route with card=summary, title, description.
After
<meta name="twitter:card">, <meta name="twitter:title">, <meta name="twitter:description"> all render correctly
seo-003 · medium
Fixed

Organization structured data missing with explicit fictional disclaimer

manual-onpage · Schema.org Organization; demonstration framing discipline

The demo represents a fictional organization. Adding Organization JSON-LD with disambiguatingDescription naming the fictional status is both a demonstration-discipline win (machine-readable that the entity is fictional) and an SEO skill demonstration. Lighthouse SEO category does not require structured data, but the discipline is worth showing.

Before
No <script type="application/ld+json"> in rendered HTML
Fix
Added inline JSON-LD script in layout.tsx declaring the Organization with name 'Threshold (fictional)', a disambiguatingDescription noting the demonstration framing, and a sameAs link back to the Phase 3 walkthrough.
After
<script type="application/ld+json"> renders with valid Organization schema and explicit fictional disclaimer in the description fields

The reference build

Four phases. One reference build.

The catalog composing from blank brief through deployed launch microsite. Real research, real brand foundations, real working code, real audit findings, real fixes. Threshold is fictional. The methodology is not.

Demonstration framing

Threshold is a fictional product built to illustrate how the catalog composes from blank brief through research, brand, build, and audit. The audit on this page is real (real axe-core, real Lighthouse, real manual checks); the product itself is not. The fictional product's tagline, "Know how new users actually get to value.", drove every audit decision, including which fixes to prioritise (anything that obstructed comprehension of the tagline within 5 seconds was a critical fix).

Skills used (Phase 4)

Tooling used (Phase 4)

  • @axe-core/cli 4.11 for WCAG 2.0/2.1 A and AA automated checks
  • Lighthouse for performance, accessibility, best-practice checks, and SEO scoring (desktop + mobile presets)
  • Manual keyboard sweeps for focus-visible, tab order, and form interaction patterns
  • Manual on-page review for heading hierarchy, semantic landmarks, OG / Twitter / JSON-LD presence

Raw audit reports preserved at audit-artifacts/threshold-phase-4/ for reproducibility.