b5a50ca3ef
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
208 lines
5.6 KiB
Markdown
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).
|