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
176 lines
4.7 KiB
CSS
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);
|
|
}
|
|
}
|