Files
juanatsap b5a50ca3ef feat: implement CSS sprite system for image optimization
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
2025-12-04 11:38:36 +00:00

208 lines
5.6 KiB
Markdown

# 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:
1. **Scans source directories** for PNG images
2. **Normalizes images** to standard sizes (60x60px for 1x, 120x120px for 2x)
3. **Maintains aspect ratio** and centers on transparent background
4. **Combines into horizontal strips** for each category
5. **Generates sprite-map.json** for documentation
6. **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
```bash
# 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:
```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:
```html
{{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
```css
/* 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
1. **Drop source image** into appropriate directory:
- `static/images/companies/` for company logos
- `static/images/projects/` for project logos
- `static/images/courses/` for course logos
2. **Run sprite generation**:
```bash
make sprites
```
3. **Update JSON files** with new `logoIndex` based on sprite-map.json
4. **Verify** in showcase page at `/static/sprite-showcase.html`
## Retina Display Support
The CSS automatically loads @2x sprites on retina displays:
```css
@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:
```json
{
"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
1. Verify `logoIndex` is present in JSON
2. Check sprite-map.json for correct index
3. Verify CSS is loaded
4. Check browser console for errors
### Wrong Icon Displayed
Verify the `logoIndex` value matches the icon's position in sprite-map.json (0-indexed).