Optimize image handling and restore layout specifications#10
Optimize image handling and restore layout specifications#10mhaadiabu wants to merge 16 commits into
Conversation
The .astro/ directory is regenerated on every build/dev run and contains content schemas, generated types, and the data store. Tracking it causes spurious diffs on every build.
The .btn-mission is inline-flex inside a flex column with default align-items: stretch, so it stretched to full column width and its centered text looked misaligned. align-self: flex-start keeps the button at content width on the left.
font-size referenced --text-s, an undefined CSS variable, so eyebrow labels fell back to inherited 1rem. Switched to --text-xs (0.75rem), the actual small-uppercase label size in the type scale.
AGENTS.md documents that partner logos with a backgroundColor get 8px padding via the has-bg class. The implementation had drifted to 20px. Restored to 8px per spec.
Cards previously lifted on hover (translate -2,-2 and a shadow appeared), which fought the static offset shadows already on .stat-card, .reg-card, and .stream-wrap — the hover shadow stacked on top of the static one and the card jumped in the wrong direction. Inverted the mechanic: cards now rest with an offset shadow, and on hover they translate by the shadow's offset so the shadow is absorbed into the card — a clean Dodonut-style press. The bouncy cubic-bezier on .card adds a tactile feel without being noisy. Per-card hover rules were added for the three components that use a larger colored shadow (.stat-card 6px pink, .reg-card 6px black, .stream-wrap 8px pink) so the press exactly absorbs each one.
Allow Astro's image optimizer to fetch and process remote images used across the site (Twitter avatars, GitHub avatars, Slack CDN, partner CDNs). www.unicef.org is intentionally left out — the source URL returns 403 to the optimizer; the partner logo still loads via <Image> without optimization.
Astro's default image service uses sharp to transform and recompress images at build time. Adding it as a direct dependency so remote images can be optimized into webp/avif with proper srcset generation.
Moving logo.png into src/ so Astro can bundle and optimize it. The unused logo-nav.png, logo-sm.png, logo-source.png, and logo@2x.png variants are dropped — they were never referenced anywhere in the codebase.
Replaced raw <img> with <Image> in four files: - Navbar, Footer, and both meetup nav bars: logo now imports from src/assets/ instead of /images/logo.png. The component infers intrinsic dimensions and lazy-decodes. - meetups/[slug].astro: speaker portraits use Image at 400x400, and photo gallery thumbnails at 1600x1200 — the sizes those remote sources actually are. The lightbox <img> stays because its src is set at runtime via JS.
Astro's <Image> requires intrinsic width/height for remote sources to prevent layout shift. Adding optional width/height fields to the partner schema so partner logos can declare their real dimensions.
Each partner YAML now declares the natural width/height of its logo. Values come from explicit dimensions in the source URL where present (mest 300x49, old-mutual 960x440, paystack 1600 wide) and from the SVG viewBox for fido (332x96). Kweku and unicef are estimated from visual inspection.
Replaced <img> in PartnersSection with <Image>, passing through the per-partner width/height from the content data (falling back to 300x100 if missing). The backgroundColor inline style and has-bg class flow through to the <Image> output, preserving the tinted logo cards.
Replaced <img> in AdminsSection with <Image>, sized to 400x400 — the natural size of the Twitter avatars used. Width/height attrs now flow through so the cards don't shift on load.
Astro regenerates the .astro/ directory on every build/dev run — content schemas, type definitions, and the data store. It was being tracked from before .gitignore had an entry, so it kept showing up as modified on every build. The directory is already in .gitignore; this just removes the existing tracked files from the index. They'll be regenerated locally on the next dev/build and never pushed.
- Add overflow-x: clip to html and body (clip is stricter than hidden and prevents the iOS Safari rubber-band scroll when any descendant is wider than the viewport) - Reduce hero button horizontal padding on mobile (<600px) so the CTAs fit within the viewport - Replace implicit minmax(auto, 1fr) with explicit minmax(0, 1fr) in all grid-template-columns — the auto minimum was allowing columns to grow past their fr share to fit wide text content, pushing the document wider than the viewport - Center the mission section content on mobile, make the Join CTA full-width with a media query restoring left-align and flex-start on >=900px
The previous flex layout with fixed height: 100px and width: auto let wide logos (Paystack 1600x280, MEST 300x49) overflow the viewport on small screens — they were being cut off by the body overflow clip. Switched to a responsive grid (2/3/4/6 cols at 0/500/768/1024px) and constrain each logo with max-width: 100% + max-height: 72px on mobile (100px on desktop) + width/height auto, so every logo scales to fit its cell while keeping its aspect ratio.
bubunyo
left a comment
There was a problem hiding this comment.
there are some good changes, but the enforced image dimensions doesnt not seem to be in the right direction. maybe if you can provide some proof on how some of them work, this should not be a problem.
| logo: https://cdn.prod.website-files.com/62fa16d8d83816b0fc5defc4/62fcf5fdb02a38631df13edb_footer_logo.svg | ||
| url: https://gh.fido.money/ | ||
| backgroundColor: "#d6086b" | ||
| width: 332 |
There was a problem hiding this comment.
not having a width contstraint but just a height constraint was intentional.
There was a problem hiding this comment.
those are more of ratio constraints than dimension constraints.
without them the images would load visually fine (CSS handles display), but there'd be CLS. the browser reserves space at the fallback 3:1 ratio, then snaps to the real aspect ratio on load, which is going to end up pushing the other grid items around.
For example, with fallback 300:100:
- Paystack (1600×280 = 5.7:1) → placeholder would reserve 3:1, then jump to 5.7:1 at 72px tall
- Old Mutual (960×440 = 2.2:1) → placeholder reserves 3:1, snaps to 2.2:1
- UNICEF (400×400 = 1:1) → placeholder 3:1, snaps to square
the 'constraints', i think the more appropriate term would be definitions, gives the browser the right ratio beforehand. prevents layout shift
There was a problem hiding this comment.
i am more worried about the images being skewed. will that happen with the contraints?
There was a problem hiding this comment.
not sure if i'm fully grasping the concern here, correct me if i'm not though
but what we're basically doing here is sort of providing restrictive grid areas for ... let's call it consistency
so it's like 'hey, paystack image, you've only got this much area to put yourself in. you can scale full width or full height in that area, you're only not allowed to break out of it. when you hit full width vertical alignment is centred. and vice versa.'
so images aren't clipped. we're just allocating a reasonable amount of area, equally to all images. where they scale as suited. allocation is enough for images to be visible and not too much so the raster effects are noticeable
There was a problem hiding this comment.
replied w wrong account, sorry
There was a problem hiding this comment.
so does that mean with every image, the size info is to bee added? i feel like there should be an easier way to do that than put very specific size numbers. Also what about skewness
There was a problem hiding this comment.
- Local images (in src/): no — Astro infers dimensions automatically (per the docs: "infers image dimensions to avoid CLS"). The site logo import needs nothing.
- Remote images: yes, width/height are required because Astro doesn't fetch the remote file to probe its dimensions at build. But the numbers only need the correct aspect ratio, not exact pixels — they set the intrinsic ratio so the browser reserves the right box. CSS controls the actual display size.
Easier path: we could drop the per-logo width/height YAML fields and store a single aspectRatio (or just a height), then derive width in the component. That keeps it to one number per logo and removes the "very specific size numbers" smell. A sane default (e.g. 3:1) covers anything unspecified.
Also, no skew. object-fit: contain + width: auto + a height cap preserves the source aspect ratio. The width/height on only set the intrinsic ratio for the optimizer/CLS; CSS governs display. Skew would require object-fit: fill or fixing both CSS width and height to mismatched values — we do neither. Every logo keeps its real proportions.
| aria-label={`Expand photo ${i + 1}`} | ||
| > | ||
| <img src={photo.url} alt={`${d.name} — photo ${i + 1}`} loading="lazy" decoding="async" /> | ||
| <Image src={photo.url} alt={`${d.name} — photo ${i + 1}`} width={1600} height={1200} loading="lazy" decoding="async" /> |
There was a problem hiding this comment.
setting both width and height? is that that right thing to do espcially since we dont enforce image dimensions.
There was a problem hiding this comment.
those are kind of fallback values since width and height values are required by the Astro Image component.
sort of like the Next.js Image component








