Master CSS preprocessors (Sass, Less, Stylus), PostCSS transformations, modern build tools, optimization techniques, and automated CSS workflows for efficient development.
Master CSS preprocessors (Sass, Less, Stylus), PostCSS transformations, modern build tools, optimization techniques, and automated CSS workflows for efficient development.
CSS preprocessors revolutionized CSS development by adding programming
features like variables, functions, mixins, and modular imports. Modern
build tools now combine preprocessing with optimization, transformation,
and automated workflows for production-ready CSS.
Enhanced Syntax:
Variables, nesting, mixins, and functions
Modular Architecture:
Import/export system for maintainable code
Build-time Optimization:
Dead code elimination and optimization
Developer Experience:
Better tooling, error reporting, and debugging
Compatibility:
Automated vendor prefixing and polyfills
Sass (Syntactically Awesome Style Sheets) provides powerful features
for creating maintainable, scalable CSS. Modern Sass includes advanced
features like modules, mathematical functions, and control directives.
Learn about Sass Modules and Architecture.
1// Design tokens with proper module structure
2@use 'sass:math';
3@use 'sass:map';
4
5// Color system with semantic naming
6$colors: (
7 'primary': (
8 50: #eff6ff,
9 100: #dbeafe,
10 200: #bfdbfe,
11 300: #93c5fd,
12 400: #60a5fa,
13 500: #3b82f6,
14 600: #2563eb,
15 700: #1d4ed8,
16 800: #1e40af,
17 900: #1e3a8a,
18 ),
19 'neutral': (
20 50: #f9fafb,
21 100: #f3f4f6,
22 200: #e5e7eb,
23 300: #d1d5db,
24 400: #9ca3af,
25 500: #6b7280,
26 600: #4b5563,
27 700: #374151,
28 800: #1f2937,
29 900: #111827,
30 )
31) !default;
32
33// Typography scale with modular scale
34$font-sizes: (
35 'xs': 0.75rem,
36 'sm': 0.875rem,
37 'base': 1rem,
38 'lg': 1.125rem,
39 'xl': 1.25rem,
40 '2xl': 1.5rem,
41 '3xl': 1.875rem,
42 '4xl': 2.25rem,
43 '5xl': 3rem,
44) !default;
45
46// Spacing system based on design tokens
47$spacing: (
48 'px': 1px,
49 '0': 0,
50 '1': 0.25rem,
51 '2': 0.5rem,
52 '3': 0.75rem,
53 '4': 1rem,
54 '6': 1.5rem,
55 '8': 2rem,
56 '12': 3rem,
57 '16': 4rem,
58 '20': 5rem,
59) !default;
60
61// Breakpoint system for responsive design
62$breakpoints: (
63 'sm': 640px,
64 'md': 768px,
65 'lg': 1024px,
66 'xl': 1280px,
67 '2xl': 1536px,
68) !default;
69
70// Utility functions for token access
71@function color($name, $shade: 500) {
72 @return map.get($colors, $name, $shade);
73}
74
75@function font-size($size) {
76 @return map.get($font-sizes, $size);
77}
78
79@function spacing($size) {
80 @return map.get($spacing, $size);
81}
82
83@function breakpoint($name) {
84 @return map.get($breakpoints, $name);
85}
86
87// Advanced mathematical functions
88@function modular-scale($base, $ratio, $steps) {
89 @return $base * math.pow($ratio, $steps);
90}
91
92@function golden-ratio($base, $steps: 1) {
93 @return modular-scale($base, 1.618, $steps);
94}
95
96// Export public API
97@mixin tokens-api {
98 :root {
99 // Color custom properties
100 @each $color-name, $shades in $colors {
101 @each $shade, $value in $shades {
102 --color-#{$color-name}-#{$shade}: #{$value};
103 }
104 }
105
106 // Typography custom properties
107 @each $name, $size in $font-sizes {
108 --font-size-#{$name}: #{$size};
109 }
110
111 // Spacing custom properties
112 @each $name, $size in $spacing {
113 --space-#{$name}: #{$size};
114 }
115 }
116}Learn about Advanced Sass Mixins and Functions.
1@use 'sass:math';
2@use 'sass:list';
3@use 'sass:string';
4@use 'tokens' as *;
5
6// Responsive breakpoint mixin with mobile-first approach
7@mixin respond-above($breakpoint) {
8 @media (min-width: breakpoint($breakpoint)) {
9 @content;
10 }
11}
12
13@mixin respond-below($breakpoint) {
14 @media (max-width: breakpoint($breakpoint) - 1px) {
15 @content;
16 }
17}
18
19@mixin respond-between($min, $max) {
20 @media (min-width: breakpoint($min)) and (max-width: breakpoint($max) - 1px) {
21 @content;
22 }
23}
24
25// Advanced typography mixin with fluid scaling
26@mixin fluid-type($min-size, $max-size, $min-width: 320px, $max-width: 1200px) {
27 font-size: $min-size;
28
29 @media (min-width: $min-width) and (max-width: $max-width) {
30 font-size: calc(#{$min-size} + #{math.div($max-size - $min-size, $max-width - $min-width)} * (100vw - #{$min-width}));
31 }
32
33 @media (min-width: $max-width) {
34 font-size: $max-size;
35 }
36}
37
38// Flexbox utilities with fallbacks
39@mixin flex-center($direction: row) {
40 display: flex;
41 flex-direction: $direction;
42 align-items: center;
43 justify-content: center;
44}
45
46@mixin flex-between($direction: row) {
47 display: flex;
48 flex-direction: $direction;
49 align-items: center;
50 justify-content: space-between;
51}
52
53// Grid system mixin
54@mixin grid-container($columns: 12, $gap: spacing('4')) {
55 display: grid;
56 grid-template-columns: repeat($columns, 1fr);
57 gap: $gap;
58}
59
60@mixin grid-column($start, $end: null) {
61 @if $end {
62 grid-column: #{$start} / #{$end};
63 } @else {
64 grid-column: span #{$start};
65 }
66}
67
68// Advanced animation mixins
69@mixin transition($properties: all, $duration: 0.3s, $timing: ease-in-out, $delay: 0s) {
70 $transitions: ();
71
72 @each $property in $properties {
73 $transitions: list.append($transitions, #{$property} #{$duration} #{$timing} #{$delay}, comma);
74 }
75
76 transition: $transitions;
77}
78
79@mixin animation($name, $duration: 1s, $timing: ease-in-out, $delay: 0s, $iteration: 1, $direction: normal, $fill: both) {
80 animation: $name $duration $timing $delay $iteration $direction $fill;
81}
82
83// Button factory mixin
84@mixin button-variant($bg-color, $text-color: white, $border-color: $bg-color) {
85 background-color: $bg-color;
86 color: $text-color;
87 border-color: $border-color;
88
89 &:hover:not(:disabled) {
90 background-color: darken($bg-color, 8%);
91 border-color: darken($border-color, 8%);
92 transform: translateY(-1px);
93 box-shadow: 0 4px 8px rgba($bg-color, 0.3);
94 }
95
96 &:active {
97 background-color: darken($bg-color, 12%);
98 transform: translateY(0);
99 }
100
101 &:disabled {
102 background-color: lighten($bg-color, 20%);
103 border-color: lighten($border-color, 20%);
104 cursor: not-allowed;
105 transform: none;
106 box-shadow: none;
107 }
108}
109
110// CSS custom property generator
111@mixin generate-custom-properties($map, $prefix: '') {
112 @each $key, $value in $map {
113 @if type-of($value) == map {
114 @include generate-custom-properties($value, #{$prefix}#{$key}-);
115 } @else {
116 --#{$prefix}#{$key}: #{$value};
117 }
118 }
119}
120
121// Aspect ratio with fallback
122@mixin aspect-ratio($width, $height) {
123 aspect-ratio: #{math.div($width, $height)};
124
125 // Fallback for older browsers
126 @supports not (aspect-ratio: 1) {
127 position: relative;
128
129 &::before {
130 content: '';
131 display: block;
132 padding-bottom: math.percentage(math.div($height, $width));
133 }
134
135 > * {
136 position: absolute;
137 top: 0;
138 left: 0;
139 width: 100%;
140 height: 100%;
141 }
142 }
143}
144
145// Dark mode utility
146@mixin dark-mode {
147 @media (prefers-color-scheme: dark) {
148 @content;
149 }
150
151 [data-theme="dark"] & {
152 @content;
153 }
154}
155
156// High contrast mode
157@mixin high-contrast {
158 @media (prefers-contrast: high) {
159 @content;
160 }
161}
162
163// Reduced motion
164@mixin reduced-motion {
165 @media (prefers-reduced-motion: reduce) {
166 @content;
167 }
168}Learn about Component Architecture with Sass.
1@use 'tokens' as *;
2@use 'mixins' as *;
3
4// Base component class
5%component-base {
6 position: relative;
7 box-sizing: border-box;
8}
9
10// Button component system
11.btn {
12 @extend %component-base;
13
14 // Base button styles
15 display: inline-flex;
16 align-items: center;
17 justify-content: center;
18 gap: spacing('2');
19 padding: spacing('3') spacing('4');
20 font-family: inherit;
21 font-size: font-size('base');
22 font-weight: 500;
23 line-height: 1;
24 border: 2px solid transparent;
25 border-radius: 6px;
26 cursor: pointer;
27 text-decoration: none;
28 user-select: none;
29 @include transition((transform, box-shadow, background-color));
30
31 // Size variants
32 &--sm {
33 padding: spacing('2') spacing('3');
34 font-size: font-size('sm');
35 }
36
37 &--lg {
38 padding: spacing('4') spacing('6');
39 font-size: font-size('lg');
40 }
41
42 // Color variants using mixin
43 &--primary {
44 @include button-variant(color('primary', 500));
45 }
46
47 &--secondary {
48 @include button-variant(color('neutral', 100), color('neutral', 900), color('neutral', 200));
49 }
50
51 &--success {
52 @include button-variant(#10b981);
53 }
54
55 &--danger {
56 @include button-variant(#ef4444);
57 }
58
59 // State variants
60 &--outline {
61 background: transparent;
62 color: color('primary', 500);
63 border-color: color('primary', 500);
64
65 &:hover:not(:disabled) {
66 background: color('primary', 50);
67 color: color('primary', 600);
68 border-color: color('primary', 600);
69 }
70 }
71
72 &--ghost {
73 background: transparent;
74 border-color: transparent;
75 color: color('neutral', 700);
76
77 &:hover:not(:disabled) {
78 background: color('neutral', 100);
79 }
80 }
81
82 // Loading state
83 &--loading {
84 position: relative;
85 color: transparent;
86 pointer-events: none;
87
88 &::after {
89 content: '';
90 position: absolute;
91 top: 50%;
92 left: 50%;
93 width: 20px;
94 height: 20px;
95 margin: -10px 0 0 -10px;
96 border: 2px solid currentColor;
97 border-top-color: transparent;
98 border-radius: 50%;
99 @include animation(spin, 1s, linear, 0s, infinite);
100 }
101 }
102
103 // Responsive behavior
104 @include respond-below('md') {
105 width: 100%;
106 justify-content: center;
107 }
108}
109
110// Card component system
111.card {
112 @extend %component-base;
113
114 background: white;
115 border-radius: 12px;
116 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
117 overflow: hidden;
118 @include transition(transform, box-shadow);
119
120 &:hover {
121 transform: translateY(-2px);
122 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
123 }
124
125 &__header {
126 padding: spacing('6');
127 border-bottom: 1px solid color('neutral', 200);
128 }
129
130 &__title {
131 margin: 0 0 spacing('2');
132 font-size: font-size('xl');
133 font-weight: 600;
134 color: color('neutral', 900);
135 }
136
137 &__subtitle {
138 margin: 0;
139 font-size: font-size('sm');
140 color: color('neutral', 500);
141 }
142
143 &__content {
144 padding: spacing('6');
145 }
146
147 &__footer {
148 padding: spacing('4') spacing('6');
149 background: color('neutral', 50);
150 border-top: 1px solid color('neutral', 200);
151 }
152
153 // Card variants
154 &--featured {
155 border: 2px solid color('primary', 500);
156 box-shadow: 0 4px 8px rgba(color('primary', 500), 0.2);
157 }
158
159 &--compact {
160 .card__header,
161 .card__content,
162 .card__footer {
163 padding: spacing('4');
164 }
165 }
166
167 // Dark mode support
168 @include dark-mode {
169 background: color('neutral', 800);
170 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
171
172 .card__title {
173 color: color('neutral', 100);
174 }
175
176 .card__subtitle {
177 color: color('neutral', 400);
178 }
179
180 .card__header {
181 border-color: color('neutral', 700);
182 }
183
184 .card__footer {
185 background: color('neutral', 700);
186 border-color: color('neutral', 600);
187 }
188 }
189}
190
191// Form component system
192.form {
193 &__group {
194 margin-bottom: spacing('6');
195 }
196
197 &__label {
198 display: block;
199 font-weight: 500;
200 margin-bottom: spacing('2');
201 color: color('neutral', 700);
202 }
203
204 &__input {
205 width: 100%;
206 padding: spacing('3') spacing('4');
207 font-size: font-size('base');
208 border: 2px solid color('neutral', 300);
209 border-radius: 6px;
210 @include transition(border-color, box-shadow);
211
212 &:focus {
213 outline: none;
214 border-color: color('primary', 500);
215 box-shadow: 0 0 0 3px rgba(color('primary', 500), 0.1);
216 }
217
218 &--error {
219 border-color: #ef4444;
220
221 &:focus {
222 border-color: #ef4444;
223 box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
224 }
225 }
226 }
227
228 &__error {
229 margin-top: spacing('1');
230 font-size: font-size('sm');
231 color: #ef4444;
232 }
233
234 &__help {
235 margin-top: spacing('1');
236 font-size: font-size('sm');
237 color: color('neutral', 500);
238 }
239}
240
241@keyframes spin {
242 to {
243 transform: rotate(360deg);
244 }
245}PostCSS transforms CSS with JavaScript plugins, providing a modular
approach to CSS processing. It enables autoprefixing, future CSS
features, optimization, and custom transformations.
Learn about PostCSS Configuration and Plugins.
1// Modern PostCSS configuration for production builds
2const postcssConfig = {
3 plugins: {
4 // Import resolution
5 'postcss-import': {
6 path: ['src/styles', 'node_modules'],
7 },
8
9 // Sass-like features
10 'postcss-nested': {},
11 'postcss-custom-properties': {
12 preserve: false, // Convert to static values
13 },
14 'postcss-custom-media': {},
15
16 // Future CSS features
17 'postcss-preset-env': {
18 stage: 2, // Stable features
19 features: {
20 'custom-properties': false, // Handled separately
21 'color-mod-function': { unresolved: 'warn' },
22 'nesting-rules': true,
23 'custom-media-queries': true,
24 'media-query-ranges': true,
25 },
26 browsers: ['> 1%', 'last 2 versions', 'not dead'],
27 },
28
29 // Vendor prefixing
30 'autoprefixer': {
31 grid: 'autoplace',
32 flexbox: 'no-2009',
33 },
34
35 // Performance optimizations
36 'cssnano': process.env.NODE_ENV === 'production' ? {
37 preset: ['advanced', {
38 discardComments: {
39 removeAll: true,
40 },
41 reduceIdents: false, // Keep animation names
42 zindex: false, // Don't optimize z-index
43 normalizeUnicode: false, // Preserve unicode
44 autoprefixer: false, // Handled separately
45 }]
46 } : false,
47
48 // CSS purging for unused styles
49 '@fullhuman/postcss-purgecss': process.env.NODE_ENV === 'production' ? {
50 content: [
51 './src/**/*.{js,jsx,ts,tsx,html}',
52 './public/**/*.html',
53 ],
54 defaultExtractor: content => content.match(/[\\w-/:]+(?<!:)/g) || [],
55 safelist: {
56 standard: [
57 // Dynamic classes
58 /^(.*-)?(sm|md|lg|xl):(.*)/,
59 /^(.*-)?hover:(.*)/,
60 /^(.*-)?focus:(.*)/,
61 // Component states
62 /^(.*-)?(active|disabled|loading|error)$/,
63 // Third-party libraries
64 /^prism-/,
65 /^hljs-/,
66 ],
67 deep: [
68 // Dynamic content classes
69 /code-block/,
70 /syntax-highlight/,
71 ],
72 greedy: [
73 // Animation classes
74 /animate-/,
75 /transition-/,
76 ]
77 }
78 } : false,
79
80 // Critical CSS extraction
81 'postcss-critical-css': process.env.NODE_ENV === 'production' ? {
82 output: 'critical',
83 preserve: true,
84 minify: true,
85 } : false,
86
87 // CSS statistics and reporting
88 'postcss-reporter': {
89 clearReportedMessages: true,
90 throwError: process.env.NODE_ENV === 'production',
91 },
92 }
93}
94
95module.exports = postcssConfigLearn about Custom PostCSS Plugins.
1// Custom PostCSS plugin for design token validation
2const postcss = require('postcss')
3
4// Plugin to validate design token usage
5const validateTokens = (options = {}) => {
6 const validTokens = options.validTokens || []
7 const tokenPattern = /var\\(--([^)]+)\\)/g
8
9 return {
10 postcssPlugin: 'validate-tokens',
11 Declaration(decl) {
12 const matches = decl.value.match(tokenPattern)
13 if (!matches) return
14
15 matches.forEach(match => {
16 const tokenName = match.replace('var(--', '').replace(')', '')
17 if (!validTokens.includes(tokenName)) {
18 throw decl.error(\`Invalid design token: --\${tokenName}\`)
19 }
20 })
21 }
22 }
23}
24validateTokens.postcss = true
25
26// Plugin to generate utility classes
27const generateUtilities = (options = {}) => {
28 const { spacing = {}, colors = {} } = options
29
30 return {
31 postcssPlugin: 'generate-utilities',
32 Once(root) {
33 // Generate spacing utilities
34 Object.entries(spacing).forEach(([key, value]) => {
35 // Margin utilities
36 root.append(postcss.rule({ selector: \`.m-\${key}\` }))
37 .last.append(postcss.decl({ prop: 'margin', value }))
38
39 root.append(postcss.rule({ selector: \`.mt-\${key}\` }))
40 .last.append(postcss.decl({ prop: 'margin-top', value }))
41
42 root.append(postcss.rule({ selector: \`.mr-\${key}\` }))
43 .last.append(postcss.decl({ prop: 'margin-right', value }))
44
45 root.append(postcss.rule({ selector: \`.mb-\${key}\` }))
46 .last.append(postcss.decl({ prop: 'margin-bottom', value }))
47
48 root.append(postcss.rule({ selector: \`.ml-\${key}\` }))
49 .last.append(postcss.decl({ prop: 'margin-left', value }))
50
51 // Padding utilities
52 root.append(postcss.rule({ selector: \`.p-\${key}\` }))
53 .last.append(postcss.decl({ prop: 'padding', value }))
54
55 root.append(postcss.rule({ selector: \`.pt-\${key}\` }))
56 .last.append(postcss.decl({ prop: 'padding-top', value }))
57
58 root.append(postcss.rule({ selector: \`.pr-\${key}\` }))
59 .last.append(postcss.decl({ prop: 'padding-right', value }))
60
61 root.append(postcss.rule({ selector: \`.pb-\${key}\` }))
62 .last.append(postcss.decl({ prop: 'padding-bottom', value }))
63
64 root.append(postcss.rule({ selector: \`.pl-\${key}\` }))
65 .last.append(postcss.decl({ prop: 'padding-left', value }))
66 })
67
68 // Generate color utilities
69 Object.entries(colors).forEach(([name, shades]) => {
70 Object.entries(shades).forEach(([shade, value]) => {
71 // Text color utilities
72 root.append(postcss.rule({ selector: \`.text-\${name}-\${shade}\` }))
73 .last.append(postcss.decl({ prop: 'color', value }))
74
75 // Background color utilities
76 root.append(postcss.rule({ selector: \`.bg-\${name}-\${shade}\` }))
77 .last.append(postcss.decl({ prop: 'background-color', value }))
78
79 // Border color utilities
80 root.append(postcss.rule({ selector: \`.border-\${name}-\${shade}\` }))
81 .last.append(postcss.decl({ prop: 'border-color', value }))
82 })
83 })
84 }
85 }
86}
87generateUtilities.postcss = true
88
89// Plugin for CSS optimization reporting
90const optimizationReporter = () => {
91 let originalSize = 0
92 let optimizedSize = 0
93
94 return {
95 postcssPlugin: 'optimization-reporter',
96 OnceExit(root) {
97 optimizedSize = root.toString().length
98
99 if (originalSize > 0) {
100 const savings = originalSize - optimizedSize
101 const percentage = ((savings / originalSize) * 100).toFixed(1)
102
103 console.log(\`\\n📊 CSS Optimization Report:\`)
104 console.log(\` Original: \${(originalSize / 1024).toFixed(1)}KB\`)
105 console.log(\` Optimized: \${(optimizedSize / 1024).toFixed(1)}KB\`)
106 console.log(\` Savings: \${(savings / 1024).toFixed(1)}KB (\${percentage}%)\`)
107 }
108 },
109 Once(root) {
110 originalSize = root.toString().length
111 }
112 }
113}
114optimizationReporter.postcss = true
115
116module.exports = {
117 validateTokens,
118 generateUtilities,
119 optimizationReporter
120}Modern build tools like Vite, Webpack, and Parcel provide optimized
CSS processing with features like hot module replacement, code splitting,
and advanced optimization. Understanding how to configure these tools
is crucial for efficient development and production builds.
Learn about Vite CSS Configuration.
1import { defineConfig } from 'vite'
2import { resolve } from 'path'
3
4export default defineConfig({
5 css: {
6 // CSS preprocessing
7 preprocessorOptions: {
8 scss: {
9 // Global imports available in all SCSS files
10 additionalData: \`
11 @use "@/styles/tokens" as *;
12 @use "@/styles/mixins" as *;
13 \`,
14 // Include paths for @import resolution
15 includePaths: ['src/styles', 'node_modules'],
16 }
17 },
18
19 // PostCSS configuration (can also use postcss.config.js)
20 postcss: {
21 plugins: [
22 require('postcss-import'),
23 require('postcss-nested'),
24 require('autoprefixer'),
25 ...(process.env.NODE_ENV === 'production' ? [
26 require('cssnano')({ preset: 'advanced' })
27 ] : [])
28 ]
29 },
30
31 // CSS modules configuration
32 modules: {
33 // Naming pattern for generated classes
34 generateScopedName: process.env.NODE_ENV === 'production'
35 ? '[hash:base64:5]'
36 : '[local]_[hash:base64:5]',
37
38 // Export globals from CSS modules
39 exportGlobals: true,
40
41 // Localize animation names
42 localizeKeyframes: true,
43 },
44
45 // Code splitting and optimization
46 codeSplit: true,
47
48 // Enable source maps in development
49 devSourcemap: true,
50 },
51
52 // Build optimizations
53 build: {
54 // CSS code splitting
55 cssCodeSplit: true,
56
57 // CSS target for modern browsers
58 cssTarget: 'chrome90',
59
60 rollupOptions: {
61 output: {
62 // Manual CSS chunking
63 manualChunks: {
64 'vendor-styles': ['normalize.css', 'prismjs/themes/prism.css'],
65 'component-styles': [
66 './src/components/Button/Button.module.scss',
67 './src/components/Card/Card.module.scss',
68 ]
69 },
70
71 // Asset naming
72 assetFileNames: (assetInfo) => {
73 if (assetInfo.name.endsWith('.css')) {
74 return 'assets/css/[name]-[hash][extname]'
75 }
76 return 'assets/[name]-[hash][extname]'
77 }
78 }
79 },
80
81 // Minification
82 minify: 'terser',
83 terserOptions: {
84 compress: {
85 drop_console: true,
86 drop_debugger: true
87 }
88 }
89 },
90
91 // Development server
92 server: {
93 // CSS hot reload
94 hmr: {
95 overlay: true
96 }
97 },
98
99 // Path resolution
100 resolve: {
101 alias: {
102 '@': resolve(__dirname, 'src'),
103 '@styles': resolve(__dirname, 'src/styles'),
104 '@components': resolve(__dirname, 'src/components'),
105 }
106 }
107})Learn about Webpack CSS Configuration.
1const path = require('path')
2const MiniCssExtractPlugin = require('mini-css-extract-plugin')
3const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
4const PurgeCSSPlugin = require('purgecss-webpack-plugin')
5const glob = require('glob')
6
7const isProduction = process.env.NODE_ENV === 'production'
8
9module.exports = {
10 module: {
11 rules: [
12 // Global CSS processing
13 {
14 test: /\\.css$/,
15 exclude: /\\.module\\.css$/,
16 use: [
17 isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
18 {
19 loader: 'css-loader',
20 options: {
21 importLoaders: 1,
22 sourceMap: !isProduction,
23 }
24 },
25 {
26 loader: 'postcss-loader',
27 options: {
28 sourceMap: !isProduction,
29 }
30 }
31 ]
32 },
33
34 // CSS Modules processing
35 {
36 test: /\\.module\\.css$/,
37 use: [
38 isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
39 {
40 loader: 'css-loader',
41 options: {
42 modules: {
43 localIdentName: isProduction
44 ? '[hash:base64:5]'
45 : '[local]__[hash:base64:5]',
46 exportLocalsConvention: 'camelCase',
47 },
48 importLoaders: 1,
49 sourceMap: !isProduction,
50 }
51 },
52 {
53 loader: 'postcss-loader',
54 options: {
55 sourceMap: !isProduction,
56 }
57 }
58 ]
59 },
60
61 // SCSS processing
62 {
63 test: /\\.scss$/,
64 exclude: /\\.module\\.scss$/,
65 use: [
66 isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
67 {
68 loader: 'css-loader',
69 options: {
70 importLoaders: 2,
71 sourceMap: !isProduction,
72 }
73 },
74 {
75 loader: 'postcss-loader',
76 options: {
77 sourceMap: !isProduction,
78 }
79 },
80 {
81 loader: 'sass-loader',
82 options: {
83 implementation: require('sass'),
84 sassOptions: {
85 includePaths: [
86 path.resolve(__dirname, 'src/styles'),
87 path.resolve(__dirname, 'node_modules'),
88 ],
89 outputStyle: isProduction ? 'compressed' : 'expanded',
90 },
91 additionalData: \`
92 @use "src/styles/tokens" as *;
93 @use "src/styles/mixins" as *;
94 \`,
95 sourceMap: !isProduction,
96 }
97 }
98 ]
99 },
100
101 // SCSS Modules processing
102 {
103 test: /\\.module\\.scss$/,
104 use: [
105 isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
106 {
107 loader: 'css-loader',
108 options: {
109 modules: {
110 localIdentName: isProduction
111 ? '[hash:base64:5]'
112 : '[local]__[hash:base64:5]',
113 exportLocalsConvention: 'camelCase',
114 },
115 importLoaders: 2,
116 sourceMap: !isProduction,
117 }
118 },
119 {
120 loader: 'postcss-loader',
121 options: {
122 sourceMap: !isProduction,
123 }
124 },
125 {
126 loader: 'sass-loader',
127 options: {
128 implementation: require('sass'),
129 sassOptions: {
130 includePaths: [
131 path.resolve(__dirname, 'src/styles'),
132 path.resolve(__dirname, 'node_modules'),
133 ],
134 },
135 additionalData: \`
136 @use "src/styles/tokens" as *;
137 @use "src/styles/mixins" as *;
138 \`,
139 sourceMap: !isProduction,
140 }
141 }
142 ]
143 }
144 ]
145 },
146
147 plugins: [
148 // CSS extraction for production
149 ...(isProduction ? [
150 new MiniCssExtractPlugin({
151 filename: 'css/[name].[contenthash:8].css',
152 chunkFilename: 'css/[name].[contenthash:8].chunk.css',
153 }),
154
155 // Remove unused CSS
156 new PurgeCSSPlugin({
157 paths: glob.sync(\`\${path.join(__dirname, 'src')}/**/*\`, { nodir: true }),
158 defaultExtractor: content => content.match(/[\\w-/:]+(?<!:)/g) || [],
159 safelist: {
160 standard: [
161 /^(.*-)?(sm|md|lg|xl):(.*)/,
162 /^(.*-)?hover:(.*)/,
163 /^(.*-)?focus:(.*)/,
164 /^(.*-)?(active|disabled|loading|error)$/,
165 ],
166 deep: [/code-block/, /syntax-highlight/],
167 greedy: [/animate-/, /transition-/]
168 }
169 })
170 ] : [])
171 ],
172
173 optimization: {
174 minimizer: [
175 \`...\`, // Keep existing JS minimizers
176 ...(isProduction ? [
177 new CssMinimizerPlugin({
178 minimizerOptions: {
179 preset: [
180 'advanced',
181 {
182 discardComments: { removeAll: true },
183 reduceIdents: false,
184 zindex: false,
185 autoprefixer: false,
186 }
187 ]
188 }
189 })
190 ] : [])
191 ],
192
193 // CSS code splitting
194 splitChunks: {
195 cacheGroups: {
196 styles: {
197 name: 'styles',
198 type: 'css/mini-extract',
199 chunks: 'all',
200 enforce: true,
201 },
202 vendor: {
203 test: /[\\\\/]node_modules[\\\\/]/,
204 name: 'vendors',
205 type: 'css/mini-extract',
206 chunks: 'all',
207 enforce: true,
208 }
209 }
210 }
211 }
212}Effective CSS optimization involves reducing file size, eliminating
unused code, optimizing critical rendering path, and implementing
efficient loading strategies for improved performance.
Learn about Critical CSS Extraction.
1const critical = require('critical')
2const path = require('path')
3const fs = require('fs').promises
4
5async function generateCriticalCSS() {
6 const pages = [
7 { url: 'http://localhost:3000/', name: 'home' },
8 { url: 'http://localhost:3000/about', name: 'about' },
9 { url: 'http://localhost:3000/blog', name: 'blog' },
10 { url: 'http://localhost:3000/contact', name: 'contact' },
11 ]
12
13 const criticalOptions = {
14 // Base configuration
15 base: path.resolve(__dirname, 'dist'),
16 src: 'index.html',
17 target: 'css/critical.css',
18
19 // Viewport dimensions
20 width: 1300,
21 height: 900,
22
23 // Performance optimizations
24 minify: true,
25 extract: true, // Remove critical CSS from main stylesheet
26 inlineImages: true,
27 timeout: 30000,
28
29 // Include/exclude patterns
30 include: [
31 // Always include critical components
32 /\\.header/,
33 /\\.nav/,
34 /\\.hero/,
35 /\\.above-fold/,
36 ],
37 ignore: [
38 // Exclude non-critical components
39 /\\.footer/,
40 /\\.modal/,
41 /\\.tooltip/,
42 /\\.lazy-load/,
43 // Exclude third-party styles
44 /@import.*url\\(/,
45 /\\/\\*.*critical.*\\*\\//,
46 ]
47 }
48
49 try {
50 // Generate critical CSS for each page
51 for (const page of pages) {
52 console.log(\`🔧 Generating critical CSS for \${page.name}...\`)
53
54 await critical.generate({
55 ...criticalOptions,
56 src: page.url,
57 target: \`css/critical-\${page.name}.css\`,
58 css: ['css/main.css'],
59 penthouse: {
60 timeout: 60000,
61 pageLoadSkipTimeout: 10000,
62 renderWaitTime: 1000,
63 blockJSRequests: true,
64 }
65 })
66
67 console.log(\`✅ Critical CSS generated for \${page.name}\`)
68 }
69
70 // Generate combined critical CSS
71 await generateCombinedCritical(pages)
72
73 } catch (error) {
74 console.error('❌ Critical CSS generation failed:', error)
75 process.exit(1)
76 }
77}
78
79async function generateCombinedCritical(pages) {
80 console.log('🔧 Generating combined critical CSS...')
81
82 const criticalFiles = pages.map(page =>
83 \`css/critical-\${page.name}.css\`
84 )
85
86 let combinedCritical = ''
87 const seenRules = new Set()
88
89 for (const file of criticalFiles) {
90 try {
91 const content = await fs.readFile(path.resolve(__dirname, 'dist', file), 'utf8')
92
93 // Parse and deduplicate CSS rules
94 const rules = content.match(/[^{}]+\\{[^}]*\\}/g) || []
95
96 for (const rule of rules) {
97 const ruleHash = rule.replace(/\\s+/g, '')
98 if (!seenRules.has(ruleHash)) {
99 seenRules.add(ruleHash)
100 combinedCritical += rule + '\\n'
101 }
102 }
103 } catch (error) {
104 console.warn(\`Warning: Could not read \${file}\`)
105 }
106 }
107
108 // Minify combined critical CSS
109 const CleanCSS = require('clean-css')
110 const minified = new CleanCSS({
111 level: 2,
112 returnPromise: true
113 }).minify(combinedCritical)
114
115 await fs.writeFile(
116 path.resolve(__dirname, 'dist/css/critical-combined.css'),
117 (await minified).styles
118 )
119
120 console.log(\`✅ Combined critical CSS generated (\${(await minified).styles.length} bytes)\`)
121}
122
123// Build script integration
124async function buildWithCriticalCSS() {
125 console.log('🚀 Starting production build with critical CSS...')
126
127 // 1. Run main build
128 const { execSync } = require('child_process')
129 execSync('npm run build', { stdio: 'inherit' })
130
131 // 2. Generate critical CSS
132 await generateCriticalCSS()
133
134 // 3. Update HTML templates with critical CSS
135 await injectCriticalCSS()
136
137 console.log('✅ Production build with critical CSS completed!')
138}
139
140async function injectCriticalCSS() {
141 const htmlFiles = ['dist/index.html', 'dist/about/index.html']
142
143 for (const htmlFile of htmlFiles) {
144 try {
145 let html = await fs.readFile(htmlFile, 'utf8')
146 const criticalCSS = await fs.readFile('dist/css/critical-combined.css', 'utf8')
147
148 // Inject critical CSS inline
149 html = html.replace(
150 '</head>',
151 \`<style>\${criticalCSS}</style>\\n</head>\`
152 )
153
154 // Preload non-critical CSS
155 html = html.replace(
156 /<link rel="stylesheet" href="([^"]*main[^"]*\\.css)">/,
157 \`<link rel="preload" href="$1" as="style" onload="this.onload=null;this.rel='stylesheet'">
158 <noscript><link rel="stylesheet" href="$1"></noscript>\`
159 )
160
161 await fs.writeFile(htmlFile, html)
162 console.log(\`✅ Critical CSS injected into \${htmlFile}\`)
163 } catch (error) {
164 console.warn(\`Warning: Could not inject critical CSS into \${htmlFile}\`)
165 }
166 }
167}
168
169module.exports = { generateCriticalCSS, buildWithCriticalCSS }
170
171// Run if called directly
172if (require.main === module) {
173 buildWithCriticalCSS()
174}Learn about Automated CSS Performance Monitoring.
1const fs = require('fs').promises
2const path = require('path')
3const gzipSize = require('gzip-size')
4const brotliSize = require('brotli-size')
5const postcss = require('postcss')
6
7class CSSPerformanceMonitor {
8 constructor(options = {}) {
9 this.baseDir = options.baseDir || 'dist'
10 this.thresholds = {
11 size: options.maxSize || 100 * 1024, // 100KB
12 gzipSize: options.maxGzipSize || 25 * 1024, // 25KB
13 selectors: options.maxSelectors || 4000,
14 rules: options.maxRules || 3000,
15 ...options.thresholds
16 }
17 this.report = {
18 files: {},
19 summary: {},
20 warnings: [],
21 errors: []
22 }
23 }
24
25 async analyzeCSS() {
26 console.log('🔍 Starting CSS performance analysis...')
27
28 const cssFiles = await this.findCSSFiles()
29
30 for (const file of cssFiles) {
31 await this.analyzeFile(file)
32 }
33
34 this.generateSummary()
35 this.generateReport()
36
37 return this.report
38 }
39
40 async findCSSFiles() {
41 const files = []
42
43 async function scan(dir) {
44 const entries = await fs.readdir(dir, { withFileTypes: true })
45
46 for (const entry of entries) {
47 const fullPath = path.join(dir, entry.name)
48
49 if (entry.isDirectory()) {
50 await scan(fullPath)
51 } else if (entry.name.endsWith('.css')) {
52 files.push(fullPath)
53 }
54 }
55 }
56
57 await scan(this.baseDir)
58 return files
59 }
60
61 async analyzeFile(filePath) {
62 const relativePath = path.relative(this.baseDir, filePath)
63 const content = await fs.readFile(filePath, 'utf8')
64 const ast = postcss.parse(content)
65
66 const analysis = {
67 path: relativePath,
68 size: Buffer.byteLength(content, 'utf8'),
69 gzipSize: await gzipSize(content),
70 brotliSize: await brotliSize(content),
71 rules: 0,
72 selectors: 0,
73 declarations: 0,
74 mediaQueries: 0,
75 keyframes: 0,
76 imports: 0,
77 complexity: {
78 maxSpecificity: 0,
79 avgSpecificity: 0,
80 deepestNesting: 0,
81 universalSelectors: 0,
82 idSelectors: 0,
83 duplicateRules: 0
84 },
85 performance: {
86 renderingCost: 0,
87 selectorEfficiency: 0,
88 cacheability: 0
89 }
90 }
91
92 // Analyze AST
93 ast.walkRules(rule => {
94 analysis.rules++
95 analysis.selectors += rule.selectors.length
96
97 // Calculate specificity
98 rule.selectors.forEach(selector => {
99 const specificity = this.calculateSpecificity(selector)
100 analysis.complexity.maxSpecificity = Math.max(
101 analysis.complexity.maxSpecificity,
102 specificity
103 )
104 })
105
106 // Count declarations
107 rule.walkDecls(() => {
108 analysis.declarations++
109 })
110 })
111
112 ast.walkAtRules(rule => {
113 if (rule.name === 'media') {
114 analysis.mediaQueries++
115 } else if (rule.name === 'keyframes') {
116 analysis.keyframes++
117 } else if (rule.name === 'import') {
118 analysis.imports++
119 }
120 })
121
122 // Calculate performance metrics
123 this.calculatePerformanceMetrics(analysis, content)
124
125 // Check thresholds
126 this.checkThresholds(analysis)
127
128 this.report.files[relativePath] = analysis
129
130 console.log(\`📊 \${relativePath}: \${this.formatSize(analysis.size)} (\${this.formatSize(analysis.gzipSize)} gzipped)\`)
131 }
132
133 calculateSpecificity(selector) {
134 // Simplified specificity calculation
135 const ids = (selector.match(/#[^\\s\\+>~.\\[:]+/g) || []).length
136 const classes = (selector.match(/\\.[^\\s\\+>~#\\[:]+/g) || []).length
137 const attrs = (selector.match(/\\[[^\\]]*\\]/g) || []).length
138 const pseudos = (selector.match(/:(?!not)[^\\s\\+>~#\\.\\[:]+/g) || []).length
139 const elements = (selector.match(/^|[\\s\\+>~][a-zA-Z]/g) || []).length
140
141 return ids * 100 + (classes + attrs + pseudos) * 10 + elements
142 }
143
144 calculatePerformanceMetrics(analysis, content) {
145 // Rendering cost factors
146 const expensiveProperties = [
147 'box-shadow', 'border-radius', 'transform', 'filter',
148 'opacity', 'position: fixed', 'position: sticky'
149 ]
150
151 const expensiveCount = expensiveProperties.reduce((count, prop) => {
152 const regex = new RegExp(prop.replace(/[.*+?^$\{}()|[\\\\]\\\\]/g, '\\\\$&'), 'gi')
153 return count + (content.match(regex) || []).length
154 }, 0)
155
156 analysis.performance.renderingCost = Math.min(100, (expensiveCount / analysis.declarations) * 100)
157
158 // Selector efficiency (lower is better)
159 const inefficientSelectors = content.match(/\\*|\\[[^\\]]*\\*=|:not\\(/g) || []
160 analysis.performance.selectorEfficiency = Math.max(0, 100 - (inefficientSelectors.length / analysis.selectors) * 100)
161
162 // Cacheability score
163 const dynamicContent = content.match(/calc\\(|var\\(|random/gi) || []
164 analysis.performance.cacheability = Math.max(0, 100 - (dynamicContent.length / analysis.declarations) * 50)
165 }
166
167 checkThresholds(analysis) {
168 const file = analysis.path
169
170 if (analysis.size > this.thresholds.size) {
171 this.report.warnings.push(\`\${file}: File size (\${this.formatSize(analysis.size)}) exceeds threshold (\${this.formatSize(this.thresholds.size)})\`)
172 }
173
174 if (analysis.gzipSize > this.thresholds.gzipSize) {
175 this.report.warnings.push(\`\${file}: Gzipped size (\${this.formatSize(analysis.gzipSize)}) exceeds threshold (\${this.formatSize(this.thresholds.gzipSize)})\`)
176 }
177
178 if (analysis.selectors > this.thresholds.selectors) {
179 this.report.warnings.push(\`\${file}: Selector count (\${analysis.selectors}) exceeds threshold (\${this.thresholds.selectors})\`)
180 }
181
182 if (analysis.rules > this.thresholds.rules) {
183 this.report.warnings.push(\`\${file}: Rule count (\${analysis.rules}) exceeds threshold (\${this.thresholds.rules})\`)
184 }
185
186 if (analysis.complexity.maxSpecificity > 100) {
187 this.report.warnings.push(\`\${file}: High specificity detected (\${analysis.complexity.maxSpecificity})\`)
188 }
189
190 if (analysis.performance.renderingCost > 20) {
191 this.report.warnings.push(\`\${file}: High rendering cost (\${analysis.performance.renderingCost.toFixed(1)}%)\`)
192 }
193 }
194
195 generateSummary() {
196 const files = Object.values(this.report.files)
197
198 this.report.summary = {
199 totalFiles: files.length,
200 totalSize: files.reduce((sum, f) => sum + f.size, 0),
201 totalGzipSize: files.reduce((sum, f) => sum + f.gzipSize, 0),
202 totalRules: files.reduce((sum, f) => sum + f.rules, 0),
203 totalSelectors: files.reduce((sum, f) => sum + f.selectors, 0),
204 avgRenderingCost: files.reduce((sum, f) => sum + f.performance.renderingCost, 0) / files.length,
205 avgSelectorEfficiency: files.reduce((sum, f) => sum + f.performance.selectorEfficiency, 0) / files.length
206 }
207 }
208
209 generateReport() {
210 const summary = this.report.summary
211
212 console.log('\\n📈 CSS Performance Report')
213 console.log('========================')
214 console.log(\`Total Files: \${summary.totalFiles}\`)
215 console.log(\`Total Size: \${this.formatSize(summary.totalSize)} (\${this.formatSize(summary.totalGzipSize)} gzipped)\`)
216 console.log(\`Total Rules: \${summary.totalRules.toLocaleString()}\`)
217 console.log(\`Total Selectors: \${summary.totalSelectors.toLocaleString()}\`)
218 console.log(\`Avg Rendering Cost: \${summary.avgRenderingCost.toFixed(1)}%\`)
219 console.log(\`Avg Selector Efficiency: \${summary.avgSelectorEfficiency.toFixed(1)}%\`)
220
221 if (this.report.warnings.length > 0) {
222 console.log('\\n⚠️ Warnings:')
223 this.report.warnings.forEach(warning => console.log(\` • \${warning}\`))
224 }
225
226 if (this.report.errors.length > 0) {
227 console.log('\\n❌ Errors:')
228 this.report.errors.forEach(error => console.log(\` • \${error}\`))
229 }
230
231 // Top 5 largest files
232 const largestFiles = Object.values(this.report.files)
233 .sort((a, b) => b.size - a.size)
234 .slice(0, 5)
235
236 console.log('\\n📁 Largest Files:')
237 largestFiles.forEach(file => {
238 console.log(\` \${file.path}: \${this.formatSize(file.size)} (\${this.formatSize(file.gzipSize)} gzipped)\`)
239 })
240 }
241
242 formatSize(bytes) {
243 if (bytes === 0) return '0 B'
244 const k = 1024
245 const sizes = ['B', 'KB', 'MB', 'GB']
246 const i = Math.floor(Math.log(bytes) / Math.log(k))
247 return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
248 }
249
250 async saveReport(outputPath = 'css-performance-report.json') {
251 await fs.writeFile(outputPath, JSON.stringify(this.report, null, 2))
252 console.log(\`\\n💾 Report saved to \${outputPath}\`)
253 }
254}
255
256// CLI usage
257if (require.main === module) {
258 const monitor = new CSSPerformanceMonitor({
259 baseDir: process.argv[2] || 'dist',
260 thresholds: {
261 size: 150 * 1024, // 150KB
262 gzipSize: 30 * 1024, // 30KB
263 selectors: 5000,
264 rules: 4000
265 }
266 })
267
268 monitor.analyzeCSS().then(report => {
269 monitor.saveReport()
270
271 // Exit with error if warnings exceed threshold
272 if (report.warnings.length > 10) {
273 console.log('\\n❌ Too many performance warnings detected')
274 process.exit(1)
275 }
276 })
277}
278
279module.exports = CSSPerformanceMonitorModern CSS development relies on automated workflows that handle
processing, optimization, testing, and deployment. These workflows
ensure consistent code quality and optimal performance across
different environments.
Learn about CI/CD CSS Pipeline.
1name: CSS Build and Quality Pipeline
2
3on:
4 push:
5 branches: [main, develop]
6 paths: ['src/styles/**', 'src/components/**/*.scss', 'postcss.config.js']
7 pull_request:
8 branches: [main]
9 paths: ['src/styles/**', 'src/components/**/*.scss']
10
11jobs:
12 # CSS Linting and Quality Checks
13 css-quality:
14 runs-on: ubuntu-latest
15 steps:
16 - uses: actions/checkout@v3
17
18 - name: Setup Node.js
19 uses: actions/setup-node@v3
20 with:
21 node-version: '18'
22 cache: 'npm'
23
24 - name: Install dependencies
25 run: npm ci
26
27 - name: Lint CSS/SCSS
28 run: |
29 npm run stylelint
30 npm run prettier:check
31
32 - name: Check CSS architecture compliance
33 run: npm run css:validate-architecture
34
35 - name: Audit design token usage
36 run: npm run tokens:audit
37
38 - name: Check accessibility compliance
39 run: npm run css:a11y-check
40
41 # CSS Build and Optimization
42 css-build:
43 runs-on: ubuntu-latest
44 needs: css-quality
45 strategy:
46 matrix:
47 environment: [development, staging, production]
48
49 steps:
50 - uses: actions/checkout@v3
51
52 - name: Setup Node.js
53 uses: actions/setup-node@v3
54 with:
55 node-version: '18'
56 cache: 'npm'
57
58 - name: Install dependencies
59 run: npm ci
60
61 - name: Build CSS for \$\{\{ matrix.environment \}\}
62 run: |
63 NODE_ENV=\$\{\{ matrix.environment \}\} npm run build:css
64 npm run css:analyze
65 env:
66 NODE_ENV: \$\{\{ matrix.environment \}\}
67
68 - name: Generate critical CSS
69 if: matrix.environment == 'production'
70 run: npm run build:critical-css
71
72 - name: Run performance analysis
73 run: |
74 npm run css:perf-analysis
75 npm run css:bundle-analysis
76
77 - name: Upload CSS artifacts
78 uses: actions/upload-artifact@v3
79 with:
80 name: css-build-\\\${{ matrix.environment }}
81 path: |
82 dist/css/
83 reports/css-performance.json
84 reports/css-bundle-analysis.json
85
86 # Visual Regression Testing
87 visual-regression:
88 runs-on: ubuntu-latest
89 needs: css-build
90 steps:
91 - uses: actions/checkout@v3
92
93 - name: Setup Node.js
94 uses: actions/setup-node@v3
95 with:
96 node-version: '18'
97 cache: 'npm'
98
99 - name: Install dependencies
100 run: npm ci
101
102 - name: Download CSS build artifacts
103 uses: actions/download-artifact@v3
104 with:
105 name: css-build-production
106 path: dist/
107
108 - name: Start development server
109 run: npm start &
110 env:
111 NODE_ENV: production
112
113 - name: Wait for server
114 run: npx wait-on http://localhost:3000
115
116 - name: Run visual regression tests
117 run: npm run test:visual
118 env:
119 PERCY_TOKEN: \\\${{ secrets.PERCY_TOKEN }}
120
121 - name: Upload screenshots
122 uses: actions/upload-artifact@v3
123 if: failure()
124 with:
125 name: visual-test-failures
126 path: cypress/screenshots/
127
128 # Performance Testing
129 performance-test:
130 runs-on: ubuntu-latest
131 needs: css-build
132 steps:
133 - uses: actions/checkout@v3
134
135 - name: Setup Node.js
136 uses: actions/setup-node@v3
137 with:
138 node-version: '18'
139 cache: 'npm'
140
141 - name: Install dependencies
142 run: npm ci
143
144 - name: Download CSS build artifacts
145 uses: actions/download-artifact@v3
146 with:
147 name: css-build-production
148 path: dist/
149
150 - name: Run Lighthouse CI
151 uses: treosh/lighthouse-ci-action@v9
152 with:
153 configPath: '.lighthouserc.json'
154 uploadArtifacts: true
155 temporaryPublicStorage: true
156 env:
157 LHCI_GITHUB_APP_TOKEN: \\\${{ secrets.LHCI_GITHUB_APP_TOKEN }}
158
159 - name: Run WebPageTest
160 run: npm run perf:webpagetest
161 env:
162 WPT_API_KEY: \\\${{ secrets.WPT_API_KEY }}
163
164 # Deployment
165 deploy:
166 runs-on: ubuntu-latest
167 needs: [css-quality, css-build, visual-regression, performance-test]
168 if: github.ref == 'refs/heads/main'
169
170 steps:
171 - uses: actions/checkout@v3
172
173 - name: Download production artifacts
174 uses: actions/download-artifact@v3
175 with:
176 name: css-build-production
177 path: dist/
178
179 - name: Deploy to CDN
180 run: |
181 npm run deploy:css-cdn
182 npm run purge:cdn-cache
183 env:
184 CDN_API_KEY: \\\${{ secrets.CDN_API_KEY }}
185 CDN_ZONE_ID: \\\${{ secrets.CDN_ZONE_ID }}
186
187 - name: Update performance monitoring
188 run: |
189 npm run perf:update-baseline
190 npm run perf:notify-teams
191 env:
192 MONITORING_WEBHOOK: \\\${{ secrets.MONITORING_WEBHOOK }}
193
194 - name: Generate deployment report
195 run: |
196 npm run report:deployment
197 npm run notify:slack
198 env:
199 SLACK_WEBHOOK: \\\${{ secrets.SLACK_WEBHOOK }}Learn about Package.json Scripts for CSS Workflow.
1{
2 "scripts": {
3 // Development
4 "dev": "vite --port 3000",
5 "dev:css": "sass src/styles:dist/css --watch --source-map",
6
7 // Building
8 "build": "npm run build:css && npm run build:js",
9 "build:css": "npm run css:compile && npm run css:optimize",
10 "build:critical-css": "node scripts/critical-css-build.js",
11
12 // CSS Processing
13 "css:compile": "sass src/styles:dist/css --style=compressed --no-source-map",
14 "css:optimize": "postcss dist/css/**/*.css --replace",
15 "css:purge": "purgecss --config purgecss.config.js",
16 "css:bundle-analysis": "webpack-bundle-analyzer dist/css-stats.json",
17
18 // Quality Assurance
19 "stylelint": "stylelint 'src/**/*.{css,scss}' --fix",
20 "css:validate-architecture": "node scripts/validate-css-architecture.js",
21 "css:a11y-check": "a11y-css src/styles",
22 "tokens:audit": "node scripts/audit-design-tokens.js",
23
24 // Performance Analysis
25 "css:perf-analysis": "node scripts/css-performance-monitor.js dist",
26 "css:analyze": "npm run css:perf-analysis && npm run css:bundle-analysis",
27
28 // Testing
29 "test:css": "jest --testPathPattern=css",
30 "test:visual": "percy exec -- cypress run",
31 "test:performance": "lighthouse-ci",
32
33 // Deployment
34 "deploy:css-cdn": "aws s3 sync dist/css/ s3://cdn-bucket/css/ --delete",
35 "purge:cdn-cache": "curl -X POST 'https://api.cloudflare.com/client/v4/zones/$CDN_ZONE_ID/purge_cache'",
36
37 // Monitoring and Reporting
38 "perf:update-baseline": "node scripts/update-performance-baseline.js",
39 "perf:webpagetest": "node scripts/webpagetest-runner.js",
40 "report:deployment": "node scripts/generate-deployment-report.js",
41
42 // Utilities
43 "css:stats": "node scripts/css-statistics.js",
44 "css:unused": "uncss dist/index.html --stylesheets dist/css/main.css",
45 "css:diff": "node scripts/css-diff-checker.js",
46 "prettier:check": "prettier --check 'src/**/*.{css,scss}'",
47 "prettier:fix": "prettier --write 'src/**/*.{css,scss}'"
48 },
49
50 "devDependencies": {
51 // Core build tools
52 "sass": "^1.62.0",
53 "postcss": "^8.4.21",
54 "postcss-cli": "^10.1.0",
55 "vite": "^4.3.0",
56
57 // PostCSS plugins
58 "autoprefixer": "^10.4.14",
59 "postcss-import": "^15.1.0",
60 "postcss-nested": "^6.0.1",
61 "postcss-custom-properties": "^13.1.5",
62 "postcss-preset-env": "^8.3.0",
63 "cssnano": "^6.0.0",
64
65 // Quality tools
66 "stylelint": "^15.6.0",
67 "stylelint-config-standard-scss": "^9.0.0",
68 "stylelint-scss": "^4.6.0",
69 "prettier": "^2.8.8",
70
71 // Performance tools
72 "@fullhuman/postcss-purgecss": "^5.0.0",
73 "critical": "^6.1.0",
74 "webpack-bundle-analyzer": "^4.8.0",
75 "lighthouse-ci": "^0.12.0",
76 "gzip-size": "^7.0.0",
77 "brotli-size": "^4.0.0",
78
79 // Testing
80 "cypress": "^12.12.0",
81 "@percy/cli": "^1.26.0",
82 "@percy/cypress": "^3.1.2",
83
84 // Utilities
85 "clean-css": "^5.3.2",
86 "uncss": "^0.17.3",
87 "a11y-css": "^1.1.0"
88 }
89}Learn about Best Practices and Production Considerations.
Use design tokens and variables for consistent values
Implement modular architecture with clear import hierarchy
Optimize for build-time performance with efficient processing
Configure proper source maps for development debugging
Implement automated quality checks and validation
Use critical CSS extraction for improved page load performance
Monitor bundle size and performance metrics continuously
Enable CSS minification and compression
Implement proper cache headers and versioning
Extract and inline critical CSS for above-fold content
Remove unused CSS with PurgeCSS or similar tools
Optimize images and assets referenced in CSS
Test across target browsers and devices
Monitor performance metrics and Core Web Vitals
Set up automated performance budgets and alerts
1// Production-ready CSS optimization configuration
2const path = require('path')
3
4module.exports = {
5 // CSS optimization pipeline
6 optimization: {
7 // Remove unused CSS
8 purge: {
9 content: ['./src/**/*.{js,jsx,ts,tsx,html}'],
10 safelist: [
11 // Dynamic classes that shouldn't be purged
12 /^(hover:|focus:|active:|disabled:)/,
13 /^(sm:|md:|lg:|xl:)/,
14 /^(animation-|transition-)/,
15 ]
16 },
17
18 // Minification settings
19 minify: {
20 preset: 'advanced',
21 discardComments: { removeAll: true },
22 normalizeWhitespace: true,
23 colormin: true,
24 convertValues: true,
25 discardDuplicates: true,
26 mergeRules: true,
27 minifyFontValues: true,
28 minifyParams: true,
29 minifySelectors: true,
30 normalizeCharset: true,
31 normalizeDisplayValues: true,
32 normalizePositions: true,
33 normalizeRepeatStyle: true,
34 normalizeTimingFunctions: true,
35 normalizeUnicode: true,
36 normalizeUrl: true,
37 orderedValues: true,
38 reduceIdents: false, // Keep animation names readable
39 reduceInitial: true,
40 reduceTransforms: true,
41 svgo: true,
42 uniqueSelectors: true
43 },
44
45 // Critical CSS settings
46 critical: {
47 dimensions: [
48 { width: 375, height: 667 }, // Mobile
49 { width: 768, height: 1024 }, // Tablet
50 { width: 1024, height: 768 }, // Desktop
51 { width: 1200, height: 800 }, // Large desktop
52 ],
53 penthouse: {
54 timeout: 60000,
55 pageLoadSkipTimeout: 10000,
56 renderWaitTime: 1000,
57 blockJSRequests: true
58 },
59 inline: true,
60 extract: true,
61 minify: true
62 },
63
64 // Performance budgets
65 budgets: {
66 maxSize: 150 * 1024, // 150KB
67 maxGzipSize: 30 * 1024, // 30KB gzipped
68 maxRules: 4000,
69 maxSelectors: 5000,
70 warningThreshold: 0.8 // Warn at 80% of budget
71 }
72 },
73
74 // Asset optimization
75 assets: {
76 // Image optimization in CSS
77 images: {
78 webp: true,
79 avif: true,
80 quality: 85,
81 progressive: true
82 },
83
84 // Font optimization
85 fonts: {
86 preload: ['woff2'],
87 display: 'swap',
88 subset: true
89 }
90 },
91
92 // CDN and caching
93 deployment: {
94 cdn: {
95 url: 'https://cdn.example.com',
96 headers: {
97 'Cache-Control': 'public, max-age=31536000, immutable',
98 'Content-Encoding': 'br, gzip'
99 }
100 },
101
102 // Asset versioning
103 versioning: {
104 strategy: 'content-hash',
105 manifest: true
106 }
107 },
108
109 // Monitoring and analytics
110 monitoring: {
111 performanceMetrics: true,
112 errorReporting: true,
113 usageAnalytics: false // Privacy-first approach
114 }
115}CSS preprocessors and build tools are essential for modern web development,
providing the foundation for maintainable, performant, and scalable stylesheets.
The key is selecting the right combination of tools and configuring them
appropriately for your project's needs and team workflow.
Choose preprocessing tools based on project complexity and team experience
Implement comprehensive build pipelines with quality gates
Configure automated optimization and performance monitoring
Establish clear workflows for development, testing, and deployment
Monitor performance metrics and maintain optimization budgets
Plan for long-term maintenance and tool evolution
Next, we'll explore component-based CSS systems and how to build
scalable design systems that integrate with modern JavaScript frameworks,
including CSS-in-JS approaches and utility-first methodologies.
Your progress is saved in this browser, on this device only.