Reduces HTTP requests from 44+ individual images to 3 sprite sheets (~93% reduction). Includes Go sprite generator tool, CSS classes, template integration, and E2E tests. - Add cmd/sprites/main.go for sprite generation (60x60px + 120x120px @2x) - Add _sprites.css with responsive sizing and retina support - Update templates to use sprites with logoIndex fallback - Add Makefile targets: sprites, sprites-clean - Add 9-test E2E suite for sprite functionality - Add doc/22-SPRITES.md with usage documentation
5.6 KiB
CSS Sprites - Image Request Optimization
Overview
The CV website uses CSS sprites to dramatically reduce HTTP requests for company, project, and course logos. Instead of loading 44+ individual image files, we load only 3 sprite sheets (6 files total including retina versions).
Performance Impact
| Metric | Before | After | Improvement |
|---|---|---|---|
| Image Requests | 44+ | 3-6 | ~93% reduction |
| Cache Invalidation | Per image | Per sprite | Simplified |
| HTTP Overhead | 44 round-trips | 3-6 round-trips | Dramatic reduction |
Architecture
File Structure
static/
├── images/
│ ├── companies/ # Source images (any size)
│ ├── projects/ # Source images (any size)
│ ├── courses/ # Source images (any size)
│ └── sprites/ # Generated sprites
│ ├── sprite-companies.png
│ ├── sprite-companies@2x.png
│ ├── sprite-projects.png
│ ├── sprite-projects@2x.png
│ ├── sprite-courses.png
│ ├── sprite-courses@2x.png
│ └── sprite-map.json
├── sprite-showcase.html # Visual QA page
└── css/
└── 04-interactive/
└── _sprites.css # Sprite CSS classes
Go Sprite Generator Tool
Located at cmd/sprites/main.go, this tool:
- Scans source directories for PNG images
- Normalizes images to standard sizes (60x60px for 1x, 120x120px for 2x)
- Maintains aspect ratio and centers on transparent background
- Combines into horizontal strips for each category
- Generates sprite-map.json for documentation
- Creates sprite-showcase.html for visual QA
Image Size Standards
- Base size: 60x60px (optimal for 80px display box with 10px padding)
- Retina size: 120x120px (@2x for high-DPI displays)
- Section display: 80x80px box (60px icon + 10px padding each side)
Usage
Makefile Targets
# Generate sprites from source images
make sprites
# Clean generated sprite files
make sprites-clean
JSON Data Structure
Add logoIndex to entries in cv-en.json and cv-es.json:
{
"company": "Olympic Broadcasting Services",
"companyLogo": "olympic-broadcasting.png",
"logoIndex": 15
}
Important: Only add logoIndex when there's an actual PNG file. Entries without a logo file should not have logoIndex.
Template Integration
Templates automatically use sprites when logoIndex is present:
{{if .LogoIndex}}
<span class="icon-sprite icon-section icon-company"
style="--icon-index: {{.LogoIndex}};"
role="img"
aria-label="{{.Company}} logo"></span>
{{else if .CompanyLogo}}
<img src="/static/images/companies/{{.CompanyLogo}}" alt="{{.Company}} logo">
{{else}}
<iconify-icon icon="mdi:office-building" width="80" height="80"></iconify-icon>
{{end}}
CSS Classes
/* Base sprite class */
.icon-sprite {
display: inline-block;
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-size: auto 50px;
}
/* Category-specific classes */
.icon-company { background-image: url('/static/images/sprites/sprite-companies.png'); }
.icon-project { background-image: url('/static/images/sprites/sprite-projects.png'); }
.icon-course { background-image: url('/static/images/sprites/sprite-courses.png'); }
/* Size variants */
.icon-sprite.icon-section {
width: 80px;
height: 80px;
padding: 10px;
background-size: auto 60px;
background-origin: content-box;
background-clip: content-box;
}
.icon-sprite.icon-small { width: 32px; height: 32px; }
.icon-sprite.icon-large { width: 64px; height: 64px; }
Adding New Icons
-
Drop source image into appropriate directory:
static/images/companies/for company logosstatic/images/projects/for project logosstatic/images/courses/for course logos
-
Run sprite generation:
make sprites -
Update JSON files with new
logoIndexbased on sprite-map.json -
Verify in showcase page at
/static/sprite-showcase.html
Retina Display Support
The CSS automatically loads @2x sprites on retina displays:
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.icon-company {
background-image: url('/static/images/sprites/sprite-companies@2x.png');
background-size: auto 60px; /* Display at 1x size */
}
}
Sprite Map JSON
The sprite-map.json file documents icon positions:
{
"companies": [
{"index": 0, "name": "accenture.png"},
{"index": 1, "name": "aena-long.png"},
...
],
"projects": [...],
"courses": [...]
}
This file is for documentation/debugging only - CSS calculates offset from index using calc(var(--icon-index) * -60px).
Verification
Showcase Page
Visit /static/sprite-showcase.html to:
- View full sprite sheets
- See all individual icons with index labels
- Test zoom levels (100%, 200%, 300%)
- Verify retina rendering
Network Verification
In browser DevTools (Network tab, filter Images):
- Should see: sprite-companies.png, sprite-projects.png, sprite-courses.png
- Should NOT see: individual logo files (unless fallback triggers)
Troubleshooting
Invalid PNG Warning
If you see "png: invalid format: not a PNG file", the source file is not a valid PNG. Check the file with file <filename> to verify format.
Icon Not Displaying
- Verify
logoIndexis present in JSON - Check sprite-map.json for correct index
- Verify CSS is loaded
- Check browser console for errors
Wrong Icon Displayed
Verify the logoIndex value matches the icon's position in sprite-map.json (0-indexed).