Dieser Teil behandelt alles rund um Rendering-Performance: Wie hältst du deine Webseite flüssig? Was triggert teure Reflows? Wie animierst du mit 60fps?
1. Browser Resize Performance
Das Problem: Ruckelnde Webseiten
Browser-Fenster resizen und die Seite ruckelt? 20fps statt 60fps? Das liegt meist am CSS.
Durch gezielte CSS-Optimierungen lassen sich 70-80% Performance-Gewinn erreichen.
Hauptübeltäter #1: Backdrop-Blur
/* ❌ Schlecht - GPU stirbt beim Resize */
.header {
backdrop-filter: blur(10px);
}
Problem:
- Browser muss Blur bei jeder Größenänderung neu berechnen
- Full Repaint des gesamten Inhalts darunter
- GPU-Auslastung: 80-90%
Lösung:
/* ✅ Gut - 60-80% weniger GPU-Last */
.header {
background: rgba(255, 255, 255, 0.95);
}
Hauptübeltäter #2: Universelle Selektoren
/* ❌ Schlecht - Browser rechnet ALLES neu */
@media (max-width: 768px) {
* {
max-width: 100vw !important;
overflow-x: hidden !important;
}
}
Bei 500+ Elementen = massive Style Recalculations.
Lösung:
/* ✅ Gut - Nur betroffene Elemente */
@media (max-width: 768px) {
.prose > *,
.prose img,
.prose pre,
.prose table {
max-width: 100%;
overflow-x: auto;
}
}
Einsparung: ~90% weniger Recalculations
Hauptübeltäter #3: Fehlende CSS Containment
/* ❌ Schlecht - Keine Isolation */
.container {
position: relative;
}
/* ✅ Gut - Isoliert Layout-Änderungen */
.container {
contain: layout style;
}
.card {
contain: layout paint;
}
Was macht contain?
layout- Verhindert Layout-Propagation nach außenpaint- Begrenzt Repaints auf das Elementstyle- Isoliert CSS-Counter etc.strict- Alle kombiniert
Einsparung: ~60% weniger Repaint-Zeit
Performance messen
// Chrome DevTools Console
performance.mark('resize-start');
window.addEventListener('resize', () => {
performance.mark('resize-end');
performance.measure('resize', 'resize-start', 'resize-end');
console.log(performance.getEntriesByName('resize'));
});
Gute Werte:
- Layout: < 5ms
- Paint: < 10ms
- Composite: < 2ms
2. Reflow & Repaint minimieren
Die Kosten-Hierarchie
Composite (günstig) → ~2ms
↓
Repaint (mittel) → ~10ms
↓
Reflow (teuer) → ~20ms+
Properties und ihre Trigger
Nur Composite (billig!):
/* ✅ Günstig - nur GPU-Compositing */
.element {
transform: translateX(100px);
opacity: 0.5;
}
Repaint (mittel):
/* ⚠️ Mittel - kein Layout, aber neu zeichnen */
.element {
color: red;
background: blue;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
Reflow (teuer!):
/* ❌ Teuer - kompletter Layout-Durchlauf */
.element {
width: 300px;
height: 200px;
margin: 20px;
top: 50px;
}
Anti-Pattern: Layout-Thrashing
// ❌ Horror - 200 Reflows!
elements.forEach(el => {
const height = el.offsetHeight; // LESEN → Reflow
el.style.height = height + 10 + 'px'; // SCHREIBEN
});
// ✅ Gut - 1 Reflow
const heights = elements.map(el => el.offsetHeight);
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px';
});
Nested Flex/Grid vermeiden
/* ❌ Teuer - mehrfache Layout-Berechnungen */
.container {
display: flex;
}
.item {
flex: 1;
display: flex;
}
.nested {
flex: 1;
display: grid;
}
/* ✅ Besser - flachere Hierarchie */
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
Faustregel: Max. 3 Ebenen Flex/Grid-Verschachtelung.
Die 100vh-Falle
/* ❌ Mobile Safari hasst das */
.fullscreen {
height: 100vh;
}
Problem: Address Bar ändert 100vh beim Scrollen → ständige Reflows.
Lösung:
:root {
--vh: 1vh;
}
.fullscreen {
height: calc(var(--vh, 1vh) * 100);
}
function setVH() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
setVH();
window.addEventListener('resize', setVH);
3. Animation Performance
Die goldene Regel
Animiere nur transform und opacity.
Browser Compositing verstehen
Browser arbeiten in Layers. Wenn du nur transform/opacity änderst, überspringt der Browser Layout und Paint.
/* ❌ Langsam - Layout + Paint + Composite */
@keyframes bad {
from { left: 0; }
to { left: 100px; }
}
/* ✅ Schnell - nur Composite */
@keyframes good {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
Messbar:
left: ~20ms pro Frame → 50fps maxtransform: ~2ms pro Frame → 60fps easy
will-change: Der GPU-Turbo
/* GPU-Layer erzwingen */
.animated {
will-change: transform;
}
Wichtig: Dynamisch setzen, dann wieder entfernen!
element.addEventListener('mouseenter', () => {
element.style.willChange = 'transform';
});
element.addEventListener('animationend', () => {
element.style.willChange = 'auto'; // Wichtig!
});
Warum? Jeder Layer kostet RAM. will-change auf 1000 Elementen = mehrere MB verschwendet.
Long-running Animations
/* ❌ Läuft ewig, frisst Ressourcen */
.spinner {
animation: spin 1s linear infinite;
}
/* ✅ Pause bei Invisibility */
.spinner {
animation: spin 1s linear infinite;
animation-play-state: running;
}
.spinner[hidden] {
animation-play-state: paused;
}
Mit Intersection Observer:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
entry.target.style.animationPlayState =
entry.isIntersecting ? 'running' : 'paused';
});
});
prefers-reduced-motion
/* Standard */
.element {
animation: slide 0.3s ease-out;
}
/* Reduced Motion */
@media (prefers-reduced-motion: reduce) {
.element {
animation: none;
transition: none;
}
}
Barrierefreiheit-Regel: Kritische Infos nie nur animiert zeigen.
4. Scroll-Performance
position: sticky smart einsetzen
/* ✅ GPU-beschleunigt */
.header {
position: sticky;
top: 0;
will-change: transform;
contain: layout paint;
}
Problem: Ohne contain muss Browser bei jedem Scroll Layout neu berechnen.
overflow: auto vs. clip
/* ❌ Langsamer - Scrollbar-Layout */
.container {
overflow-x: hidden;
}
/* ✅ Schneller - clippt ohne Scrollbar */
.container {
overflow-x: clip;
}
Virtuelle Listen (Konzept)
Für sehr lange Listen (1000+ Items):
Idee: Rendere nur sichtbare Items + Puffer.
// Pseudo-Code
const visibleRange = calculateVisibleRange(scrollTop, itemHeight);
const itemsToRender = allItems.slice(
visibleRange.start,
visibleRange.end
);
Libraries:
react-windowreact-virtualized@tanstack/virtual
Achtung: Nur bei wirklich langen Listen nötig (> 500 Items).
Passive Event Listeners
// ✅ Besseres Scrolling
element.addEventListener('scroll', handler, { passive: true });
passive: true sagt dem Browser: “Ich rufe nicht preventDefault() auf” → Browser kann sofort scrollen.
5. Bild & Media-Optimierung aus CSS-Sicht
object-fit
/* ❌ Verzerrt */
img {
width: 100%;
height: 300px;
}
/* ✅ Behält Seitenverhältnis */
img {
width: 100%;
height: 300px;
object-fit: cover;
object-position: center;
}
aspect-ratio
/* ✅ Verhindert Layout Shift */
.video-container {
aspect-ratio: 16 / 9;
}
.avatar {
aspect-ratio: 1;
object-fit: cover;
}
Vor aspect-ratio:
/* ❌ Padding-Hack */
.video-container {
padding-top: 56.25%; /* 16:9 */
position: relative;
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
image-set für Retina
.logo {
background-image: image-set(
url('logo.png') 1x,
url('[email protected]') 2x,
url('[email protected]') 3x
);
}
Responsive Images (CSS-Dimension)
<!-- Modernes srcset -->
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w
"
sizes="(max-width: 768px) 100vw, 800px"
alt="Beschreibung"
/>
CSS unterstützt mit:
img {
max-width: 100%;
height: auto;
display: block;
}
Loading-States
/* Skeleton während Laden */
.image-container {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.image-container img {
opacity: 0;
transition: opacity 0.3s;
}
.image-container img.loaded {
opacity: 1;
}
KI-Audit: Rendering-Performance prüfen
Kopiere diesen Prompt in Claude, ChatGPT oder deine bevorzugte KI:
Analysiere mein Projekt auf CSS Rendering-Performance Probleme:
1. **Backdrop-Filter**: Finde alle `backdrop-filter` Nutzungen. Schlage Alternativen mit rgba() vor.
2. **Universelle Selektoren**: Suche nach `* { ... }` Selektoren, besonders mit `!important`. Zeige spezifischere Alternativen.
3. **CSS Containment**: Prüfe große Container (.container, .card, article, section) - fehlt `contain: layout` oder `contain: layout paint`?
4. **Teure Animationen**: Finde Animationen die width, height, top, left, margin nutzen. Schlage `transform` Alternativen vor.
5. **will-change**: Wird `will-change` statisch gesetzt statt dynamisch? Zeige wie man es per JS toggled.
6. **100vh Mobile**: Suche nach `height: 100vh` ohne CSS Custom Property Fallback.
7. **overflow: hidden vs clip**: Finde `overflow-x: hidden` und schlage `overflow-x: clip` vor wo sinnvoll.
Gib mir eine priorisierte Liste mit Code-Beispielen und Performance-Impact (hoch/mittel/niedrig).
Links: