|
| 1 | +--- |
| 2 | +title: "How I Achieved Perfect Lighthouse Scores (100 Across the Board)" |
| 3 | +description: "A walkthrough of the specific fixes I made to push my Astro site from 95/81/92 to 100/100/100 on Lighthouse's Accessibility, Best Practices, and SEO audits." |
| 4 | +pubDate: 2026-03-19T00:00:00.000Z |
| 5 | +heroImage: ../../assets/pagespeed-100-scores.png |
| 6 | +categories: |
| 7 | + - performance |
| 8 | + - accessibility |
| 9 | +keywords: |
| 10 | + - lighthouse |
| 11 | + - web performance |
| 12 | + - accessibility |
| 13 | + - SEO |
| 14 | + - WCAG |
| 15 | + - Cloudflare Zaraz |
| 16 | + - Astro |
| 17 | +contentType: "technical-tutorial" |
| 18 | +faqs: |
| 19 | + - question: "What causes Lighthouse Best Practices to drop below 100?" |
| 20 | + answer: "A common culprit is Partytown, which enumerates all window properties on initialization, triggering Chrome's deprecation tracking for APIs like SharedStorage and AttributionReporting. Removing Partytown and using an edge-based solution like Cloudflare Zaraz eliminates these warnings." |
| 21 | + - question: "How do I fix color contrast issues flagged by Lighthouse?" |
| 22 | + answer: "Use a contrast checker to ensure your text colors meet WCAG AA's 4.5:1 ratio for normal text. Watch out for compounding CSS opacity values and text on tinted backgrounds, where the effective contrast can be much lower than expected." |
| 23 | + - question: "Does removing Partytown hurt performance?" |
| 24 | + answer: "Not necessarily. For static sites with excellent Core Web Vitals, Partytown's overhead (service worker initialization, window enumeration) can actually be more costly than loading analytics directly with async. Edge-based alternatives like Cloudflare Zaraz have near-zero client impact." |
| 25 | +--- |
| 26 | + |
| 27 | +I recently set out to fix the Lighthouse audit failures on this site. The starting scores were Accessibility 95, Best Practices 81, and SEO 92 — not bad, but not perfect. Here's exactly what I fixed to get all three to 100. |
| 28 | + |
| 29 | +## The Starting Point |
| 30 | + |
| 31 | +The site is built with Astro, deployed on Cloudflare Pages, with Core Web Vitals already in great shape (LCP ~180ms, CLS 0.00). The issues were all in the non-performance categories: color contrast, missing aria attributes, generic link text, and deprecated browser API warnings. |
| 32 | + |
| 33 | +## Fix 1: Color Contrast (Accessibility) |
| 34 | + |
| 35 | +Lighthouse flagged several text elements for insufficient contrast against their backgrounds. WCAG AA requires at least a 4.5:1 contrast ratio for normal-sized text. |
| 36 | + |
| 37 | +**Muted text color** — `#7a786f` on `#faf9f5` was only ~4.2:1. Darkened to `#65635b` (~5.1:1). |
| 38 | + |
| 39 | +**Accent color** — `#d97757` on light backgrounds was ~3.0:1. Darkened to `#b5532f` (~4.7:1). |
| 40 | + |
| 41 | +**Category badges** — This one was subtle. The badge used `color: var(--color-accent)` on a background of `color-mix(in srgb, var(--color-accent) 15%, transparent)`. The text and background were both derived from the same accent color, yielding only 3.85:1 contrast. Fixed by using the main text color instead. |
| 42 | + |
| 43 | +**Footer credit** — The footer used `opacity: 0.8` on the container and `opacity: 0.7` on the credit text. These compound multiplicatively: `0.8 x 0.7 = 0.56` effective opacity, making the text far too light. Replaced with an explicit `color: var(--color-text-muted)`. |
| 44 | + |
| 45 | +The takeaway: **always check computed contrast, not just your CSS variable values.** Opacity stacking and tinted backgrounds can silently break your ratios. |
| 46 | + |
| 47 | +## Fix 2: Aria-Label Mismatches (Accessibility) |
| 48 | + |
| 49 | +WCAG 2.5.3 requires that when an element has both visible text and an `aria-label`, the label must contain the visible text. Two elements failed this: |
| 50 | + |
| 51 | +- **Logo link** — visible text "Angel Baez", aria-label was just "Home". Changed to "Angel Baez - Home". |
| 52 | +- **Language switcher** — visible text "ES", aria-label was "Switch to Spanish". Changed to "ES - Switch to Spanish". |
| 53 | + |
| 54 | +## Fix 3: Descriptive Link Text (SEO) |
| 55 | + |
| 56 | +Generic link text like "Read more" and "Learn more" hurts both screen reader usability and SEO. Two fixes: |
| 57 | + |
| 58 | +- **Blog "Read more" links** — Added `aria-label="Read more about {post title}"` to each link. |
| 59 | +- **Homepage CTA** — Changed "Learn more" to "Learn more about me". |
| 60 | + |
| 61 | +## Fix 4: Replacing Partytown with Cloudflare Zaraz (Best Practices) |
| 62 | + |
| 63 | +This was the most interesting one. Lighthouse flagged `SharedStorage` and `AttributionReporting` as deprecated APIs, dropping Best Practices to 81. But my code wasn't using those APIs — Partytown was. |
| 64 | + |
| 65 | +Partytown works by running third-party scripts in a web worker. During initialization, it calls `Object.getOwnPropertyNames(window)` to enumerate every browser API and create proxy objects. This enumeration touches deprecated APIs, which Chrome flags even though nobody actually called them. |
| 66 | + |
| 67 | +**The fix:** Remove Partytown entirely and switch to [Cloudflare Zaraz](https://developers.cloudflare.com/zaraz/). Since the site already runs on Cloudflare Pages, Zaraz was a natural fit: |
| 68 | + |
| 69 | +- **Zero client-side code** — Zaraz runs analytics logic on Cloudflare's edge, injecting only a tiny ~2-3 KB loader (vs. gtag.js at ~111 KB + Partytown's service worker). |
| 70 | +- **No deprecated API warnings** — No window enumeration, no service worker sandbox. |
| 71 | +- **Free tier** — 1 million events/month included. |
| 72 | +- **Setup** — Just enable GA4 in the Cloudflare dashboard and delete all analytics code from the codebase. |
| 73 | + |
| 74 | +## The Result |
| 75 | + |
| 76 | +| Category | Before | After | |
| 77 | +|---|---|---| |
| 78 | +| Performance | 99 | 99 | |
| 79 | +| Accessibility | 95 | **100** | |
| 80 | +| Best Practices | 81 | **100** | |
| 81 | +| SEO | 92 | **100** | |
| 82 | + |
| 83 | +Zero failed audits. The site is faster too — removing Partytown's service worker and gtag.js eliminated ~120 KB of client-side JavaScript. |
| 84 | + |
| 85 | +If you're using Partytown on a Cloudflare-hosted site and wondering why your Best Practices score won't budge, consider Zaraz. It solved the problem with less code, not more. |
0 commit comments