Skip to content

perf: web performance audit fixes#56

Open
ZingerLittleBee wants to merge 1 commit intomainfrom
perf/web-performance-fixes
Open

perf: web performance audit fixes#56
ZingerLittleBee wants to merge 1 commit intomainfrom
perf/web-performance-fixes

Conversation

@ZingerLittleBee
Copy link
Copy Markdown
Owner

Web Performance Audit — Fixes

Findings and fixes from a codebase audit of dockerman.app (Next.js 16 + Tailwind CSS v4 + Turbo).

Fixes

Critical: Image lazy-loading bug (SnapshotPlaygroundScroll.tsx)
loading={index >= 3 ? 'eager' : undefined} should be 'lazy'. This caused all 14 playground screenshots (~5.4MB of PNGs before optimizer compression) to download eagerly on page load. Only the first 3 are visible; the rest should be deferred.

LCP image quality (HeroImage.tsx)
Both hero images used quality={100}, the maximum. Changed to quality={75}, which is visually indistinguishable for UI screenshots but generates significantly smaller optimized images from Next.js's image pipeline.

Render-blocking font (globals.css)
NanumPenScript (a decorative handwriting font used for accent text) used font-display: block, which holds back text rendering until the font arrives. Changed to font-display: swap — text renders immediately with a fallback and swaps when the font loads.

797 KB GIF eagerly downloaded on CSS parse (globals.css)
/images/i-love-you-love.gif was referenced as a CSS mask fallback in ::view-transition-new(root). Browsers pre-fetch URLs in CSS at parse time — before any user interaction. The GIF is only needed for the theme-toggle animation (which already sets --transition-mask via JS). Changed the fallback from the URL to none to defer the fetch until the user actually clicks the toggle.

Long-term caching for static assets (next.config.mjs)
Added Cache-Control: public, max-age=31536000, immutable for /screenshots/*, /fonts/*, and /images/*. Previously these served with max-age=0, must-revalidate, forcing a conditional re-fetch on every page visit.

Removed unused dependency
@tabler/icons-react was listed in package.json but is never imported anywhere in the codebase.

Not changed (informational)

  • Redirect on first visit (//en): The middleware handles this at the edge and sets a 1-year cookie, so only the very first visit incurs the redirect hop. Acceptable tradeoff for i18n.
  • Dual animation libraries (GSAP + Motion): Both are legitimately used — GSAP for scroll-triggered animations via Lenis, Motion for component-level transitions. Consolidating would be a larger refactor.
  • 90 KB HTML, uncompressed in headers: Vercel handles Brotli transparently for browser requests; the curl -sI probe didn't advertise Accept-Encoding: br correctly.

This PR was generated with Oz.

- Fix critical image lazy-loading bug in SnapshotPlaygroundScroll:
  loading={index >= 3 ? 'eager'} → 'lazy', preventing all 14
  playground screenshots (~5.4MB) from downloading on initial load

- Lower hero image quality from 100 → 75 in HeroImage, reducing
  LCP image weight while keeping visual fidelity

- Fix NanumPenScript font-display: block → swap to unblock rendering
  while the decorative handwriting font loads

- Defer 797KB view-transition GIF: change CSS fallback from
  url('/images/i-love-you-love.gif') to none so the GIF is only
  fetched when the user clicks the theme toggle (already set via JS)

- Add long-term Cache-Control headers (1 year, immutable) for
  /screenshots/*, /fonts/*, /images/* in next.config.mjs

- Remove unused @tabler/icons-react dependency from package.json

Co-Authored-By: Oz <oz-agent@warp.dev>
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dockerman Ready Ready Preview, Comment Mar 4, 2026 2:59am

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant