lighthouse fixes
This commit is contained in:
144
openspec/changes/archive/2026-02-10-lighthouse-fixes/design.md
Normal file
144
openspec/changes/archive/2026-02-10-lighthouse-fixes/design.md
Normal file
@@ -0,0 +1,144 @@
|
||||
## Context
|
||||
|
||||
The production site is built as static output and served via nginx (Docker image). Lighthouse runs (mobile/desktop, light/dark/high-contrast) report repeated misses across Performance, SEO, Best Practices, and (in dark theme) Accessibility.
|
||||
|
||||
Notable characteristics from the current system:
|
||||
|
||||
- SEO is mostly static-file driven (`site/public/robots.txt`, sitemap output) and must be correct for the deployed domain.
|
||||
- Global styling is served from `site/public/styles/global.css`, which bypasses typical bundler optimizations (minification, pruning) unless explicitly piped through the build.
|
||||
- A service worker exists (`site/public/sw.js`) and is involved in caching shell assets; cache behavior must not cause stale critical assets after deploy.
|
||||
- The site uses environment-driven configuration and has multiple theme modes (light/dark/high-contrast) that must remain visually consistent and accessible.
|
||||
|
||||
The goal is a deterministic, repeatable Lighthouse gate that can hit 100s in a clean run environment.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Reach 100 Lighthouse scores (Performance, Accessibility, Best Practices, SEO) for the defined set of URLs, form factors, and theme variants.
|
||||
- Fix SEO correctness issues that are unambiguous (e.g., `robots.txt` sitemap URL must be absolute for the production domain).
|
||||
- Ensure all user-visible navigational CTAs and modal actions are implemented as crawlable anchors (real `href`s) when they represent links.
|
||||
- Eliminate dark theme contrast regressions by tightening theme token choices and/or component-level styles.
|
||||
- Reduce initial render blocking work (fonts + CSS) to improve mobile performance.
|
||||
- Reduce CSS overhead by ensuring the global stylesheet is minified and sized appropriately for production.
|
||||
- Make Lighthouse runs deterministic (clean Chrome profile, fixed throttling/UA) and enforce a quality gate in CI or a scripted check.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Redesigning the visual aesthetic.
|
||||
- Building a general-purpose CSS framework migration.
|
||||
- Perfect scores when Lighthouse is run with extensions or non-deterministic settings.
|
||||
- Solving third-party performance/caching issues by weakening metrics (the goal is to reduce third-party dependence where it blocks 100s, or scope the gate to first-party pages that can be made deterministic).
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1) Define the Lighthouse gate upfront
|
||||
|
||||
Decision: treat 100s as a contract under a specific, documented run configuration.
|
||||
|
||||
- URLs: start with the production home page and any key landing/content pages that are stable and representative.
|
||||
- Variants: mobile/desktop x light/dark/high-contrast.
|
||||
- Run environment: headless Chrome in CI (no extensions), consistent throttling, at least N runs per variant with median selection.
|
||||
|
||||
Rationale: Lighthouse scores are otherwise too sensitive to environment noise to serve as a reliable build gate.
|
||||
|
||||
Alternative considered: manual Lighthouse checks. Rejected because it does not prevent regressions.
|
||||
|
||||
### 2) Fix SEO static outputs in `public/`
|
||||
|
||||
Decision: keep `robots.txt` and sitemap-related outputs as static files, but ensure correctness for production.
|
||||
|
||||
- Make the sitemap reference in `site/public/robots.txt` absolute (not a relative path).
|
||||
|
||||
Rationale: This is required for Lighthouse SEO audits and is the least invasive change.
|
||||
|
||||
Alternative considered: generate robots dynamically. Rejected as unnecessary complexity.
|
||||
|
||||
### 3) Enforce crawlable links in modal/CTA surfaces
|
||||
|
||||
Decision: if an element semantically represents navigation (internal or outbound), it must be an `<a href="...">`.
|
||||
|
||||
- For modal components, ensure the CTA is rendered as an anchor with a real destination URL when appropriate.
|
||||
- If the interaction is not navigation (e.g., close modal), use a `<button>` and ensure it is accessible.
|
||||
|
||||
Rationale: Lighthouse flags anchors without `href` as non-crawlable. Using correct semantics also improves accessibility.
|
||||
|
||||
Alternative considered: keep `<a>` without `href` and add JS click handlers. Rejected (SEO + a11y regression).
|
||||
|
||||
### 4) Process `global.css` through a production pipeline
|
||||
|
||||
Decision: stop serving the main global stylesheet as an unprocessed static asset.
|
||||
|
||||
Options:
|
||||
|
||||
- Import `global.css` from the Astro entry/layout so Vite can minify it for production builds.
|
||||
- If the site intentionally keeps a standalone global CSS file, add an explicit build step to minify it and (optionally) run a basic unused rule trimming strategy.
|
||||
|
||||
Rationale: Lighthouse currently flags unminified CSS and unused CSS rules; bundling/minification is the most straightforward fix.
|
||||
|
||||
Alternative considered: leave it in `public/` and accept lower scores. Rejected (goal is 100s).
|
||||
|
||||
### 5) Font loading strategy optimized for mobile performance
|
||||
|
||||
Decision: make font loading non-blocking and deterministic.
|
||||
|
||||
- Prefer self-hosted fonts or Astro's recommended font strategy.
|
||||
- Use `font-display: swap` and consider `preload` for critical weights.
|
||||
- Avoid unnecessary font variants.
|
||||
|
||||
Rationale: render-blocking and late text rendering negatively impact Performance and perceived load.
|
||||
|
||||
Alternative considered: keep current remote font loading. Rejected if it remains render-blocking.
|
||||
|
||||
### 6) Theme token constraints for contrast
|
||||
|
||||
Decision: fix contrast failures at the token level first, with targeted overrides only when needed.
|
||||
|
||||
- Identify failing color pairs (foreground/background) in dark mode.
|
||||
- Update tokens in `site/public/styles/global.css` so common surfaces always meet contrast expectations.
|
||||
|
||||
Rationale: token-level fixes prevent regressions across multiple components.
|
||||
|
||||
Alternative considered: per-component overrides only. Rejected as brittle.
|
||||
|
||||
### 7) CSP / Best Practices compliance
|
||||
|
||||
Decision: address Lighthouse Best Practices issues by aligning nginx headers with modern expectations without breaking the site.
|
||||
|
||||
- Keep security headers in `deploy/nginx.conf`.
|
||||
- If inline scripts/styles exist, prefer refactoring to external files to avoid unsafe CSP allowances; otherwise use a nonce/hash strategy.
|
||||
|
||||
Rationale: strong CSP improves security posture and can satisfy Lighthouse findings, but must be implemented in a way compatible with the build output.
|
||||
|
||||
Alternative considered: disable CSP to appease some checks. Rejected.
|
||||
|
||||
### 8) Images: reduce third-party variability
|
||||
|
||||
Decision: prioritize first-party control of above-the-fold images.
|
||||
|
||||
- Use Astro image tooling for locally hosted images.
|
||||
- For third-party images that block 100s (e.g., external thumbnails), prefer either self-hosting (if allowed) or avoid rendering them above the fold during initial load.
|
||||
|
||||
Rationale: image delivery and caching audits can be impossible to satisfy if key images are served from third parties with unknown caching/format behavior.
|
||||
|
||||
Alternative considered: ignore image findings. Rejected if they prevent the 100 gate.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Lighthouse instability] Scores vary across runs/environments -> Mitigation: lock run configuration and use a clean profile in CI.
|
||||
- [Build pipeline change risk] Moving `global.css` into bundling may change selector precedence -> Mitigation: diff visual output across themes and add a lightweight snapshot/DOM test where possible.
|
||||
- [CSP breakage] Tight CSP can block analytics or inline scripts -> Mitigation: inventory scripts, move to external, or add nonce/hash strategy.
|
||||
- [Third-party dependencies] External media assets may prevent 100 -> Mitigation: reduce third-party assets on gated pages or self-host where permissible.
|
||||
- [Service worker caching] SW can serve stale assets after deploy -> Mitigation: version critical assets and disable caching for SW + critical CSS, ensure SW update flow.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
- Implement fixes behind minimal, incremental commits (SEO correctness, crawlable anchors, contrast tokens, CSS pipeline/font loading, CSP).
|
||||
- Deploy with cache-busting in place so critical style updates reach clients quickly.
|
||||
- Rollback: revert to a known-good image tag/digest and confirm SW cache versioning does not pin stale assets.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Which URLs are included in the Lighthouse gate (home only vs additional pages)?
|
||||
- Are 100 scores required even when pages include third-party embeds/thumbnails, or should we scope the gate to pages that can be made first-party deterministic?
|
||||
- Should schema.org structured data be implemented for all pages or only key landing/content types?
|
||||
Reference in New Issue
Block a user