Results: aphshalloffame.com (after migration)

Measured: 2026-10-09, against main at e004699 and the live site at https://www.aphshalloffame.com/. Re-measured on the live site the same day after the quick-win fixes were deployed (4efc2c8, PR #12). See Quick-win fixes. Baseline for comparison: BASELINE.md

Before and after

MeasureOld site (Oct 2020)New site (Oct 2026)
Inductee profiles88141 (every class, 2003 to 2026)
Most recent class shown2016, homepage last modified 2015-12-182026
Pages in the sitemapNo sitemap155
HTTPSNoYes. http:// returns a 308 redirect, and HSTS is set for 2 years (max-age=63072000)
Works on a phoneNo viewport tagResponsive layout with a mobile menu
Unique page titles1 (“Main Page @ aphshalloffame.com”)152 of 154 pages. Donate and Contact were using the homepage’s title and description (fixed below)
Meta descriptionsEmpty154 of 154 pages have one
Images with alt text0 of 6 sampledProfile photos use the inductee’s name. Homepage grid links read out each name. Ceremony photos use file names (see backlog)
Lighthouse Accessibility5396
Lighthouse SEO82100
Lighthouse Best Practices82 (overstated, see baseline)96 to 100 (89 to 96 before the quick wins)
Lighthouse Performance94 to 9983 to 94 (76 to 94 before the quick wins, see below)
Files edited to add an inducteeUp to 107 (the sidebar is copied into every page)1 data file (db/members.json). Profile, directory, homepage grid and sitemap are generated from it
Sidebar copies in syncNo: 88, 76 and 63 names on different pagesSingle source, so always in sync
Ceremony photosOne page load per photoOne gallery page per year with a slider, grid and lightbox
FormsNoneContact and Bio Nomination
Events and donation infoNoneEvents page with invitation and ad PDFs, Donate page with form

The class of 2026 (10 inductees, the event page, the invitation and ad PDFs, and the menu) went live in one commit, 26c0d72, which changed 11 files.

Lighthouse

This section is the first measurement, at e004699, before the quick-win fixes. For the numbers after the fixes, see Live site after deploying.

Same setup as the baseline: Lighthouse 12.6.1, Chrome 154, mobile preset with simulated throttling, Incognito, 3 runs per page, median reported. The same three pages as the baseline, measured on the live site (Vercel) and on a local production build (pnpm build && pnpm start) for a like-for-like comparison with the baseline’s local copy.

PagePerformanceAccessibilityBest PracticesSEO
Home (/)84 (local 81)9696100
Inductee (/inductee/JosephOReedJr)94 (local 90)9696100
Ceremony (/ceremony/2010)76 (local 79)9689100

Live site:

MetricHomeInducteeCeremony
FCP0.95 s0.86 s0.98 s
LCP4.5 s3.1 s7.7 s
TBT0 ms0 ms0 ms
CLS000
Total transfer1,300 KiB436 KiB2,977 KiB
Requests1651959
Photos shown10 portraits (141 downloaded)119

Old vs new, same page

PagePerformanceAccessibilityLCPTransfer
Home99 → 8453 → 962.1 s → 4.5 s216 → 1,300 KiB
Inductee94 → 9453 → 962.6 s → 3.1 s303 → 436 KiB
Ceremony95 → 7653 → 963.0 s → 7.7 s366 → 2,977 KiB

Reading the Performance numbers

The new site scores lower on Performance for the homepage and the ceremony page. Two reasons:

  • The homepage downloads every portrait. MemberGallery starts with all 141 members (useState(members_db)) and filters to the 2026 class only after the page loads. Visitors see 10 portraits but download 141 (142 image requests, 737 KiB of images). The old homepage had no photos at all.
  • The ceremony page does more. The old ceremony page showed one thumbnail. The new one loads the whole 2010 gallery of 19 photos. Per photo the cost is about the same: roughly 157 KiB per photo on the new gallery, against about 155 KiB for each old photo page after the first (estimated from the 2010c1.jpg size, not measured across the whole old gallery). The difference is that the old site needed a separate page load for every photo.
  • Images aren’t optimized yet. On the ceremony page Lighthouse estimates 1,341 KiB of savings from modern image formats and 501 KiB from compression. Cloudinary can do both by URL (f_auto,q_auto). On the homepage the LCP element is the CSS background banner, which the browser discovers late.

The inductee profile, the page most people land on from a shared link, is now as fast as the old one (94) and scores 96 on Accessibility instead of 53.

Remaining failed audits:

  • Accessibility: color-contrast on the light-blue section subheading and the year filter buttons. All pages.
  • Best Practices: errors-in-console. A 404 on every page, and on the ceremony page React hydration errors #418 and #423 (server and client HTML don’t match).
  • Ceremony page only: image-aspect-ratio, image-size-responsive, unsized-images, font-display.

Build and setup

Fresh-clone setup

