“Das fühlt sich langsam an” ist keine Metrik. Messen, nicht raten.
1. CSS-Profiling mit DevTools
Chrome DevTools Performance:
F12 → Performance Tab → Record → Interaktion → Stop
Was du suchst:
- Recalculate Style < 10ms
- Layout < 5ms
- Paint < 10ms
- Composite < 2ms
Rote Flags:
- Lange gelbe Balken (Style Recalc)
- Violette Balken (Layout/Reflow)
- “Forced reflow” Warnungen
Coverage-Check:
DevTools → Coverage → Reload
Ziel: > 70% genutztes CSS (grün). Alles andere (rot) mit PurgeCSS eliminieren.
Rendering-Debugging:
DevTools → Rendering → Paint flashing
Viele grüne Flashes beim Scrollen? Du hast ein Repaint-Problem.
Performance Tab:
- Bessere Waterfall-Ansicht
- Genauere Timing-Daten
Inspector → Animations:
- Zeigt alle laufenden Animationen
- Timeline zum Scrubben
- Performance-Warnungen
2. Linting & Stylelint für Performance
Stylelint Setup
npm install --save-dev stylelint stylelint-config-standard
// .stylelintrc.js
module.exports = {
extends: 'stylelint-config-standard',
rules: {
// Performance-Regeln
'no-descending-specificity': true,
'no-duplicate-selectors': true,
'selector-max-id': 0, // Keine IDs
'selector-max-universal': 1, // Max 1x *
'max-nesting-depth': 3,
// Accessibility
'color-contrast': true,
'font-weight-notation': 'numeric',
// Konsistenz
'color-hex-length': 'short',
'color-named': 'never',
'length-zero-no-unit': true,
}
};
Performance-Regeln
Verbiete teure Selektoren:
rules: {
// ❌ Verhindert
'selector-max-universal': 0, // Kein *
'selector-max-type': 2, // Max 2 Type-Selektoren
'selector-max-combinators': 3, // Max 3 Kombinatoren
}
Verbiete teure Properties:
rules: {
'declaration-property-value-disallowed-list': {
// Warnung bei teuren Filtern
'filter': ['/blur/'],
'backdrop-filter': ['/.*/'],
}
}
Stylelint Plugins
stylelint-high-performance-animation:
npm install --save-dev stylelint-high-performance-animation
{
plugins: ['stylelint-high-performance-animation'],
rules: {
'plugin/no-low-performance-animation-properties': [
true,
{
// Nur transform und opacity erlauben
ignoreProperties: ['transform', 'opacity']
}
]
}
}
stylelint-a11y:
npm install --save-dev stylelint-a11y
{
plugins: ['stylelint-a11y'],
rules: {
'a11y/content-property-no-static-value': true,
'a11y/font-size-is-readable': true,
'a11y/line-height-is-vertical-rhythmed': true,
'a11y/media-prefers-reduced-motion': true,
'a11y/no-outline-none': true,
}
}
Pre-commit Hooks
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.css": "stylelint --fix"
}
}
3. Performance-Metriken
Core Web Vitals für CSS
LCP (Largest Contentful Paint):
- Ziel: < 2.5s
- CSS-Impact: Render-blocking CSS
<!-- ❌ Blockiert Rendering -->
<link rel="stylesheet" href="styles.css">
<!-- ✅ Kritisches CSS inline -->
<style>
/* Critical CSS */
body { margin: 0; font-family: sans-serif; }
.hero { height: 100vh; }
</style>
<!-- Nicht-kritisches verzögert laden -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
CLS (Cumulative Layout Shift):
- Ziel: < 0.1
- CSS-Impact: Keine Größenangaben
/* ✅ Verhindert CLS */
img {
aspect-ratio: 16 / 9;
}
.skeleton {
min-height: 200px;
}
FID (First Input Delay):
- Ziel: < 100ms
- CSS-Impact: Schwere Selektoren
CSS-Size Budget
// budget.json
{
"budgets": [{
"resourceSizes": [{
"resourceType": "stylesheet",
"budget": 50 // 50KB
}]
}]
}
Webpack:
// webpack.config.js
module.exports = {
performance: {
maxAssetSize: 50000, // 50KB
maxEntrypointSize: 50000,
hints: 'error'
}
}
Lighthouse CI
# .lighthouserc.yml
ci:
collect:
numberOfRuns: 3
assert:
assertions:
'first-contentful-paint': ['error', {maxNumericValue: 2000}]
'cumulative-layout-shift': ['error', {maxNumericValue: 0.1}]
'total-blocking-time': ['error', {maxNumericValue: 300}]
npm install -g @lhci/cli
lhci autorun
Bundle Analyzer
# Webpack Bundle Analyzer
npm install --save-dev webpack-bundle-analyzer
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
Für CSS:
- Zeigt CSS-Bundle-Größen
- Identifiziert große Dependencies
- Zeigt Duplikate
4. Automatisierte Performance-Tests
Jest + CSS Testing
// css-performance.test.js
describe('CSS Performance', () => {
test('No universal selectors with !important', () => {
const css = fs.readFileSync('styles.css', 'utf-8');
expect(css).not.toMatch(/\*[^{]*!important/);
});
test('Max 3 levels of nesting', () => {
// PostCSS parsing
const root = postcss.parse(css);
root.walkRules(rule => {
const depth = getDepth(rule);
expect(depth).toBeLessThanOrEqual(3);
});
});
});
Visual Regression Testing
// backstop.config.js
module.exports = {
scenarios: [{
label: "Homepage",
url: "http://localhost:3000",
delay: 500,
misMatchThreshold: 0.1
}],
onBeforeScript: "puppet/onBefore.js",
onReadyScript: "puppet/onReady.js"
};
CSS-Änderungen testen:
- Baseline erstellen
- CSS ändern
- Vergleich → Visual Diff
Automated Accessibility Testing
// a11y.test.js
import { axe } from 'jest-axe';
test('should not have a11y violations', async () => {
const { container } = render(<App />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Performance-Budget Beispiel
{
"css": {
"critical": "< 14KB (inline)",
"total": "< 50KB",
"unused": "< 10%"
},
"metrics": {
"lcp": "< 2.5s",
"cls": "< 0.1",
"fid": "< 100ms"
},
"rendering": {
"styleRecalc": "< 10ms",
"layout": "< 5ms",
"paint": "< 10ms"
}
}
- Zugängliches CSS (WCAG AA+)
- Messbares CSS (Metriken!)
Links: