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
This commit is contained in:
juanatsap
2025-12-04 11:38:36 +00:00
parent 7727405c25
commit b5a50ca3ef
25 changed files with 2194 additions and 76 deletions
+5 -5
View File
@@ -13,15 +13,15 @@
</summary>
{{range .CV.Courses}}
<div class="course-item" id="course-{{.CourseID}}" data-course="{{.CourseID}}" data-title="{{.Title}}" data-institution="{{.Institution}}">
{{if .CourseLogo}}
<div class="course-icon">
{{if .LogoIndex}}
<span class="icon-sprite icon-section icon-course" style="--icon-index: {{.LogoIndex}};" role="img" aria-label="{{.Title}} logo"></span>
{{else if .CourseLogo}}
<img src="/static/images/courses/{{.CourseLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:school\' width=\'80\' height=\'80\' class=\'default-course-icon\'></iconify-icon>'">
</div>
{{else}}
<div class="course-icon">
{{else}}
<iconify-icon icon="mdi:school" width="80" height="80" class="default-course-icon"></iconify-icon>
{{end}}
</div>
{{end}}
<div class="course-content">
<strong>{{.Title}}</strong><br>
<small>{{.Institution}} - {{.Date}} - ({{.Location}})</small>
+3 -1
View File
@@ -14,7 +14,9 @@
{{range .CV.Experience}}
<div class="experience-item" id="exp-{{.CompanyID}}" data-company="{{.CompanyID}}" data-title="{{.Company}}" data-position="{{.Position}}">
<div class="company-logo">
{{if .CompanyLogo}}
{{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" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:office-building\' width=\'80\' height=\'80\' class=\'default-company-icon\'></iconify-icon>'">
{{else}}
<iconify-icon icon="mdi:office-building" width="80" height="80" class="default-company-icon"></iconify-icon>
+5 -5
View File
@@ -13,15 +13,15 @@
</summary>
{{range .CV.Projects}}
<div class="project-item" id="proj-{{.ProjectID}}" data-project="{{.ProjectID}}" data-title="{{if .ProjectName}}{{.ProjectName}}{{else}}{{.Title}}{{end}}">
{{if .ProjectLogo}}
<div class="project-icon">
{{if .LogoIndex}}
<span class="icon-sprite icon-section icon-project" style="--icon-index: {{.LogoIndex}};" role="img" aria-label="{{.Title}} logo"></span>
{{else if .ProjectLogo}}
<img src="/static/images/projects/{{.ProjectLogo}}" alt="{{.Title}} logo" onerror="this.parentElement.innerHTML='<iconify-icon icon=\'mdi:web\' width=\'80\' height=\'80\' class=\'default-project-icon\'></iconify-icon>'">
</div>
{{else}}
<div class="project-icon">
{{else}}
<iconify-icon icon="mdi:web" width="80" height="80" class="default-project-icon"></iconify-icon>
{{end}}
</div>
{{end}}
<div class="project-content">
<strong>
{{if .ProjectName}}