Master advanced CSS performance optimization techniques including critical path CSS, code splitting, lazy loading, efficient selectors, and modern performance monitoring.
Master advanced CSS performance optimization techniques including critical path CSS, code splitting, lazy loading, efficient selectors, and modern performance monitoring.
CSS performance optimization is crucial for modern web applications as
stylesheets directly impact the critical rendering path. Understanding
how browsers parse, load, and apply CSS enables us to implement
strategies that dramatically reduce First Contentful Paint (FCP) and
Largest Contentful Paint (LCP) times.
Parser Blocking:
CSS blocks DOM construction until parsed
Render Blocking:
Stylesheets prevent initial page render
Layout Thrashing:
Inefficient selectors cause repeated calculations
Bundle Size:
Large CSS files delay initial page load
Cache Efficiency:
Poor caching strategies reduce performance
Optimization Focus
First Contentful Paint
Initial render
Critical CSS inline
Largest Contentful Paint
Main content
Above-fold optimization
Cumulative Layout Shift
Visual stability
Dimension specification
Time to Interactive
User interaction
Non-critical lazy loading
Critical CSS extraction involves identifying and inlining the minimum
CSS required for above-the-fold content, allowing the page to render
immediately while non-critical styles load asynchronously.
Learn about Critical CSS Implementation Strategy.
1// Advanced critical CSS extraction with Puppeteer
2import puppeteer from 'puppeteer';
3import { CleanCSS } from 'clean-css';
4import postcss from 'postcss';
5import cssnano from 'cssnano';
6
7interface CriticalCSSConfig {
8 url: string;
9 viewport: { width: number; height: number };
10 stylesheets: string[];
11 ignore?: string[];
12 minify?: boolean;
13}
14
15class CriticalCSSExtractor {
16 private browser?: puppeteer.Browser;
17 private cleanCSS = new CleanCSS({
18 level: 2,
19 returnPromise: true
20 });
21
22 async initialize(): Promise<void> {
23 this.browser = await puppeteer.launch({
24 headless: 'new',
25 args: ['--no-sandbox', '--disable-setuid-sandbox']
26 });
27 }
28
29 async extractCritical(config: CriticalCSSConfig): Promise<string> {
30 if (!this.browser) await this.initialize();
31
32 const page = await this.browser!.newPage();
33 await page.setViewport(config.viewport);
34
35 // Inject CSS coverage collection
36 await page.coverage.startCSSCoverage();
37 await page.goto(config.url, { waitUntil: 'networkidle0' });
38
39 // Get CSS coverage data
40 const cssCoverage = await page.coverage.stopCSSCoverage();
41
42 let criticalCSS = '';
43
44 for (const entry of cssCoverage) {
45 if (config.stylesheets.some(sheet => entry.url.includes(sheet))) {
46 // Extract used CSS ranges
47 for (const range of entry.ranges) {
48 const usedCSS = entry.text.substring(range.start, range.end);
49 criticalCSS += usedCSS;
50 }
51 }
52 }
53
54 await page.close();
55
56 // Process and optimize critical CSS
57 return await this.optimizeCriticalCSS(criticalCSS, config);
58 }
59
60 private async optimizeCriticalCSS(
61 css: string,
62 config: CriticalCSSConfig
63 ): Promise<string> {
64 // Remove ignored selectors
65 let processedCSS = css;
66
67 if (config.ignore) {
68 config.ignore.forEach(selector => {
69 const regex = new RegExp(\`\\\${selector}[^}]*}\`, 'g');
70 processedCSS = processedCSS.replace(regex, '');
71 });
72 }
73
74 // PostCSS optimization pipeline
75 const result = await postcss([
76 cssnano({
77 preset: ['advanced', {
78 reduceIdents: false,
79 zindex: false,
80 discardComments: { removeAll: true }
81 }]
82 })
83 ]).process(processedCSS, { from: undefined });
84
85 return config.minify ?
86 (await this.cleanCSS.minify(result.css)).styles :
87 result.css;
88 }
89
90 async cleanup(): Promise<void> {
91 if (this.browser) {
92 await this.browser.close();
93 }
94 }
95}
96
97// Usage in build process
98export async function generateCriticalCSS(
99 pages: string[],
100 outputPath: string
101): Promise<void> {
102 const extractor = new CriticalCSSExtractor();
103
104 try {
105 for (const page of pages) {
106 const criticalCSS = await extractor.extractCritical({
107 url: \`http://localhost:3000\\\${page}\`,
108 viewport: { width: 1200, height: 800 },
109 stylesheets: ['main.css', 'components.css'],
110 ignore: ['.hidden', '.mobile-only'],
111 minify: true
112 });
113
114 // Write critical CSS for each page
115 const outputFile = \`\\\${outputPath}/critical-\\\${page.replace('/', '-')}.css\`;
116 await fs.writeFile(outputFile, criticalCSS);
117 }
118 } finally {
119 await extractor.cleanup();
120 }
121}Learn about Critical CSS Injection Strategy.
1// React component for critical CSS injection
2import { useEffect, useState } from 'react';
3import { useRouter } from 'next/router';
4
5interface CriticalCSSProps {
6 criticalCSS: string;
7 nonCriticalCSS: string[];
8 preloadCSS?: string[];
9}
10
11export const CriticalCSSManager: React.FC<CriticalCSSProps> = ({
12 criticalCSS,
13 nonCriticalCSS,
14 preloadCSS = []
15}) => {
16 const [isLoaded, setIsLoaded] = useState(false);
17 const router = useRouter();
18
19 useEffect(() => {
20 // Load non-critical CSS after critical render
21 const loadNonCriticalCSS = () => {
22 nonCriticalCSS.forEach((href, index) => {
23 const link = document.createElement('link');
24 link.rel = 'stylesheet';
25 link.href = href;
26 link.media = 'print'; // Load asynchronously
27 link.onload = () => {
28 link.media = 'all'; // Apply after load
29 };
30
31 // Add loading priority
32 if (index === 0) {
33 link.setAttribute('data-priority', 'high');
34 }
35
36 document.head.appendChild(link);
37 });
38
39 setIsLoaded(true);
40 };
41
42 // Use requestIdleCallback for better performance
43 if ('requestIdleCallback' in window) {
44 requestIdleCallback(loadNonCriticalCSS, { timeout: 2000 });
45 } else {
46 setTimeout(loadNonCriticalCSS, 100);
47 }
48 }, [nonCriticalCSS]);
49
50 useEffect(() => {
51 // Preload CSS for likely next pages
52 preloadCSS.forEach(href => {
53 const link = document.createElement('link');
54 link.rel = 'preload';
55 link.as = 'style';
56 link.href = href;
57 document.head.appendChild(link);
58 });
59 }, [preloadCSS]);
60
61 return (
62 <>
63 {/* Inline critical CSS */}
64 <style
65 dangerouslySetInnerHTML={{ __html: criticalCSS }}
66 data-critical="true"
67 />
68
69 {/* Preload hints for non-critical resources */}
70 {nonCriticalCSS.map((href, index) => (
71 <link
72 key={href}
73 rel="preload"
74 as="style"
75 href={href}
76 media="print"
77 onLoad="this.media='all'"
78 />
79 ))}
80 </>
81 );
82};
83
84// Next.js integration with critical CSS
85export function withCriticalCSS(WrappedComponent: React.ComponentType) {
86 return function CriticalCSSWrapper(props: any) {
87 const criticalCSS = props.criticalCSS || '';
88 const nonCriticalCSS = props.nonCriticalCSS || [];
89
90 return (
91 <>
92 <CriticalCSSManager
93 criticalCSS={criticalCSS}
94 nonCriticalCSS={nonCriticalCSS}
95 />
96 <WrappedComponent {...props} />
97 </>
98 );
99 };
100}Code splitting CSS enables loading only the styles needed for specific
routes or components, reducing initial bundle sizes and improving
loading performance through granular resource management.
Learn about Advanced CSS Code Splitting.
1// Webpack-based CSS code splitting configuration
2import MiniCssExtractPlugin from 'mini-css-extract-plugin';
3import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
4import { Configuration } from 'webpack';
5
6interface CSSCodeSplittingConfig {
7 chunks: 'all' | 'async' | 'initial';
8 minSize: number;
9 maxSize: number;
10 cacheGroups: Record<string, any>;
11}
12
13export const createCSSCodeSplittingConfig = (): Configuration => {
14 return {
15 optimization: {
16 splitChunks: {
17 cacheGroups: {
18 // Critical CSS bundle
19 critical: {
20 name: 'critical',
21 test: /critical\\.css$/,
22 chunks: 'all',
23 priority: 30,
24 enforce: true
25 },
26
27 // Component-specific CSS
28 components: {
29 name: 'components',
30 test: /components.*\\.css$/,
31 chunks: 'all',
32 priority: 20,
33 minChunks: 2
34 },
35
36 // Vendor CSS (third-party libraries)
37 vendor: {
38 name: 'vendor',
39 test: /node_modules.*\\.css$/,
40 chunks: 'all',
41 priority: 10
42 },
43
44 // Page-specific CSS
45 pages: {
46 name(module: any) {
47 const modulePath = module.context || module.identifier();
48 const pageName = modulePath.match(/pages\\\\/(.+?)\\\\//)?.[1] || 'default';
49 return \`page-\\\${pageName}\`;
50 },
51 test: /pages.*\\.css$/,
52 chunks: 'async',
53 priority: 15
54 }
55 }
56 }
57 },
58
59 plugins: [
60 new MiniCssExtractPlugin({
61 filename: ({ chunk }) => {
62 // Generate cache-friendly filenames
63 const isProductionMode = process.env.NODE_ENV === 'production';
64 const hash = isProductionMode ? '[contenthash:8]' : '[name]';
65
66 if (chunk?.name?.includes('critical')) {
67 return \`css/critical.\\\${hash}.css\`;
68 }
69
70 return \`css/[name].\\\${hash}.css\`;
71 },
72 chunkFilename: 'css/[name].[contenthash:8].css',
73 ignoreOrder: false
74 }),
75
76 new OptimizeCSSAssetsPlugin({
77 assetNameRegExp: /\\.css$/g,
78 cssProcessor: require('cssnano'),
79 cssProcessorPluginOptions: {
80 preset: ['default', {
81 discardComments: { removeAll: true },
82 normalizeUnicode: false
83 }]
84 },
85 canPrint: true
86 })
87 ]
88 };
89};
90
91// Dynamic CSS loading utility
92class DynamicCSSLoader {
93 private loadedStyles = new Set<string>();
94 private loadingPromises = new Map<string, Promise<void>>();
95
96 async loadCSS(href: string, priority: 'high' | 'normal' | 'low' = 'normal'): Promise<void> {
97 if (this.loadedStyles.has(href)) {
98 return Promise.resolve();
99 }
100
101 if (this.loadingPromises.has(href)) {
102 return this.loadingPromises.get(href)!;
103 }
104
105 const loadPromise = this.createLoadPromise(href, priority);
106 this.loadingPromises.set(href, loadPromise);
107
108 try {
109 await loadPromise;
110 this.loadedStyles.add(href);
111 } finally {
112 this.loadingPromises.delete(href);
113 }
114
115 return loadPromise;
116 }
117
118 private createLoadPromise(href: string, priority: string): Promise<void> {
119 return new Promise((resolve, reject) => {
120 const link = document.createElement('link');
121 link.rel = 'stylesheet';
122 link.href = href;
123
124 // Set loading priority
125 if (priority === 'high') {
126 link.setAttribute('fetchpriority', 'high');
127 } else if (priority === 'low') {
128 link.setAttribute('fetchpriority', 'low');
129 }
130
131 link.onload = () => resolve();
132 link.onerror = () => reject(new Error(\`Failed to load CSS: \\\${href}\`));
133
134 // Preload for faster loading
135 const preloadLink = document.createElement('link');
136 preloadLink.rel = 'preload';
137 preloadLink.as = 'style';
138 preloadLink.href = href;
139 document.head.appendChild(preloadLink);
140
141 // Add actual stylesheet
142 document.head.appendChild(link);
143 });
144 }
145
146 async preloadCSS(hrefs: string[]): Promise<void> {
147 const preloadPromises = hrefs.map(href => {
148 return new Promise<void>((resolve) => {
149 const link = document.createElement('link');
150 link.rel = 'preload';
151 link.as = 'style';
152 link.href = href;
153 link.onload = () => resolve();
154 link.onerror = () => resolve(); // Don't fail on preload errors
155 document.head.appendChild(link);
156 });
157 });
158
159 await Promise.allSettled(preloadPromises);
160 }
161}
162
163// React hook for dynamic CSS loading
164export const useDynamicCSS = () => {
165 const [loader] = useState(() => new DynamicCSSLoader());
166
167 const loadCSS = useCallback(async (
168 href: string,
169 priority?: 'high' | 'normal' | 'low'
170 ) => {
171 try {
172 await loader.loadCSS(href, priority);
173 } catch (error) {
174 console.error('Failed to load CSS:', error);
175 }
176 }, [loader]);
177
178 const preloadCSS = useCallback(async (hrefs: string[]) => {
179 await loader.preloadCSS(hrefs);
180 }, [loader]);
181
182 return { loadCSS, preloadCSS };
183};CSS selector performance directly impacts style calculation time.
Understanding selector efficiency and implementing optimal CSS
architecture patterns prevents layout thrashing and reduces
computational overhead.
Learn about Selector Performance Optimization.
1/* INEFFICIENT SELECTORS - AVOID */
2
3/* Universal selector with descendant combinator - expensive */
4* div p span { color: red; }
5
6/* Complex descendant chains - triggers expensive tree walking */
7.container .sidebar .menu .item .link .icon { font-size: 16px; }
8
9/* Attribute selectors with complex matching - slow */
10input[type*="text"][class~="form-control"][data-validation*="required"] {
11 border: 1px solid #ccc;
12}
13
14/* Pseudo-selectors with complex conditions - computationally expensive */
15.item:nth-child(3n+1):not(.hidden):hover:focus-within {
16 transform: scale(1.05);
17}
18
19/* EFFICIENT SELECTORS - RECOMMENDED */
20
21/* Direct class selectors - fastest */
22.button-primary {
23 background: #007bff;
24 color: white;
25}
26
27/* Single-level descendant with specific classes */
28.card-header .title {
29 font-size: 1.5rem;
30 font-weight: 600;
31}
32
33/* Efficient attribute selectors with exact matching */
34[data-theme="dark"] { background: #1a1a1a; }
35[aria-expanded="true"] { display: block; }
36
37/* Optimized pseudo-selectors */
38.nav-item:hover { background: rgba(0,0,0,0.05); }
39.form-input:focus { outline: 2px solid #007bff; }
40
41/* CSS Custom Properties for dynamic values - better than inline styles */
42.component {
43 background: var(--component-bg, #ffffff);
44 color: var(--component-color, #333333);
45 padding: var(--component-padding, 1rem);
46}
47
48/* Container queries for efficient responsive design */
49@container (min-width: 400px) {
50 .card {
51 display: grid;
52 grid-template-columns: 1fr 2fr;
53 }
54}
55
56/* Efficient animations using transform and opacity */
57.fade-in {
58 opacity: 0;
59 transform: translateY(20px);
60 transition: opacity 0.3s ease, transform 0.3s ease;
61}
62
63.fade-in.visible {
64 opacity: 1;
65 transform: translateY(0);
66}
67
68/* Layer-optimized animations */
69.gpu-accelerated {
70 will-change: transform, opacity;
71 transform: translateZ(0); /* Force GPU layer */
72}
73
74/* Efficient grid systems using CSS Grid */
75.layout-grid {
76 display: grid;
77 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
78 gap: 1rem;
79 contain: layout style paint;
80}
81
82/* Performance-optimized utility classes */
83.visually-hidden {
84 position: absolute !important;
85 width: 1px !important;
86 height: 1px !important;
87 padding: 0 !important;
88 margin: -1px !important;
89 overflow: hidden !important;
90 clip: rect(0, 0, 0, 0) !important;
91 white-space: nowrap !important;
92 border: 0 !important;
93}Learn about CSS Architecture Performance Patterns.
1// CSS Performance Analyzer
2class CSSPerformanceAnalyzer {
3 private performanceObserver?: PerformanceObserver;
4 private metrics = {
5 styleRecalculations: 0,
6 layoutThrashing: 0,
7 paintEvents: 0,
8 renderingTime: 0
9 };
10
11 startMonitoring(): void {
12 // Monitor paint and layout events
13 this.performanceObserver = new PerformanceObserver((list) => {
14 for (const entry of list.getEntries()) {
15 switch (entry.entryType) {
16 case 'paint':
17 this.metrics.paintEvents++;
18 break;
19 case 'layout-shift':
20 this.metrics.layoutThrashing += (entry as any).value;
21 break;
22 case 'longtask':
23 if (entry.duration > 50) {
24 this.metrics.renderingTime += entry.duration;
25 }
26 break;
27 }
28 }
29 });
30
31 this.performanceObserver.observe({
32 entryTypes: ['paint', 'layout-shift', 'longtask']
33 });
34
35 // Monitor CSS recalculation frequency
36 this.monitorStyleRecalculations();
37 }
38
39 private monitorStyleRecalculations(): void {
40 const observer = new MutationObserver((mutations) => {
41 let hasStyleChanges = false;
42
43 mutations.forEach((mutation) => {
44 if (mutation.type === 'attributes' &&
45 (mutation.attributeName === 'class' ||
46 mutation.attributeName === 'style')) {
47 hasStyleChanges = true;
48 }
49 });
50
51 if (hasStyleChanges) {
52 this.metrics.styleRecalculations++;
53 }
54 });
55
56 observer.observe(document.body, {
57 attributes: true,
58 childList: true,
59 subtree: true,
60 attributeFilter: ['class', 'style']
61 });
62 }
63
64 getMetrics() {
65 return { ...this.metrics };
66 }
67
68 generateReport(): string {
69 return \`
70CSS Performance Report:
71- Style Recalculations: \${this.metrics.styleRecalculations}
72- Layout Thrashing Score: \${this.metrics.layoutThrashing.toFixed(3)}
73- Paint Events: \${this.metrics.paintEvents}
74- Total Rendering Time: \${this.metrics.renderingTime.toFixed(2)}ms
75\`;
76 }
77
78 cleanup(): void {
79 if (this.performanceObserver) {
80 this.performanceObserver.disconnect();
81 }
82 }
83}
84
85// CSS Architecture Validator
86interface SelectorAnalysis {
87 selector: string;
88 specificity: number;
89 performance: 'excellent' | 'good' | 'poor' | 'critical';
90 suggestions: string[];
91}
92
93class CSSArchitectureValidator {
94 analyzeSelectorPerformance(css: string): SelectorAnalysis[] {
95 const selectorRegex = /([^{]+){[^}]*}/g;
96 const results: SelectorAnalysis[] = [];
97 let match;
98
99 while ((match = selectorRegex.exec(css)) !== null) {
100 const selector = match[1].trim();
101 const analysis = this.analyzeSelector(selector);
102 results.push(analysis);
103 }
104
105 return results.sort((a, b) => {
106 const performanceOrder = { critical: 4, poor: 3, good: 2, excellent: 1 };
107 return performanceOrder[b.performance] - performanceOrder[a.performance];
108 });
109 }
110
111 private analyzeSelector(selector: string): SelectorAnalysis {
112 const suggestions: string[] = [];
113 let performanceScore = 100;
114
115 // Check for universal selector
116 if (selector.includes('*')) {
117 performanceScore -= 40;
118 suggestions.push('Avoid universal selector (*) - use specific classes instead');
119 }
120
121 // Check for complex descendant chains
122 const descendantCount = (selector.match(/\s+/g) || []).length;
123 if (descendantCount > 3) {
124 performanceScore -= descendantCount * 10;
125 suggestions.push('Reduce descendant chain length - use direct class selectors');
126 }
127
128 // Check for complex attribute selectors
129 const complexAttributeSelectors = selector.match(/\[[^=]*[*^$|~][^=]*=/g);
130 if (complexAttributeSelectors) {
131 performanceScore -= complexAttributeSelectors.length * 15;
132 suggestions.push('Use exact attribute matching instead of complex patterns');
133 }
134
135 // Check for :nth-child with complex expressions
136 if (selector.includes(':nth-child') && selector.match(/:nth-child\([^)]+[+\-]/)) {
137 performanceScore -= 25;
138 suggestions.push('Complex nth-child expressions are expensive - consider alternatives');
139 }
140
141 // Calculate specificity
142 const specificity = this.calculateSpecificity(selector);
143
144 // Determine performance rating
145 let performance: SelectorAnalysis['performance'];
146 if (performanceScore >= 90) performance = 'excellent';
147 else if (performanceScore >= 70) performance = 'good';
148 else if (performanceScore >= 40) performance = 'poor';
149 else performance = 'critical';
150
151 return {
152 selector,
153 specificity,
154 performance,
155 suggestions
156 };
157 }
158
159 private calculateSpecificity(selector: string): number {
160 // Simplified specificity calculation
161 const idCount = (selector.match(/#/g) || []).length * 100;
162 const classCount = (selector.match(/\./g) || []).length * 10;
163 const elementCount = (selector.match(/[a-z]/g) || []).length;
164
165 return idCount + classCount + elementCount;
166 }
167
168 generateOptimizationSuggestions(analyses: SelectorAnalysis[]): string {
169 const criticalIssues = analyses.filter(a => a.performance === 'critical');
170 const poorPerformance = analyses.filter(a => a.performance === 'poor');
171
172 let report = 'CSS Architecture Optimization Report:\\\\n\\\\n';
173
174 if (criticalIssues.length > 0) {
175 report += 'CRITICAL PERFORMANCE ISSUES:\\\\n';
176 criticalIssues.forEach(issue => {
177 report += \`- \\\${issue.selector}\\\\n\`;
178 issue.suggestions.forEach(suggestion => {
179 report += \` • \\\${suggestion}\\\\n\`;
180 });
181 });
182 }
183
184 if (poorPerformance.length > 0) {
185 report += '\\\\nPERFORMANCE IMPROVEMENTS NEEDED:\\\\n';
186 poorPerformance.forEach(issue => {
187 report += \`- \\\${issue.selector}\\\\n\`;
188 issue.suggestions.forEach(suggestion => {
189 report += \` • \\\${suggestion}\\\\n\`;
190 });
191 });
192 }
193
194 return report;
195 }
196}
197
198// Usage example
199export const initializeCSSPerformanceMonitoring = () => {
200 const analyzer = new CSSPerformanceAnalyzer();
201 const validator = new CSSArchitectureValidator();
202
203 // Start performance monitoring
204 analyzer.startMonitoring();
205
206 // Analyze existing CSS
207 fetch('/css/main.css')
208 .then(response => response.text())
209 .then(css => {
210 const analysis = validator.analyzeSelectorPerformance(css);
211 const report = validator.generateOptimizationSuggestions(analysis);
212 console.log(report);
213 });
214
215 // Return cleanup function
216 return () => analyzer.cleanup();
217};Modern CSS optimization goes beyond basic minification to include
advanced techniques like unused CSS elimination, CSS tree shaking,
and intelligent bundling strategies that maximize compression and
minimize network overhead.
Learn about Advanced CSS Build Optimization.
1// Advanced CSS optimization pipeline
2import postcss from 'postcss';
3import cssnano from 'cssnano';
4import purgecss from '@fullhuman/postcss-purgecss';
5import autoprefixer from 'autoprefixer';
6import { brotliCompressSync, gzipSync } from 'zlib';
7import { promises as fs } from 'fs';
8
9interface OptimizationConfig {
10 input: string[];
11 output: string;
12 purgeContent: string[];
13 compressionTypes: ('gzip' | 'brotli')[];
14 extractUnusedSelectors: boolean;
15 generateSourceMaps: boolean;
16}
17
18class AdvancedCSSOptimizer {
19 private postcssProcessor: postcss.Processor;
20
21 constructor() {
22 this.postcssProcessor = postcss([
23 // Vendor prefixes
24 autoprefixer({
25 overrideBrowserslist: ['last 2 versions', '> 1%', 'not dead']
26 }),
27
28 // Remove unused CSS
29 purgecss({
30 content: [],
31 extractors: [
32 {
33 extractor: (content: string) => {
34 // Advanced extraction for component classes
35 const componentClasses = content.match(/className[s]?\\\\s*[:=]\\\\s*["'\\\`]([^"'\\\`]*)/g) || [];
36 const regularClasses = content.match(/class\\\\s*=\\\\s*["'\\\`]([^"'\\\`]*)/g) || [];
37 const dynamicClasses = content.match(/['"\\\\x60]([a-zA-Z0-9\\\\-_]+(?:\\\\.[a-zA-Z0-9\\\\-_]+)*)['"\\\\x60]/g) || [];
38
39 return [
40 ...componentClasses.map(match => match.split(/["'\\\\x60\\\\s]/)[1]),
41 ...regularClasses.map(match => match.split(/["'\\\\x60\\\\s]/)[1]),
42 ...dynamicClasses.map(match => match.replace(/['"\\\\x60]/g, ''))
43 ].filter(Boolean);
44 },
45 extensions: ['html', 'js', 'jsx', 'ts', 'tsx', 'vue']
46 }
47 ],
48 whitelist: [
49 // Keep utility classes
50 /^(flex|grid|m[trblxy]?|p[trblxy]?)-/,
51 // Keep state classes
52 /^(hover|focus|active|disabled):/,
53 // Keep responsive classes
54 /^(sm|md|lg|xl):/
55 ],
56 whitelistPatterns: [
57 // Dynamic classes patterns
58 /^text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl)$/,
59 /^bg-(red|blue|green|yellow|purple|pink|gray)-(100|200|300|400|500|600|700|800|900)$/
60 ]
61 }),
62
63 // Advanced optimization
64 cssnano({
65 preset: ['advanced', {
66 autoprefixer: false,
67 cssDeclarationSorter: true,
68 discardComments: { removeAll: true },
69 discardDuplicates: true,
70 discardEmpty: true,
71 discardOverridden: true,
72 discardUnused: true,
73 mergeIdents: true,
74 mergeLonghand: true,
75 mergeRules: true,
76 minifyFontValues: true,
77 minifyGradients: true,
78 minifyParams: true,
79 minifySelectors: true,
80 normalizeCharset: true,
81 normalizeDisplayValues: true,
82 normalizePositions: true,
83 normalizeRepeatStyle: true,
84 normalizeString: true,
85 normalizeTimingFunctions: true,
86 normalizeUnicode: false,
87 normalizeUrl: true,
88 normalizeWhitespace: true,
89 orderedValues: true,
90 reduceIdents: true,
91 reduceInitial: true,
92 reduceTransforms: true,
93 svgo: {
94 plugins: [
95 { name: 'removeViewBox', active: false },
96 { name: 'removeDimensions', active: true }
97 ]
98 },
99 uniqueSelectors: true,
100 zindex: false
101 }]
102 })
103 ]);
104 }
105
106 async optimizeCSS(config: OptimizationConfig): Promise<void> {
107 const combinedCSS = await this.combineCSS(config.input);
108
109 // Configure PurgeCSS content
110 if (config.purgeContent.length > 0) {
111 this.updatePurgeCSSContent(config.purgeContent);
112 }
113
114 // Process CSS
115 const result = await this.postcssProcessor.process(combinedCSS, {
116 from: config.input[0],
117 to: config.output,
118 map: config.generateSourceMaps ? { inline: false } : false
119 });
120
121 // Write optimized CSS
122 await fs.writeFile(config.output, result.css);
123
124 if (result.map && config.generateSourceMaps) {
125 await fs.writeFile(\\\`\\\${config.output}.map\\\`, result.map.toString());
126 }
127
128 // Generate compression variants
129 await this.generateCompressedVersions(config.output, result.css, config.compressionTypes);
130
131 // Generate optimization report
132 const report = await this.generateOptimizationReport(combinedCSS, result.css);
133 await fs.writeFile(\\\`\\\${config.output}.report.json\\\`, JSON.stringify(report, null, 2));
134 }
135
136 private async combineCSS(inputs: string[]): Promise<string> {
137 const cssContents = await Promise.all(
138 inputs.map(input => fs.readFile(input, 'utf8'))
139 );
140
141 return cssContents.join('\\\\n/* === FILE SEPARATOR === */\\\\n');
142 }
143
144 private updatePurgeCSSContent(content: string[]): void {
145 // Update PurgeCSS configuration dynamically
146 const purgeCSSPlugin = this.postcssProcessor.plugins.find(
147 plugin => plugin.pluginName === 'postcss-purgecss'
148 );
149
150 if (purgeCSSPlugin) {
151 (purgeCSSPlugin as any).content = content;
152 }
153 }
154
155 private async generateCompressedVersions(
156 outputPath: string,
157 css: string,
158 compressionTypes: ('gzip' | 'brotli')[]
159 ): Promise<void> {
160 const compressionPromises = compressionTypes.map(async (type) => {
161 const compressed = type === 'gzip'
162 ? gzipSync(css, { level: 9 })
163 : brotliCompressSync(css, {
164 params: {
165 [11]: 11, // BROTLI_PARAM_QUALITY - maximum compression
166 [10]: 22 // BROTLI_PARAM_LGWIN - large window size
167 }
168 });
169
170 await fs.writeFile(\\\`\\\${outputPath}.\\\${type}\\\`, compressed);
171 });
172
173 await Promise.all(compressionPromises);
174 }
175
176 private async generateOptimizationReport(
177 originalCSS: string,
178 optimizedCSS: string
179 ): Promise<OptimizationReport> {
180 const originalSize = Buffer.byteLength(originalCSS, 'utf8');
181 const optimizedSize = Buffer.byteLength(optimizedCSS, 'utf8');
182 const savings = originalSize - optimizedSize;
183 const percentage = ((savings / originalSize) * 100).toFixed(2);
184
185 // Analyze selector counts
186 const originalSelectors = (originalCSS.match(/[^{}]+(?={)/g) || []).length;
187 const optimizedSelectors = (optimizedCSS.match(/[^{}]+(?={)/g) || []).length;
188 const removedSelectors = originalSelectors - optimizedSelectors;
189
190 // Analyze rules
191 const originalRules = (originalCSS.match(/{[^}]*}/g) || []).length;
192 const optimizedRules = (optimizedCSS.match(/{[^}]*}/g) || []).length;
193
194 return {
195 timestamp: new Date().toISOString(),
196 sizes: {
197 original: originalSize,
198 optimized: optimizedSize,
199 savings: savings,
200 percentage: parseFloat(percentage)
201 },
202 selectors: {
203 original: originalSelectors,
204 optimized: optimizedSelectors,
205 removed: removedSelectors
206 },
207 rules: {
208 original: originalRules,
209 optimized: optimizedRules,
210 removed: originalRules - optimizedRules
211 },
212 compressionEstimates: {
213 gzip: Math.round(optimizedSize * 0.3), // Estimated gzip compression
214 brotli: Math.round(optimizedSize * 0.25) // Estimated brotli compression
215 }
216 };
217 }
218}
219
220interface OptimizationReport {
221 timestamp: string;
222 sizes: {
223 original: number;
224 optimized: number;
225 savings: number;
226 percentage: number;
227 };
228 selectors: {
229 original: number;
230 optimized: number;
231 removed: number;
232 };
233 rules: {
234 original: number;
235 optimized: number;
236 removed: number;
237 };
238 compressionEstimates: {
239 gzip: number;
240 brotli: number;
241 };
242}
243
244// CSS Bundle Analyzer
245class CSSBundleAnalyzer {
246 async analyzeBundle(cssFilePath: string): Promise<BundleAnalysis> {
247 const css = await fs.readFile(cssFilePath, 'utf8');
248
249 return {
250 fileSize: Buffer.byteLength(css, 'utf8'),
251 selectorCount: this.countSelectors(css),
252 ruleCount: this.countRules(css),
253 mediaQueryCount: this.countMediaQueries(css),
254 keyframeCount: this.countKeyframes(css),
255 customPropertyCount: this.countCustomProperties(css),
256 complexityScore: this.calculateComplexityScore(css),
257 duplicateRules: this.findDuplicateRules(css),
258 unusedVendorPrefixes: this.findUnusedVendorPrefixes(css)
259 };
260 }
261
262 private countSelectors(css: string): number {
263 return (css.match(/[^{}]+(?={)/g) || []).length;
264 }
265
266 private countRules(css: string): number {
267 return (css.match(/{[^}]*}/g) || []).length;
268 }
269
270 private countMediaQueries(css: string): number {
271 return (css.match(/@media[^{]*{/g) || []).length;
272 }
273
274 private countKeyframes(css: string): number {
275 return (css.match(/@keyframes[^{]*{/g) || []).length;
276 }
277
278 private countCustomProperties(css: string): number {
279 return (css.match(/--[a-zA-Z0-9-]+/g) || []).length;
280 }
281
282 private calculateComplexityScore(css: string): number {
283 const selectorComplexity = (css.match(/[>+~]/g) || []).length;
284 const pseudoComplexity = (css.match(/:nth-|:not\\\\(|:has\\\\(/g) || []).length;
285 const nestedComplexity = (css.match(/\\\\s{4,}/g) || []).length;
286
287 return selectorComplexity + pseudoComplexity * 2 + nestedComplexity;
288 }
289
290 private findDuplicateRules(css: string): string[] {
291 const ruleMap = new Map<string, number>();
292 const duplicates: string[] = [];
293
294 const rules = css.match(/{[^}]*}/g) || [];
295 rules.forEach(rule => {
296 const normalized = rule.replace(/\\\\s+/g, ' ').trim();
297 const count = ruleMap.get(normalized) || 0;
298 ruleMap.set(normalized, count + 1);
299
300 if (count === 1) {
301 duplicates.push(normalized);
302 }
303 });
304
305 return duplicates;
306 }
307
308 private findUnusedVendorPrefixes(css: string): string[] {
309 const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-'];
310 const unused: string[] = [];
311
312 vendorPrefixes.forEach(prefix => {
313 const prefixedProps = css.match(new RegExp(\\\`\\\${prefix}[a-zA-Z-]+\\\`, 'g')) || [];
314 const standardProps = prefixedProps.map(prop => prop.replace(prefix, ''));
315
316 standardProps.forEach(standardProp => {
317 if (css.includes(standardProp) && css.includes(\\\`\\\${prefix}\\\${standardProp}\\\`)) {
318 unused.push(\\\`\\\${prefix}\\\${standardProp}\\\`);
319 }
320 });
321 });
322
323 return unused;
324 }
325}
326
327interface BundleAnalysis {
328 fileSize: number;
329 selectorCount: number;
330 ruleCount: number;
331 mediaQueryCount: number;
332 keyframeCount: number;
333 customPropertyCount: number;
334 complexityScore: number;
335 duplicateRules: string[];
336 unusedVendorPrefixes: string[];
337}
338
339// Usage example
340export const optimizeCSSBundle = async () => {
341 const optimizer = new AdvancedCSSOptimizer();
342
343 await optimizer.optimizeCSS({
344 input: ['src/styles/main.css', 'src/styles/components.css'],
345 output: 'dist/styles/bundle.css',
346 purgeContent: ['src/**/*.{js,jsx,ts,tsx,html}'],
347 compressionTypes: ['gzip', 'brotli'],
348 extractUnusedSelectors: true,
349 generateSourceMaps: true
350 });
351};Continuous CSS performance monitoring enables data-driven optimization
decisions. Implementing comprehensive metrics collection and analysis
helps maintain optimal performance as applications evolve.
Loading Optimization
Inline critical CSS for above-the-fold content
Use preload hints for important stylesheets
Implement progressive CSS loading strategies
Minimize render-blocking resources
Code Architecture
Use efficient selectors and avoid deep nesting
Implement CSS custom properties for theming
Optimize selector specificity and reduce conflicts
Use container queries for responsive components
Bundle Optimization
Remove unused CSS with PurgeCSS
Implement code splitting for route-based loading
Use advanced minification and compression
Generate multiple compression formats
Performance Monitoring
Track Core Web Vitals and CSS-specific metrics
Monitor style recalculation frequency
Analyze bundle sizes and loading performance
Implement automated performance regression testing
Learn about Production Performance Checklist.
✅ Critical CSS extracted and inlined
✅ Non-critical CSS loaded asynchronously
✅ Preload hints implemented for key resources
✅ Render-blocking stylesheets minimized
✅ Selectors optimized for performance
✅ Unused CSS removed from production builds
✅ CSS architecture follows scalable patterns
✅ Vendor prefixes optimized for target browsers
✅ CSS minified and compressed
✅ Gzip and Brotli compression enabled
✅ HTTP/2 push configured for critical resources
✅ CDN caching strategies implemented
✅ Core Web Vitals tracking implemented
✅ Bundle size monitoring in place
✅ Performance regression tests automated
✅ Real user monitoring configured
Your progress is saved in this browser, on this device only.