mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-05 14:54:50 +08:00
4.7 KiB
4.7 KiB
Unsplash Images — Implementation Guide
URL Format
Always use the direct images.unsplash.com format with explicit sizing:
https://images.unsplash.com/photo-{PHOTO_ID}?w={WIDTH}&q={QUALITY}
| Parameter | Purpose | Recommended values |
|---|---|---|
w |
Pixel width served by the CDN | 600 card, 800 section, 1200 hero, 1920 full-bleed |
q |
JPEG quality 1–100 | 80–85 (good balance of quality vs size) |
Deprecated / broken formats — do NOT use
| Format | Why it fails |
|---|---|
source.unsplash.com/random |
Deprecated; returns 404 |
source.unsplash.com/{WIDTH}x{HEIGHT} |
Deprecated; returns 404 |
source.unsplash.com/featured/?{query} |
Deprecated; returns 404 |
How to find a valid photo ID
- Go to unsplash.com and search for the subject (e.g. "modern apartment").
- Open a photo. The URL will look like
unsplash.com/photos/{slug}-{PHOTO_ID}orunsplash.com/photos/{PHOTO_ID}. - The
PHOTO_IDis the last hyphen-separated segment (e.g.photo-1600596542815-ffad4c1539a9). - Build your URL:
https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=1200&q=85 - Test the URL in a browser before committing.
Declaring image constants
Define all Unsplash URLs as named constants at the top of the file. Never inline URLs in JSX.
const HERO_IMAGE = "https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=1200&q=85";
const SECTION_IMAGE = "https://images.unsplash.com/photo-1514565131-fce0801e5785?w=800&q=85";
Rendering images in JSX
Hero / banner (decorative — empty alt)
<div className="relative w-full overflow-hidden rounded-2xl">
<div className="relative aspect-[21/9] min-h-[280px] w-full md:aspect-[3/1]">
<img
src={HERO_IMAGE}
alt=""
className="h-full w-full object-cover"
loading="eager"
fetchPriority="high"
/>
<div className="absolute inset-0 bg-black/40" />
<div className="absolute inset-0 flex flex-col items-center justify-center px-4">
{/* overlay content */}
</div>
</div>
</div>
Key points:
alt=""for decorative images (screen readers skip them).loading="eager"andfetchPriority="high"for above-the-fold heroes.object-coverprevents stretching.- Semi-transparent overlay (
bg-black/40) ensures text readability.
Section accent (meaningful — descriptive alt)
<div className="relative min-h-[240px] overflow-hidden rounded-2xl">
<img src={SECTION_IMAGE} alt="City skyline at sunset" className="h-full w-full object-cover" />
<div className="absolute inset-0 bg-gradient-to-r from-black/20 to-transparent" />
</div>
Card thumbnail
<div className="aspect-[4/3] overflow-hidden bg-muted">
<img
src={imageUrl}
alt=""
className="h-full w-full object-cover transition-transform hover:scale-105"
loading="lazy"
/>
</div>
Key points:
loading="lazy"for below-the-fold images.hover:scale-105subtle zoom on hover.bg-mutedfallback color while loading.
Fallback when no image is available
Always provide a placeholder when the image URL may be null:
{imageUrl ? (
<img src={imageUrl} alt="" className="h-full w-full object-cover" />
) : (
<div className="flex h-full items-center justify-center text-muted-foreground">
No image
</div>
)}
Content Security Policy (CSP)
If the application enforces CSP headers, add images.unsplash.com to img-src:
img-src 'self' https://images.unsplash.com;
Other commonly needed origins for stock images:
| Origin | Purpose |
|---|---|
images.unsplash.com |
Unsplash photos |
images.pexels.com |
Pexels photos |
fonts.googleapis.com |
Google Fonts CSS |
fonts.gstatic.com |
Google Fonts files |
Accessibility checklist
- Decorative images have
alt="" - Meaningful images have descriptive
alttext - Hero images use
loading="eager"andfetchPriority="high" - Below-fold images use
loading="lazy" - Text over images has sufficient contrast (use overlay like
bg-black/40) - All URLs verified to return a valid image (no 404s)
Common mistakes
| Mistake | Fix |
|---|---|
Using source.unsplash.com |
Replace with images.unsplash.com/photo-{id}?w=…&q=… |
| Hardcoding an unverified URL | Open URL in browser first; replace if broken |
Missing w parameter |
Always set width — CDN returns full-res (5000px+) otherwise |
loading="lazy" on hero |
Use loading="eager" for above-the-fold images |
| No fallback for nullable URLs | Wrap in conditional with placeholder div |
| Inline URLs in JSX | Extract to named constants at file top |