lighthouse fixes
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-11
|
||||
@@ -0,0 +1,42 @@
|
||||
# Lighthouse Baseline (From Provided Reports)
|
||||
|
||||
Source reports used:
|
||||
|
||||
- `C:\Users\simpl\Downloads\santhoshj.com-light-mobile.json`
|
||||
- `C:\Users\simpl\Downloads\santhoshj.com-dark-mobile.json`
|
||||
- `C:\Users\simpl\Downloads\santhoshj.com-hc-mobile.json`
|
||||
- `C:\Users\simpl\Downloads\santhoshj.com-hc-desktop.json`
|
||||
- `C:\Users\simpl\Downloads\santhoshj.com-light-desktop.json`
|
||||
- `C:\Users\simpl\Downloads\santhoshj.com-dark-desktop.json`
|
||||
|
||||
## Category Scores (Baseline)
|
||||
|
||||
- `light-mobile`: performance 92, accessibility 100, best-practices 96, seo 85
|
||||
- `dark-mobile`: performance 83, accessibility 95, best-practices 96, seo 85
|
||||
- `hc-mobile`: performance 84, accessibility 100, best-practices 96, seo 85
|
||||
- `hc-desktop`: performance 98, accessibility 100, best-practices 96, seo 85
|
||||
- `light-desktop`: performance 99, accessibility 100, best-practices 96, seo 85
|
||||
- `dark-desktop`: performance 99, accessibility 95, best-practices 96, seo 85
|
||||
|
||||
## Failing Audits Seen In 6/6 Reports
|
||||
|
||||
- `unminified-css`
|
||||
- `unused-css-rules`
|
||||
- `unused-javascript`
|
||||
- `inspector-issues`
|
||||
- `crawlable-anchors`
|
||||
- `robots-txt`
|
||||
- `cache-insight`
|
||||
- `forced-reflow-insight`
|
||||
- `image-delivery-insight`
|
||||
- `network-dependency-tree-insight`
|
||||
- `render-blocking-insight`
|
||||
|
||||
## Intermittent / Variant-Specific Fails
|
||||
|
||||
- `color-contrast` (2/6)
|
||||
- `interactive` (3/6)
|
||||
|
||||
## Environmental Warning In Reports
|
||||
|
||||
- Chrome extension interference warning appears in reports. This is why the new gate requires clean profile runs.
|
||||
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?
|
||||
@@ -0,0 +1,38 @@
|
||||
## Why
|
||||
|
||||
Chrome Lighthouse runs against the production site show repeated misses across Performance, SEO, Best Practices, and (in dark theme) Accessibility. These gaps reduce technical robustness and make it hard to trust deploy-to-deploy quality.
|
||||
|
||||
We want the site to be measurably correct and fast under a repeatable, automated Lighthouse configuration, and to treat Lighthouse regressions as build-blocking.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Make SEO outputs Lighthouse-clean by fixing `robots.txt` validity (absolute sitemap URL) and ensuring key links are crawlable.
|
||||
- For better SEO, implement schema based on https://schema.org/docs/full.html
|
||||
- If it helps performance and SEO, minify relevant assets.
|
||||
- Remove non-crawlable anchor patterns (anchors without `href`) in the media modal and CTA surfaces.
|
||||
- Fix dark theme contrast failures so text and links meet WCAG intent and Lighthouse `color-contrast` passes.
|
||||
- Reduce render-blocking work in the initial load path (fonts + global stylesheet) to improve mobile Performance.
|
||||
- Reduce CSS overhead by moving `global.css` through the build/minification pipeline and trimming unused rules.
|
||||
- Eliminate forced reflow hotspots caused by synchronous layout reads/writes in scroll/resize handlers.
|
||||
- Improve image delivery (size, format, and caching) to address Lighthouse image savings and cache lifetime findings.
|
||||
- Add a deterministic Lighthouse quality gate (CI or scripted) that runs against known URLs, form factors, and themes and requires 100s in a clean environment (no extensions).
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `lighthouse-quality-gate`: Define the target Lighthouse configuration (URLs, mobile/desktop, themes, run environment) and require 100/100/100/100 with report artifacts.
|
||||
- `responsive-image-delivery`: Deliver appropriately sized images and modern formats (or controlled proxies/self-hosting) for key surfaces so image delivery audits pass consistently.
|
||||
|
||||
### Modified Capabilities
|
||||
- `seo-content-surface`: Ensure `robots.txt` is valid and sitemap references are correct for the deployed domain; ensure Lighthouse SEO crawlability audits pass.
|
||||
- `media-modal`: Ensure modal fallback and CTA links are crawlable anchors (real `href`s) and remain accessible.
|
||||
- `wcag-responsive-ui`: Tighten requirements to prevent Lighthouse accessibility regressions (contrast and keyboard/focus behavior).
|
||||
- `site-theming`: Ensure dark theme tokens never produce low-contrast text-on-light backgrounds on primary surfaces.
|
||||
- `service-worker-performance`: Ensure caching behavior does not cause stale critical assets after deploy, and align cache strategy/headers with Lighthouse expectations where feasible.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected areas include: `site/public/robots.txt`, `site/src/layouts/BaseLayout.astro`, `site/src/components/MediaModal.astro`, `site/public/styles/global.css`, `site/public/sw.js`, and `deploy/nginx.conf`.
|
||||
- This change may require build-pipeline adjustments (CSS processing/minification, image processing or controlled caching/proxying) and CI scripting to run Lighthouse deterministically.
|
||||
- Some Lighthouse findings are attributable to third-party resources (e.g., YouTube thumbnails, externally hosted podcast art) and/or local Chrome extensions; hitting a strict 100 may require reducing third-party dependence and running audits in a clean profile.
|
||||
- Coding agent may run out of context window. In that case, compact the context and retry with chunking.
|
||||
@@ -0,0 +1,46 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Deterministic Lighthouse configuration
|
||||
The project MUST define a deterministic Lighthouse run configuration that specifies:
|
||||
|
||||
- a fixed list of URLs to audit
|
||||
- mobile and desktop runs
|
||||
- theme variants: `light`, `dark`, and `high-contrast`
|
||||
- a clean run environment (no browser extensions)
|
||||
- throttling / device emulation settings
|
||||
|
||||
The configuration MUST be checked into the repository.
|
||||
|
||||
#### Scenario: Lighthouse config is version-controlled
|
||||
- **WHEN** a developer checks the repository
|
||||
- **THEN** a Lighthouse configuration file/script exists and is referenced by a documented command
|
||||
|
||||
### Requirement: Lighthouse gate enforces perfect scores
|
||||
The project MUST provide a command that runs Lighthouse for the configured URLs and variants and fails if any category score is below 100:
|
||||
|
||||
- Performance
|
||||
- Accessibility
|
||||
- Best Practices
|
||||
- SEO
|
||||
|
||||
The command MUST output the reports as build artifacts (HTML and/or JSON) to a deterministic output directory.
|
||||
|
||||
#### Scenario: Gate fails on a regression
|
||||
- **WHEN** Lighthouse runs and any audited variant scores 99 in any category
|
||||
- **THEN** the command exits non-zero and reports which URL/variant failed
|
||||
|
||||
#### Scenario: Gate produces artifacts
|
||||
- **WHEN** Lighthouse runs successfully
|
||||
- **THEN** the reports are written to the configured output directory for later inspection
|
||||
|
||||
### Requirement: Repeatable scoring rule
|
||||
The Lighthouse gate MUST define a repeatable scoring rule to reduce run-to-run noise.
|
||||
|
||||
At minimum, it MUST document one of the following:
|
||||
|
||||
- single-run with fixed throttling and clean environment, or
|
||||
- multiple runs per variant with a deterministic selection rule (e.g., median)
|
||||
|
||||
#### Scenario: Run-to-run method is documented
|
||||
- **WHEN** a developer runs the gate locally or in CI
|
||||
- **THEN** the method for selecting/reporting scores is explicitly documented
|
||||
@@ -0,0 +1,49 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Media modal dialog
|
||||
The site MUST provide a modal dialog that displays embedded media (YouTube video or podcast episode) when a user clicks a video or podcast content card on a listing surface (homepage, `/videos`, `/podcast`).
|
||||
|
||||
The modal MUST render the following elements in order:
|
||||
|
||||
- A header row with the content title on the left and a close button on the right
|
||||
- An embedded media player (YouTube iframe for videos; Spotify embed for podcast episodes when the URL is a Spotify URL; otherwise an in-modal audio player when an `audioUrl` is available)
|
||||
- The full description/summary text (not truncated)
|
||||
- The publish date and view count (when available)
|
||||
- A "Subscribe on YouTube" / "Follow on Spotify" CTA and a "View on YouTube" / "Listen on Spotify" CTA
|
||||
|
||||
All modal CTAs that represent navigation MUST be implemented as crawlable anchors:
|
||||
|
||||
- Each CTA MUST be an `<a>` element with a non-empty `href` attribute.
|
||||
- The UI MUST NOT render placeholder `<a>` elements without `href` in the initial HTML.
|
||||
- If CTA destinations are not known until a user selects an item, the CTA UI MUST be rendered as non-anchor elements until the destinations are known.
|
||||
|
||||
#### Scenario: User clicks a YouTube video card
|
||||
- **WHEN** a user clicks a video content card on any listing surface
|
||||
- **THEN** a modal dialog opens displaying a YouTube iframe embed, the video title, full description, date, view count (if available), and CTAs for "Subscribe on YouTube" and "View on YouTube"
|
||||
|
||||
#### Scenario: User clicks a podcast episode card with a Spotify URL
|
||||
- **WHEN** a user clicks a podcast content card whose URL is a Spotify URL
|
||||
- **THEN** a modal dialog opens displaying a Spotify episode embed, the episode title, full description, date, and CTAs for "Follow on Spotify" and "Listen on Spotify"
|
||||
|
||||
#### Scenario: User clicks a podcast episode card with a non-Spotify URL
|
||||
- **WHEN** a user clicks a podcast content card whose URL is not a Spotify URL
|
||||
- **THEN** the modal dialog opens displaying the episode metadata (title, description, date) and either:
|
||||
- an in-modal audio player when an `audioUrl` is available
|
||||
- otherwise, a "Listen on Spotify" outbound link
|
||||
|
||||
#### Scenario: Modal renders with missing optional fields
|
||||
- **WHEN** a content item has no view count or no summary
|
||||
- **THEN** the modal MUST still render cleanly with those fields omitted
|
||||
|
||||
#### Scenario: Modal CTAs are crawlable anchors
|
||||
- **WHEN** the modal is present in the DOM (before any user interaction)
|
||||
- **THEN** the document contains no `<a>` elements in the modal that are missing `href`
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Embed fallback is a link only when a destination is available
|
||||
If an embed fallback is presented as a link to an external page, it MUST be an anchor with a valid `href`. If no destination is available, the fallback MUST be hidden or rendered as non-link text.
|
||||
|
||||
#### Scenario: Embed fallback does not render a non-crawlable anchor
|
||||
- **WHEN** the modal is rendered before any item selection
|
||||
- **THEN** the embed fallback is not rendered as an anchor without `href`
|
||||
@@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Card thumbnails have explicit dimensions
|
||||
All card thumbnail images MUST include explicit `width` and `height` attributes matching the rendered aspect ratio.
|
||||
|
||||
#### Scenario: Thumbnail dimensions are present
|
||||
- **WHEN** a crawler or browser loads a listing surface with content cards
|
||||
- **THEN** each card thumbnail `<img>` includes `width` and `height` attributes
|
||||
|
||||
### Requirement: Above-the-fold imagery is optimized
|
||||
The site MUST ensure above-the-fold images are optimized for performance:
|
||||
|
||||
- avoid unnecessarily large image payloads for the requested viewport
|
||||
- prefer modern image formats when first-party controlled
|
||||
- avoid layout shift caused by late image dimension discovery
|
||||
|
||||
#### Scenario: Above-the-fold images do not cause layout shift
|
||||
- **WHEN** a user loads the home page on a mobile viewport
|
||||
- **THEN** the hero/top content area does not shift vertically due to image loading
|
||||
|
||||
### Requirement: Third-party image variability is controlled for the Lighthouse gate
|
||||
If a gated page depends on third-party images (e.g., external thumbnails), the gate MUST either:
|
||||
|
||||
- ensure those images do not block reaching 100s by design (e.g., not above-the-fold, lazy-loaded), or
|
||||
- provide a first-party controlled alternative for gated pages.
|
||||
|
||||
#### Scenario: Gated pages avoid third-party image volatility
|
||||
- **WHEN** Lighthouse runs against the gated URL set
|
||||
- **THEN** third-party image delivery does not prevent meeting the required scores
|
||||
@@ -0,0 +1,39 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Sitemap and robots
|
||||
The site MUST provide:
|
||||
|
||||
- `sitemap-index.xml` enumerating indexable pages (and/or referencing sitemap shards)
|
||||
- `robots.txt` that allows indexing of indexable pages
|
||||
|
||||
The sitemap MUST include the blog surface routes:
|
||||
|
||||
- `/blog`
|
||||
- blog post detail routes
|
||||
- blog page detail routes
|
||||
- blog category listing routes
|
||||
|
||||
`robots.txt` MUST reference the sitemap using an absolute URL for the production domain.
|
||||
|
||||
#### Scenario: Sitemap index is available
|
||||
- **WHEN** a crawler requests `/sitemap-index.xml`
|
||||
- **THEN** the server returns an XML sitemap index (or sitemap) listing `/`, `/videos`, `/podcast`, `/about`, and `/blog`
|
||||
|
||||
#### Scenario: Blog URLs appear in sitemap
|
||||
- **WHEN** WordPress content is available in the cache at build time
|
||||
- **THEN** the generated sitemap includes the blog detail URLs for those items
|
||||
|
||||
#### Scenario: Robots references sitemap with absolute URL
|
||||
- **WHEN** a crawler requests `/robots.txt`
|
||||
- **THEN** the response contains a `Sitemap:` line with an absolute URL to `/sitemap-index.xml`
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Organization and website structured data
|
||||
The home page SHOULD include JSON-LD structured data for the site and its owner/organization.
|
||||
|
||||
If present, the JSON-LD MUST be valid JSON and MUST use a recognized schema type.
|
||||
|
||||
#### Scenario: Home page includes valid JSON-LD
|
||||
- **WHEN** a crawler requests `/`
|
||||
- **THEN** the HTML contains a JSON-LD script tag that parses as valid JSON
|
||||
@@ -0,0 +1,17 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Critical assets do not remain stale after deploy
|
||||
The service worker and server caching strategy MUST ensure critical shell assets (including the global stylesheet and service worker script) do not remain stale across deploys.
|
||||
|
||||
The implementation MUST include at least one cache-busting mechanism for critical assets, such as:
|
||||
|
||||
- content-hashed asset filenames, or
|
||||
- an asset version query suffix that changes per deploy
|
||||
|
||||
#### Scenario: New deploy updates critical CSS
|
||||
- **WHEN** a new deploy is released and a returning user loads the site
|
||||
- **THEN** the user receives the updated global stylesheet without requiring a manual hard refresh
|
||||
|
||||
#### Scenario: Service worker updates predictably
|
||||
- **WHEN** a new deploy is released
|
||||
- **THEN** the browser can retrieve the updated service worker script and activate it without being pinned by long-lived caches
|
||||
@@ -0,0 +1,41 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Theme tokens meet contrast intent
|
||||
For each supported theme (`dark`, `light`, `high-contrast`), the theme token pairs used for primary text and primary surfaces MUST meet WCAG 2.2 AA contrast intent.
|
||||
|
||||
At minimum:
|
||||
|
||||
- primary body text on the primary background MUST be high-contrast
|
||||
- link text on the primary background MUST be distinguishable and meet contrast intent
|
||||
- secondary labels on the primary background MUST remain readable
|
||||
|
||||
#### Scenario: Dark theme text is readable
|
||||
- **WHEN** `data-theme="dark"` is active
|
||||
- **THEN** primary text remains readable against primary surfaces without low-contrast combinations
|
||||
|
||||
#### Scenario: Dark theme links are readable
|
||||
- **WHEN** `data-theme="dark"` is active
|
||||
- **THEN** links in common surfaces are readable against their background
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Site themes
|
||||
The site MUST support three themes:
|
||||
|
||||
- `dark`
|
||||
- `light`
|
||||
- `high-contrast`
|
||||
|
||||
Themes MUST be applied by setting a `data-theme` attribute on the root document element (`<html>`).
|
||||
|
||||
#### Scenario: Dark theme active
|
||||
- **WHEN** `data-theme="dark"` is set on `<html>`
|
||||
- **THEN** the site's background, text, and component styling reflect the dark palette
|
||||
|
||||
#### Scenario: Light theme active
|
||||
- **WHEN** `data-theme="light"` is set on `<html>`
|
||||
- **THEN** the site's background, text, and component styling reflect the light palette
|
||||
|
||||
#### Scenario: High contrast theme active
|
||||
- **WHEN** `data-theme="high-contrast"` is set on `<html>`
|
||||
- **THEN** the site uses a high-contrast palette with a clearly visible focus ring and high-contrast borders
|
||||
@@ -0,0 +1,21 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Interactive elements use correct semantics
|
||||
Interactive elements MUST use correct semantic elements:
|
||||
|
||||
- Navigation MUST use anchors (`<a>`) with valid `href`.
|
||||
- Actions that do not navigate MUST use `<button>`.
|
||||
|
||||
The site MUST NOT render anchor elements that lack an `href` attribute.
|
||||
|
||||
#### Scenario: Modal triggers are buttons
|
||||
- **WHEN** a content card opens the media modal
|
||||
- **THEN** the card is a `<button>` element and is keyboard operable
|
||||
|
||||
#### Scenario: Navigation CTAs are anchors
|
||||
- **WHEN** a user sees a CTA that navigates to another page
|
||||
- **THEN** the CTA is an `<a>` element with a valid `href`
|
||||
|
||||
#### Scenario: No non-crawlable anchors exist
|
||||
- **WHEN** a crawler inspects the rendered HTML
|
||||
- **THEN** there are no `<a>` elements without `href`
|
||||
@@ -0,0 +1,52 @@
|
||||
## 1. Baseline And Repro
|
||||
|
||||
- [x] 1.1 Add a repeatable Lighthouse run command (local) that outputs reports for mobile/desktop x light/dark/high-contrast
|
||||
- [x] 1.2 Document the Lighthouse run environment requirements (clean profile / no extensions) and scoring rule (single vs median)
|
||||
- [x] 1.3 Capture current failing audits in a checked-in note (from the provided JSON reports) to validate fixes one-by-one
|
||||
|
||||
## 2. SEO Correctness
|
||||
|
||||
- [x] 2.1 Update `site/public/robots.txt` to reference the sitemap with an absolute URL to `/sitemap-index.xml`
|
||||
- [x] 2.2 Verify `site/astro.config.mjs` has `site` set correctly in production via `PUBLIC_SITE_URL` and that sitemap output is present after build
|
||||
- [x] 2.3 Add/verify JSON-LD structured data on the all pages (valid JSON, recognized schema type)
|
||||
|
||||
## 3. Crawlable Anchors (Media Modal)
|
||||
|
||||
- [x] 3.1 Update `site/src/components/MediaModal.astro` so no modal anchors are rendered without `href` in the initial HTML
|
||||
- [x] 3.2 Ensure modal CTAs that navigate are always `<a href="...">` and actions that do not navigate are `<button>`
|
||||
- [x] 3.3 Re-run Lighthouse SEO audit to confirm `crawlable-anchors` passes
|
||||
|
||||
## 4. CSS Processing And Render Blocking
|
||||
|
||||
- [x] 4.1 Move `site/public/styles/global.css` into the Astro/Vite build pipeline (so production output is minified and cache-busted)
|
||||
- [x] 4.2 Update `site/src/layouts/BaseLayout.astro` to load the built CSS output and remove any no-longer-needed manual cache-busting for CSS
|
||||
- [x] 4.3 Adjust `site/public/sw.js` shell precache list to match the new CSS delivery strategy
|
||||
- [x] 4.4 Confirm Lighthouse no longer reports `unminified-css` and materially reduces `unused-css-rules` for the primary pages
|
||||
|
||||
## 5. Fonts
|
||||
|
||||
- [x] 5.1 Replace render-blocking Google Fonts with a self-hosted font strategy (woff2 + `font-display`)
|
||||
- [x] 5.2 Preload only the critical font files/weights needed for above-the-fold rendering
|
||||
- [x] 5.3 Re-run Lighthouse to confirm improved `render-blocking` findings and LCP trend
|
||||
|
||||
## 6. Images
|
||||
|
||||
- [x] 6.1 Add explicit `width`/`height` for card thumbnail images (and any other frequently used images missing dimensions)
|
||||
- [x] 6.2 Ensure above-the-fold imagery on gated pages is deterministic and does not rely on third-party delivery for a perfect Lighthouse gate
|
||||
- [x] 6.3 Re-run Lighthouse to validate reduced CLS and improved image-related savings audits
|
||||
|
||||
## 7. Service Worker / Cache Robustness
|
||||
|
||||
- [x] 7.1 Verify critical asset cache-busting is effective across deploys (SW + global CSS) and does not pin stale styles
|
||||
- [x] 7.2 Confirm nginx cache headers are correct for `/sw.js` (no-store) and any critical CSS entrypoint
|
||||
|
||||
## 8. Security Headers (Best Practices)
|
||||
|
||||
- [x] 8.1 Add baseline security headers in `deploy/nginx.conf` (CSP, nosniff, frame-ancestors / X-Frame-Options, referrer-policy, permissions-policy)
|
||||
- [x] 8.2 Choose a CSP tier (moderate vs hash-based) and ensure the site functions with no CSP console violations in the normal path
|
||||
- [x] 8.3 Re-run Lighthouse Best Practices to confirm CSP-related inspector issues are resolved
|
||||
|
||||
## 9. CI Quality Gate
|
||||
|
||||
- [x] 9.1 Add a CI job that runs the Lighthouse command and uploads reports as artifacts
|
||||
- [x] 9.2 Make CI fail if any configured URL/variant scores below 100 in any category
|
||||
Reference in New Issue
Block a user