2026-10-09, macOS 27.0, Node 24.20.0, pnpm 10.34.1. Cloned main into a new folder, copied in .env.local (Cloudinary keys), then followed the README:

StepTime
git cloneunder 1 s
pnpm install --frozen-lockfile4 s
pnpm build16 s (13 static pages plus generated inductee and ceremony pages)
pnpm start to first 200 response1 s
Total, clone to working site21 s
Steps that failed or needed a guessNone, given the Cloudinary keys

Caveat: the pnpm store was already warm on this machine. A cold machine will spend longer downloading packages. The build needs the Cloudinary keys, because ceremony and inductee pages fetch their image lists from Cloudinary at build time.

Dependency check

pnpm audit on 2026-10-09: 44 known vulnerabilities (2 critical, 23 high, 17 moderate, 2 low). Production dependencies only (--prod): 32.

PackageCriticalHighModerateLow
next 14.2.3528112
brace-expansion63
postcss22
sharp3
cloudinary1
glob, braces, source-map-js3
postcss-selector-parser1

Both critical advisories are remote code execution issues in Next.js, fixed in 15.5.24. One affects Windows-hosted servers, which doesn’t apply here. The other is in the Image Optimization API when AVIF files are involved. This site uses next/image with remote Cloudinary images. On Vercel, image optimization is handled by the platform, which may reduce exposure, but I haven’t verified that. Upgrading is the first item in the backlog.

The old site had no dependencies to audit. It also ran unpatched Apache over plain HTTP, and its exposure there can’t be measured from the archive.

How this was measured

  • Baseline: see BASELINE.md.
  • Lighthouse: lighthouse <url> --output=json --chrome-flags="--headless=new --incognito", three runs per page, median of each value.
  • Titles and descriptions: every URL in https://www.aphshalloffame.com/sitemap.xml (155) was fetched and its <title> and <meta name="description"> read. One URL failed: the sitemap lists /ceremony, which returns 404.
  • Gallery photo counts: unique Cloudinary file names under Ceremonies/<year>/ in each live ceremony page.

Quick-win fixes

Applied and measured 2026-10-09, on top of e004699. Deployed the same day: commit 9053992, merged to main in PR #12 (4efc2c8).

Lighthouse, before and after the fixes

Both builds served locally with pnpm start, measured in the same session, alternating between them, with the same settings as above (mobile, 3 runs, median). The “before” column is a clean build of e004699. Every run of each page scored within 3 points of the others.

PagePerformanceBest PracticesLCPTransferRequests
Home81 → 8896 → 1005.1 s → 3.9 s1,299 → 539 KiB165 → 33
Inductee90 → 9096 → 1003.6 s → 3.6 s436 → 432 KiB19 → 19
Ceremony (2010)77 → 8089 → 966.5 s → 5.5 s2,981 → 1,212 KiB59 → 45

Accessibility stayed at 96 and SEO at 100 on all three. Production JavaScript on the homepage, History and Mission Statement dropped from 239 kB to 104 kB, 98.5 kB and 98.5 kB (first-load JS from next build).

Combined with the baseline:

PageOld siteNew site before fixesNew site after fixes
Home, Accessibility539696
Home, SEO82100100
Home, Best Practices8296100
Home, Performance998188
Ceremony, transfer for the whole galleryone 366 KiB page load per photo2,981 KiB for 19 photos1,212 KiB for 19 photos

What changed

