Files
cv-site/static/css/04-interactive/_sprites.css
T
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

176 lines
4.7 KiB
CSS

/* ============================================================================
CSS SPRITES - Image Request Optimization
============================================================================
Reduces HTTP requests from 44+ individual images to just 3 sprite sheets.
Each sprite uses CSS custom property --icon-index for positioning.
============================================================================ */
/* Base sprite class */
.icon-sprite {
display: inline-block;
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-size: auto 50px;
vertical-align: middle;
}
/* Company icons */
.icon-company {
background-image: url('/static/images/sprites/sprite-companies.png');
background-position-x: calc(var(--icon-index, 0) * -50px);
}
/* Project icons */
.icon-project {
background-image: url('/static/images/sprites/sprite-projects.png');
background-position-x: calc(var(--icon-index, 0) * -50px);
}
/* Course icons */
.icon-course {
background-image: url('/static/images/sprites/sprite-courses.png');
background-position-x: calc(var(--icon-index, 0) * -50px);
}
/* Retina displays - use @2x sprites for crisp rendering */
@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 50px; /* Display at 1x size */
}
.icon-project {
background-image: url('/static/images/sprites/sprite-projects@2x.png');
background-size: auto 50px;
}
.icon-course {
background-image: url('/static/images/sprites/sprite-courses@2x.png');
background-size: auto 50px;
}
}
/* Size variants for different contexts */
.icon-sprite.icon-small {
width: 32px;
height: 32px;
background-size: auto 32px;
}
.icon-sprite.icon-small.icon-company {
background-position-x: calc(var(--icon-index, 0) * -32px);
}
.icon-sprite.icon-small.icon-project {
background-position-x: calc(var(--icon-index, 0) * -32px);
}
.icon-sprite.icon-small.icon-course {
background-position-x: calc(var(--icon-index, 0) * -32px);
}
.icon-sprite.icon-large {
width: 64px;
height: 64px;
background-size: auto 64px;
}
.icon-sprite.icon-large.icon-company {
background-position-x: calc(var(--icon-index, 0) * -64px);
}
.icon-sprite.icon-large.icon-project {
background-position-x: calc(var(--icon-index, 0) * -64px);
}
.icon-sprite.icon-large.icon-course {
background-position-x: calc(var(--icon-index, 0) * -64px);
}
/* For section logos - match .company-logo img styling */
/* Use a wrapper approach: 80px box, 60px icon centered inside */
.icon-sprite.icon-section {
/* Outer box matches img styling */
width: 80px;
height: 80px;
border-radius: 4px;
border: 1px solid var(--icon-border, #ddd);
background-color: transparent;
box-sizing: border-box;
/* Inner content area for sprite */
padding: 10px;
background-size: auto 60px;
background-origin: content-box;
background-clip: content-box;
background-position: 0 0;
}
.icon-sprite.icon-section.icon-company {
background-position-x: calc(var(--icon-index, 0) * -60px);
}
.icon-sprite.icon-section.icon-project {
background-position-x: calc(var(--icon-index, 0) * -60px);
}
.icon-sprite.icon-section.icon-course {
background-position-x: calc(var(--icon-index, 0) * -60px);
}
/* Retina overrides for size variants */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.icon-sprite.icon-small {
background-size: auto 32px;
}
.icon-sprite.icon-small.icon-company {
background-position-x: calc(var(--icon-index, 0) * -32px);
}
.icon-sprite.icon-small.icon-project {
background-position-x: calc(var(--icon-index, 0) * -32px);
}
.icon-sprite.icon-small.icon-course {
background-position-x: calc(var(--icon-index, 0) * -32px);
}
.icon-sprite.icon-large {
background-size: auto 64px;
}
.icon-sprite.icon-large.icon-company {
background-position-x: calc(var(--icon-index, 0) * -64px);
}
.icon-sprite.icon-large.icon-project {
background-position-x: calc(var(--icon-index, 0) * -64px);
}
.icon-sprite.icon-large.icon-course {
background-position-x: calc(var(--icon-index, 0) * -64px);
}
.icon-sprite.icon-section {
padding: 10px;
background-size: auto 60px;
background-position: 0 0;
}
.icon-sprite.icon-section.icon-company {
background-size: auto 60px;
background-position-x: calc(var(--icon-index, 0) * -60px);
}
.icon-sprite.icon-section.icon-project {
background-size: auto 60px;
background-position-x: calc(var(--icon-index, 0) * -60px);
}
.icon-sprite.icon-section.icon-course {
background-size: auto 60px;
background-position-x: calc(var(--icon-index, 0) * -60px);
}
}