Fix broken UI and migrate all
<img>to Astro's<Image>Five small UI fixes plus a full
<img>→<Image>migration.UI fixes
flex-start.Before:

After:

.eyebrowreferenced an undefined--text-stoken and fell back to 1rem. Now points at--text-xs..has-bgpadding was 20px; AGENTS.md documents 8px. Restored.Before:


After:


.cardhover used to lift the card (translate(-2,-2)+ shadow appears), which fought the static offset shadows on.stat-card,.reg-card, and.stream-wrap. Inverted: cards rest with an offset shadow and press down on hover to absorb it. Bouncy easing. Same mechanic applied per-card so each absorbs its own larger colored shadow.Before:
After:

<img>→<Image>src/assets/and imported in Navbar, Footer, and both meetup nav bars.width/heightfrom YAML data; schema extended.<img>left alone (src is set at runtime by JS).No visual change for end users. Build emits webp/avif with proper srcset.
Tooling
sharpadded so remote images actually get optimized.astro.config.mjs.www.unicef.orgexcluded — source returns 403 to the optimizer; logo still renders, just unoptimized..astro/added to.gitignoreand untracked. 11 stale tracked files removed from the index.Build impact
Sharp optimizes ~12 remote sources at build. Biggest: 328KB GitHub avatar → 21KB webp.
pnpm buildstays under 6s.Test plan
pnpm devand scroll the home page — confirm mission CTA left-aligns, eyebrow labels are small uppercase, partner logos have tighter padding..card(stats, program, meetup) — confirm it presses down rather than lifts.<Image>(no layout shift).pnpm buildsucceeds; only warnings should be pre-existing remote URL issues.