FixFilesEffect
Homepage grid starts on the default yearcomponents/shared/MemberGallery.tsx141 portraits downloaded per visit → 10
Gallery gets only name, slug and year from a server component, so biographies aren’t sent to the browser. Removed the unused React Query provider and dependencycomponents/shared/MemberGallerySection.tsx (new), MemberGalleryQueryProvider.tsx (deleted), package.jsonHomepage JS 239 kB → 104 kB
Banner is a prioritized next/image instead of a CSS backgroundapp/page.tsx, tailwind.config.jsBanner found and preloaded immediately, served at the right size
Cloudinary f_auto,q_auto on ceremony photos, h_1200 for slides, explicit width/height, only the first slide eagerapp/ceremony/[year]/page.tsx, components/GallerySlider.tsxCeremony page 2,981 KiB → 1,212 KiB
Slider CSS bundled from slick-carousel@1.8.1 instead of a CDN <head> injected by a layoutapp/ceremony/[year]/layout.tsx (deleted), components/GallerySlider.tsxFixed the hydration errors (#418, #423) that made every ceremony page re-render from scratch in the browser. Removed two render-blocking third-party requests
Ceremony photo alt text: “2010 induction ceremony, photo 1” instead of the file pathapp/ceremony/[year]/page.tsxReadable for screen readers
Favicon addedapp/icon.svgFixed the 404 in the console on every page
Inductee photo: https URL, removed the deprecated layout propapp/inductee/[slug]/page.tsxNo http:// image request, no console warning
Ceremony date corrected to Thursday, October 15, 2026 on Events and Donate (it said October 17, which is a Saturday). Source: the committee’s invitation PDF and “3-INV 2026 inside”db/events.json, db/donate.jsonMatches the printed invitation
Events advertise form embedded a Google Doc that returns 410 Gone. Now shows the 2026 ad PDFdb/events.jsonThe form view works again
Events meta description updated from the 2020/2021 textdb/pageSEO.jsonAccurate in search results
Donate and Contact use their own title and descriptionapp/donate/page.tsx, app/contact/page.tsxThe two pages that shared the homepage title now have their own
338 doubled apostrophes (Men''s Club) fixed across 89 lines of biographiesdb/members.jsonBios read correctly
2007 gallery added to the Ceremonies menu (8 photos were in Cloudinary but not linked)db/navigation.json, db/ceremonies.jsonRestores a gallery from the old site
/ceremony removed from the sitemapapp/sitemap.tsNo 404 in the sitemap
Removed the dead /api/keep-awake cron, the bio-updater/dump/seed scripts, and the “mongodb” keywordvercel.json (deleted), package.json, README.mdNothing points at code that no longer exists

After the fixes, the browser console is empty on Home, Inductee, both ceremony pages checked, Events, Donate and Contact.

Live site after deploying

Re-measured on https://www.aphshalloffame.com/ after the merge, with the same settings (mobile, 3 runs, median) and the same three pages. “Before” is the first live measurement at e004699.

PagePerformanceBest PracticesLCPTransferRequests
Home84 → 9296 → 1004.5 s → 3.4 s1,300 → 540 KiB165 → 33
Inductee94 → 9496 → 1003.1 s → 3.1 s436 → 433 KiB19 → 19
Ceremony (2010)76 → 8389 → 967.7 s → 4.8 s2,977 → 1,292 KiB59 → 45

Accessibility stayed at 96 and SEO at 100 on all three. TBT and CLS stayed at 0. Individual Performance runs: Home 85, 92, 92; Inductee 94, 94, 94; Ceremony 83, 78, 85.

The live numbers are a little better than the local ones, as they were before the fixes. Vercel’s CDN serves the pages and the banner image closer and with better compression than pnpm start on a laptop.

Against the old site, the homepage has gone from 15 points behind on Performance (99 → 84) to 7 points behind (99 → 92), while showing 10 portraits and a full-width photo the old page didn’t have. The ceremony page loads the whole 19-photo gallery in 1,292 KiB. On the old site that would buy about seven photos: 366 KiB for the first page load and about 155 KiB for each one after.

Remaining failed audits on the live site:

  • Accessibility: color-contrast (light-blue subheading and year buttons). All three pages. This is the only Accessibility failure left.
  • Best Practices, ceremony page only: image-aspect-ratio. On a phone the slider is 292px wide, but each photo is forced to 600px tall, so landscape photos are squashed to about a third of their real width. This was already failing at e004699, where the slider also fixed the height at 600px. See the backlog.
  • Performance diagnostic, ceremony page only: font-display still flags some web fonts.
  • The console errors, the hydration errors, unsized-images and image-size-responsive from the first measurement no longer fail.

Tried and reverted

  • lazyLoad: 'ondemand' on the ceremony slider. It cut transfer to 957 KiB but caused a large layout shift (CLS 0.39) and a later LCP, and Performance fell to 59. Reverted.

Backlog: what’s left

Security

  • Upgrade Next.js from 14.2.35 to 15.5.24 or later. This clears both critical advisories and most of the high ones. pnpm audit still reports 44 after the quick wins, because none of them changed Next.js. Next 15 also needs React 19, so react-slick, react-18-image-lightbox and @headlessui/react need checking. That’s why it isn’t in the quick wins.

Content (needs the committee)

  • Cloudinary has no photos for the 2012, 2018, 2024 or 2026 ceremonies. The old site had a 2012 gallery, so those photos existed at one point. 2026 photos can be added after October 15.
  • Donate page says “the 10th induction ceremony”. With 2003 through 2026 that would be the 11th. Confirm with the committee.

Performance

  • Homepage LCP (3.4 s live, 3.9 s local) is now mostly render delay (2.4 s), waiting on stylesheets and about 280 KiB of web fonts. Load fewer font weights: app/font.ts requests six Open Sans weights plus italics.
  • Ceremony slider distorts photos on phones: the fixed 600px height plus the narrow slide squashes them (image-aspect-ratio). Cap the height instead of fixing it (for example max-height: 600px; height: auto; max-width: 100%) and re-check CLS, since the slides currently rely on the fixed height.
  • Ceremony LCP (4.8 s live, 5.5 s local) is the 1200px-tall first slide. Smaller responsive sizes need the slider’s fixed 600px height to change first.

Accessibility

  • Raise the contrast of the light-blue subheading and the active year button (site.lightBlue, #59a7cf). The fix means darkening the brand color, so it’s a design decision. This is the only remaining Accessibility failure.