Create a complete CSS sprite system for company, project, and course icons using Go. This dramatically improves page load performance by reducing HTTP requests from 44+ individual images to just 3 sprite sheets. PERFORMANCE IMPACT: - Current: 23 company + 12 project + 9 course = 44 separate HTTP requests - Target: 3 sprite images (one per category) - Result: ~93% reduction in image requests CRITICAL REQUIREMENTS: 1. **Automated normalization**: Users throw ANY size image into source folders → system automatically normalizes to icon size 2. **Go implementation**: Use Go with native/standard libraries (prefer stdlib, avoid heavy dependencies) 3. **One command**: `make sprites` handles everything (normalize + generate + update registry) 4. **Documentation required**: Create doc/XX-SPRITES.md following existing doc/ patterns 5. **Tests required**: Create tests/mjs/XX-sprites.test.mjs following existing test patterns Project: CV website (Go + HTMX) Current image structure: - static/images/companies/*.png (23 images, various sizes) - static/images/projects/*.png (12 images, various sizes) - static/images/courses/*.png (9 images, various sizes) JSON references (data/cv-en.json, data/cv-es.json): - experience[].companyLogo: "filename.png" - projects[].projectLogo: "filename.png" - courses[].courseLogo: "filename.png" Existing patterns to follow: - Tests: tests/mjs/XX-name.test.mjs (Playwright E2E, numbered) - Docs: doc/XX-NAME.md (numbered markdown) - Scripts: scripts/*.sh (deployment tools) - Makefile: existing targets for build, test, css-prod Before implementing, thoroughly research: 1. Go native image processing libraries (image/png, image/draw, golang.org/x/image) 2. Best approach for image resizing with aspect ratio preservation in Go 3. How to composite images into horizontal strips in Go 4. Whether to use cmd/sprites/ pattern or internal/tools/ for Go tooling ## 1. Go Sprite Generator Tool Create a Go tool that: - Scans source folders for images (any size/format) - Normalizes each to 48x48px (1x) and 96x96px (2x retina) - Maintains aspect ratio, centers on transparent background - Combines into horizontal sprite strips - Generates ICON-REGISTRY.md with positions Location options to evaluate: - `cmd/sprites/main.go` (separate binary) - `internal/tools/sprites/` (internal package) - Prefer native Go libs: `image`, `image/png`, `image/draw` - If needed: `golang.org/x/image/draw` for better scaling ## 2. Icon Size Standards - Base: 48x48px (readable at 100% zoom, works up to 300%) - Retina: 96x96px (@2x for high-DPI displays) - WHY 48px: Standard icon size, crisp at all zoom levels, small file size ## 3. Naming Convention Source images (user drops these - ANY size): ``` static/images/companies/olympic-broadcasting.png (could be 500x300) static/images/companies/sap.png (could be 100x100) ``` Generated outputs: ``` static/images/sprites/sprite-companies.png (horizontal strip, 48px tall) static/images/sprites/sprite-companies@2x.png (horizontal strip, 96px tall) static/images/sprites/sprite-projects.png static/images/sprites/sprite-projects@2x.png static/images/sprites/sprite-courses.png static/images/sprites/sprite-courses@2x.png ``` ## 4. JSON Integration Add `logoIndex` to each entry in cv-en.json and cv-es.json: ```json { "company": "Olympic Broadcasting Services", "companyLogo": "olympic-broadcasting.png", "logoIndex": 0 } ``` **⚠️ DO NOT TOUCH - Preserve these existing patterns:** 1. **Empty logo fields** - Some projects have `"projectLogo": ""` (no image). Leave as-is, no logoIndex. ```json // KEEP AS-IS - no sprite integration { "projectLogo": "", ... } ``` 2. **Iconify icons in HTML** - Course/project items use inline `` for individual entries (e.g., Go courses from Udemy). These are in the `responsibilities` array, NOT the logo field. Do not modify. ```json // KEEP AS-IS - uses Iconify system, not image sprites "responsibilities": [ "
Go - The Complete Guide
" ] ``` **RULE: Only add logoIndex when there's an actual PNG file in companyLogo/projectLogo/courseLogo** **INDEX ORDERING: Chronological (oldest first)** - Index 0 = oldest/first experience in career - Last index = most recent/current experience - This matches natural reading order (career progression) **KEEP CV JSON CLEAN:** - Only add `logoIndex` field - nothing else - NO offset in JSON (calculated as `index × 48px` in CSS) - The CV JSON is an INFO document, minimize pollution The Go tool generates a SEPARATE mapping file (not in CV JSON): ```json // static/images/sprites/sprite-map.json (reference only, not used at runtime) { "companies": [ {"index": 0, "name": "oldest-company.png"}, {"index": 1, "name": "next-company.png"}, {"index": 10, "name": "olympic-broadcasting.png"} ], "projects": [...], "courses": [...] } ``` This file is for documentation/debugging only - the CSS calculates offset from index. ## 5. CSS Integration Create `static/css/04-interactive/_sprites.css`: ```css .icon-sprite { display: inline-block; width: 48px; height: 48px; background-repeat: no-repeat; background-size: auto 48px; } .icon-company { background-image: url('/static/images/sprites/sprite-companies.png'); background-position-x: calc(var(--icon-index, 0) * -48px); } /* Retina */ @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 48px; /* Display at 1x size */ } } ``` ## 6. Template Integration Update templates to use sprites: ```html {{if ge .logoIndex 0}} {{else if .companyLogo}} {{.company}} logo {{end}} ```
PHASE 1: Go Tool Creation 1. Research best Go approach for image processing 2. Create sprite generator tool 3. Implement: scan → normalize → combine → output 4. Generate sprite-map.json for reference PHASE 2: Integration 1. Create _sprites.css with positioning classes 2. Update main.css to import sprites 3. Update JSON files with logoIndex values 4. Modify templates to render sprites PHASE 3: Makefile & Workflow 1. Add `make sprites` target 2. Add `make sprites-clean` target 3. Document workflow in doc/XX-SPRITES.md PHASE 4: Testing & Documentation 1. Create tests/mjs/XX-sprites.test.mjs 2. Create doc/XX-SPRITES.md 3. Update PROJECT-MEMORY.md with sprite system rules Files to CREATE: - `cmd/sprites/main.go` (or appropriate location) - Go sprite generator - `static/css/04-interactive/_sprites.css` - Sprite CSS classes - `static/images/sprites/` - Output directory - `static/images/sprites/sprite-map.json` - Icon position mapping - `static/sprite-showcase.html` - **AUTO-GENERATED** visual showcase page - `doc/XX-SPRITES.md` - Complete documentation - `tests/mjs/XX-sprites.test.mjs` - E2E tests Files to MODIFY: - `Makefile` - Add sprites targets - `static/css/main.css` - Import sprites CSS - `data/cv-en.json` - Add logoIndex to all entries - `data/cv-es.json` - Add logoIndex to all entries - `templates/partials/sections/experience.html` - Use sprite rendering - `templates/partials/sections/projects.html` - Use sprite rendering - `templates/partials/sections/courses.html` - Use sprite rendering - `PROJECT-MEMORY.md` - Document sprite system rules **CRITICAL DOCUMENTATION UPDATE:** - `doc/2-MODERN-WEB-TECHNIQUES.md` - Add new section "12. CSS Sprites - Image Request Optimization" This document tracks all performance optimizations with metrics. Add a comprehensive section including: - Problem statement (44 HTTP requests for individual images) - Solution (CSS sprites with Go generator) - Before/After metrics table - Implementation details (Go tool, CSS positioning, template integration) - Benefits list (93% HTTP reduction, single cache invalidation, etc.) - Browser support - Testing approach Follow the existing document pattern (see sections 1-11 for format/style) REQUIRED TESTS (must all pass): 1. `make sprites` completes without errors 2. Sprite images exist in static/images/sprites/ 3. sprite-map.json has correct positions for all icons 4. `make build` passes 5. `make css-prod` compiles successfully 6. E2E test verifies: - Only 3 sprite images loaded (not 44 individual) - All logos display correctly - Sprites work at different zoom levels - Retina sprites load on high-DPI **SHOWCASE PAGE (Critical for visual verification):** Create `static/sprite-showcase.html` - A standalone HTML page that displays: - All 3 sprite sheets as full images (visible strips) - Grid of all individual icons extracted via CSS positioning - Icon index numbers displayed under each icon - Category headers (Companies, Projects, Courses) - Zoom test section (100%, 200%, 300%) - Retina vs 1x comparison This page serves as: 1. Visual QA during development 2. Documentation for icon positions 3. Test fixture for E2E tests 4. Reference for future icon additions Example structure: ```html Sprite Showcase

