Skip to content

Aggressively reduce the asset library and enforce image budgets #279

Description

@jbensal

Goal

Reduce repository and deployment weight by keeping one canonical source for each image, generating only the required delivery variants during the normal Vite build, removing duplicate and unused assets, and preventing regressions with automated asset budgets in CI.

Context

The current public/ tree contains 714 files totaling 445,830,431 bytes (425.2 MiB / approximately 427 MB). A SHA-256 inventory finds 37 byte-identical duplicate groups that waste 79,601,565 bytes (75.9 MiB), plus 151 same-stem groups with multiple extensions. The two largest raster files are 17,378,585 bytes at 7,360×4,912 and 17,255,914 bytes at 7,360×4,912; other committed images are as wide as 8,533 pixels.

The live homepage and case-study listing currently render representative images successfully on desktop and at a 390×844 mobile viewport. The homepage can select committed AVIF variants from <picture> while case-study cards serve committed WebP files that are typically 1,200 pixels wide but render at about 300 pixels on mobile. Preserve that visible result, responsive behavior, aspect ratios, loading priority, and fallback behavior during cleanup.

This React/Vite repository already depends on sharp, but its conversion scripts write WebP and AVIF files beside their PNG/JPEG/SVG inputs and are not part of the package build scripts. Vite copies public/ to dist/ without transforming it, and the pull-request workflow currently runs lint/format and the production build without any asset dimension, byte-size, duplicate-content, or unused-file checks.

Requirements

  • Add a reproducible asset inventory command that scans committed image assets and their references in routes, components, data, CSS, HTML, and other public files. It must report total bytes, dimensions, byte-identical duplicate groups, same-image format variants, missing references, and unreferenced files. Handle URL-encoded paths and provide a narrowly documented allowlist for intentionally stable public URLs or assets that cannot be discovered statically.
  • Choose and document one canonical source asset for each logical image. Keep vector artwork such as logos as SVG when appropriate; do not retain redundant raster exports. Remove byte-identical copies, unused images, and committed delivery variants after updating every consumer to the canonical asset identity.
  • Consolidate the existing one-off sharp scripts into a deterministic build-time image step. Store canonical inputs outside Vite's pass-through deployment set (or otherwise exclude them from dist/), generate the required responsive widths and formats during npm run build and npm run build:ci, and do not commit generated variants.
  • Update image consumers to use the generated outputs, including responsive srcset/<picture> behavior where multiple widths or formats materially reduce transfer size. Preserve explicit dimensions or aspect-ratio reservation, meaningful alt text, intentional decorative alt text, eager/high-priority loading for above-the-fold images, and lazy loading elsewhere.
  • Add a repository-owned asset policy with explicit maximum source dimensions, source byte size, and generated-variant byte size. Derive the initial limits from audited rendered dimensions and required pixel density; do not silently grandfather the current 7K–8.5K-pixel or 10–17 MB raster files. Any exception must be narrowly allowlisted with a reason.
  • Add npm run check:assets and run it in the pull-request and deployment workflows before the production build. It must fail on a limit violation, a new byte-identical duplicate, a missing referenced asset, a generated variant committed to source control, or an unreferenced asset that is not allowlisted.
  • Record the before/after file count and byte totals for both committed canonical inputs and the generated dist/ assets in the implementation pull request. Migrating the site to Astro is out of scope; the solution must work with the current React/Vite build.

Acceptance criteria

  • A clean checkout can run the documented asset inventory and reports zero byte-identical duplicate image groups and zero unreferenced images outside the documented allowlist.
  • Each logical image has one tracked canonical source; required WebP, AVIF, fallback, and responsive-width files are reproducibly generated by npm run build/npm run build:ci and are not tracked in Git.
  • The generated deployment output excludes unused images and oversized canonical originals unless a documented runtime requirement explicitly needs one.
  • npm run check:assets enforces the documented dimension and byte-size limits and has automated coverage proving that an oversized image, duplicate content, missing reference, and committed generated variant each fail the check.
  • Both GitHub Actions workflows run the asset check before building, and npm run check, npm run build, and the site tests pass from a clean checkout.
  • The homepage and /insights/case-studies have no broken images or layout regressions at representative desktop and mobile viewports; responsive format selection, aspect-ratio reservation, alt text, and eager/lazy loading behavior remain correct.
  • The implementation pull request reports the verified before/after repository and dist/ asset totals and demonstrates that at least the measured 75.9 MiB of byte-identical duplication is no longer committed or deployed.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions