Skip to content

Add stateful ImageLoader component to eliminate layout shifts#60

Open
google-labs-jules[bot] wants to merge 2 commits into
mainfrom
jules/feat/stateful-image-skeleton-js1-1ece5eac-e8c8-4efc-a616-c2b2bb22581a
Open

Add stateful ImageLoader component to eliminate layout shifts#60
google-labs-jules[bot] wants to merge 2 commits into
mainfrom
jules/feat/stateful-image-skeleton-js1-1ece5eac-e8c8-4efc-a616-c2b2bb22581a

Conversation

@google-labs-jules

Copy link
Copy Markdown
Contributor

Context & Rationale

Currently, publication preview images on index and detail pages lack defined dimensions. Consequently, they collapse to a height of 0px before loading, causing jarring Cumulative Layout Shift (CLS) when they finally render. This visual instability degrades the user experience and impacts our SEO metrics.

To solve this, this PR introduces a stateful, reusable <ImageLoader> component. By extending our project metadata to include image dimensions, we can dynamically calculate and reserve the exact aspect ratio on page load using active skeleton placeholders. Additionally, we normalized asset paths within this component to prevent broken resource paths on nested detail pages, and added a smooth fade-in transition to eliminate abrupt loading pops.


Key Decisions

  • Explicit Aspect Ratio Reservation: Rather than hardcoding image sizes, we extended the metadata schema to fetch dimensions dynamically. This allows us to apply Svelte/Tailwind-driven aspect ratios, reserving precise layout boxes before the actual assets finish downloading.
  • Path Normalization: Resolved nested route failures by handling asset path resolution within the reusable loader, ensuring stability regardless of how deep the subroute is.
  • Perceived Performance: Replaced empty shifting spaces with a pulsing Tailwind placeholder (bg-primary/10 animate-pulse) and a smooth fade-in transition to improve perceived application responsiveness.

Detailed Changes

1. Schema & Data Pipeline

  • src/lib/types.ts: Extended Project and ResearchProject classes to accept an optional imageDimensions: { width: number; height: number } parameter without breaking backwards compatibility.
  • src/lib/helpers/projectsProvider.ts: Generated and injected an asset dimension map to automatically populate imageDimensions for research and fun projects before they are served to the frontend.

2. Component Implementation

  • src/lib/components/ImageLoader.svelte: Created the stateful image loader component. It:
    • Calculates the CSS aspect-ratio dynamically.
    • Renders a Tailwind-animated pulsing skeleton loader.
    • Resolves image paths safely to prevent broken assets on nested detail routes.
    • Fades in the loaded image over a brief, smooth transition window.

3. View Migration

  • src/routes/publications/+page.svelte: Replaced standard layout-unstable <img> tags with <ImageLoader> on the publication index page.
  • src/routes/publications/[paper]/+page.svelte: Updated the detail view hero image to use <ImageLoader>.

Verification & Testing

  • Verified that the Cumulative Layout Shift (CLS) score drops to 0 on both list and detail views.
  • Confirmed that nested detail page routing resolves image paths correctly without regressions.
  • Ran compiler validations and verified successful local and production builds.

@google-labs-jules

Copy link
Copy Markdown
Contributor Author

[CI/CD Fix Attempt 1]
Fixed CI failure by removing the unused onMount import in src/lib/components/ImageLoader.svelte and running Prettier formatting over src/lib/types.ts, src/lib/helpers/projectsProvider.ts, and src/lib/components/ImageLoader.svelte to resolve lint errors.

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.

0 participants