CSS Sprite Showcase

Companies (Full Sprite)

Companies sprite

Individual Icons

Zoom Test

100%:
200%:
300%:
``` The Go tool should AUTO-GENERATE this showcase page from sprite-map.json. Manual verification: ```bash # Check sprite dimensions (should be width = 48 * icon_count, height = 48) identify static/images/sprites/sprite-companies.png # Open showcase page open http://localhost:1999/static/sprite-showcase.html # Verify network requests # Open browser DevTools → Network → filter Images # Should see: sprite-companies.png, sprite-projects.png, sprite-courses.png # Should NOT see: individual logo files ```
- [ ] Go tool processes any-size images automatically - [ ] 3 sprite sheets generated (1x and 2x each = 6 files total) - [ ] ICON-REGISTRY or sprite-map.json documents all positions - [ ] CSS sprites work with zoom up to 300% - [ ] Retina displays show crisp icons - [ ] `make sprites` is single command for regeneration - [ ] doc/XX-SPRITES.md created following project patterns - [ ] tests/mjs/XX-sprites.test.mjs passes - [ ] PROJECT-MEMORY.md updated with sprite rules - [ ] Network requests reduced from 44+ to 3 images - [ ] **CRITICAL**: doc/2-MODERN-WEB-TECHNIQUES.md updated with Section 12 (CSS Sprites) - [ ] `static/sprite-showcase.html` auto-generated with all icons visible and labeled