3.5 KiB
3.5 KiB
1. CSS shimmer styles
- 1.1 Add
@keyframes shimmeranimation toglobal.css— a translucent gradient sweep (left-to-right) that works on the dark theme background (--bg0/--bg1palette). - 1.2 Add
.img-shimmer-wrapclass toglobal.css—position: relative; overflow: hidden;container that inherits dimensions from its parent. Add a::beforepseudo-element with the shimmer animation (full width/height, absolute positioned, translucent light gradient). - 1.3 Add
.img-loadingclass toglobal.css— setsopacity: 0on the<img>element. Add transition:opacity 250ms ease. - 1.4 Add
.img-errorclass toglobal.css— stops the shimmer animation on the wrapper (animation: noneon::before) so the placeholder displays as a static block. - 1.5 Verify the existing
@media (prefers-reduced-motion: reduce)rule inglobal.cssalready suppresses the shimmer@keyframesanimation (it should — the global rule setsanimation-duration: 0.001ms !important).
2. Card thumbnail shimmer
- 2.1 Update
StandardCard.astro— wrap the existing<img>in a<div class="img-shimmer-wrap">. Add classimg-loadingto the<img>element. Keep the existing.card-placeholderfallback for cards with no image (no shimmer needed there). - 2.2 Ensure the
.img-shimmer-wrapinside.card-mediainherits the correct dimensions (width: 100%; height: 180pxon desktop,200pxon mobile) without causing layout shift.
3. Blog featured image shimmer
- 3.1 Update
blog/post/[slug].astro— wrap the featured<img>in a<div class="img-shimmer-wrap">with matching inline styles (width: 100%; max-height: 420px; border-radius: 16px; overflow: hidden;). Add classimg-loadingto the<img>. - 3.2 Update
blog/page/[slug].astro— same shimmer wrapper treatment as blog posts.
4. Image load/error script
- 4.1 Add an inline
<script is:inline>inBaseLayout.astro(or a shared location) that runs on DOM ready. For everyimg.img-loadingelement: ifimg.complete && img.naturalWidth > 0, removeimg-loadingimmediately (cached images). Otherwise, addloadevent listener to removeimg-loadinganderrorevent listener to addimg-errorto the wrapper. - 4.2 Verify the script handles dynamically added images (not currently needed — SSG renders all images server-side — but ensure the script runs after all DOM is ready).
5. Verification
- 5.1 Run
npm run buildinsite/and verify no build errors. - 5.2 Run
npm testinsite/and verify all existing tests pass (1 pre-existing failure in blog-nav.test.ts unrelated to this change — expects/aboutnav link that doesn't exist). - 5.3 Manual smoke test: throttle network in DevTools (Slow 3G), load homepage — verify shimmer appears on card thumbnails, images fade in on load.
- 5.4 Manual smoke test: load a blog post with a featured image on throttled network — verify shimmer and fade-in.
- 5.5 Manual smoke test: break an image URL temporarily — verify static placeholder shows (no broken image icon, no infinite shimmer).
- 5.6 Manual smoke test: enable
prefers-reduced-motion: reducein browser/OS settings — verify shimmer animation is suppressed (static placeholder, no sweep). - 5.7 Verify no cumulative layout shift: compare card dimensions before and after the change using DevTools Layout Shift overlay.
Note: Tasks 5.3–5.7 require manual browser testing with DevTools network throttling.