Master modern CSS-in-JS workflows including styled-components, emotion, and advanced patterns for component-driven styling with TypeScript integration.
CSS-in-JS represents a paradigm shift in how we approach styling in modern web applications. By colocating styles with components and leveraging JavaScript's power, CSS-in-JS solutions enable dynamic styling, automatic vendor prefixing, and powerful theming systems that traditional CSS struggles to achieve.
This comprehensive lesson explores the modern CSS-in-JS ecosystem, from foundational concepts to advanced patterns. You'll learn to build scalable, maintainable, and performant styling systems that leverage the best of both JavaScript and CSS worlds.
CSS-in-JS is a styling technique where CSS is composed using JavaScript. This approach enables dynamic styling, better maintainability, and powerful developer experiences.
Core Concepts:
1. Runtime vs Build-time Processing:
Runtime CSS-in-JS generates styles during application execution, while build-time solutions extract CSS at compile time for better performance.
2. Component Scoping:
Automatically scoped styles prevent global namespace pollution and ensure style isolation between components.
3. JavaScript Integration:
Direct access to JavaScript variables, functions, and state enables dynamic styling based on props, themes, and application state.
Popular Libraries:
styled-components (Runtime):
1const Button = styled.button`
2 background: ${props => props.primary ? 'blue' : 'white'};
3 color: ${props => props.primary ? 'white' : 'blue'};
4 padding: 12px 24px;
5 border: 2px solid blue;
6 border-radius: 4px;
7 cursor: pointer;
8
9 &:hover {
10 background: ${props => props.primary ? 'darkblue' : 'lightblue'};
11 }
12`;
13
14// Usage
15<Button primary>Primary Button</Button>
16<Button>Secondary Button</Button>Emotion (Runtime/Build-time):
1import { css } from '@emotion/react'
2
3const buttonStyles = css`
4 background: hotpink;
5 color: white;
6 padding: 12px 24px;
7 border: none;
8 border-radius: 4px;
9
10 &:hover {
11 background: darkpink;
12 }
13`;
14
15<button css={buttonStyles}>Emotion Button</button>Advantages:
Considerations:
Modern CSS-in-JS libraries provide sophisticated theming capabilities that go far beyond simple color swapping.
Theme Provider Pattern:
1import { ThemeProvider } from 'styled-components';
2
3const theme = {
4 colors: {
5 primary: {
6 50: '#eff6ff',
7 500: '#3b82f6',
8 900: '#1e3a8a',
9 },
10 semantic: {
11 success: '#10b981',
12 warning: '#f59e0b',
13 error: '#ef4444',
14 }
15 },
16 spacing: {
17 xs: '0.5rem',
18 sm: '0.75rem',
19 md: '1rem',
20 lg: '1.5rem',
21 xl: '2rem',
22 },
23 typography: {
24 fontFamily: {
25 sans: ['Inter', 'system-ui', 'sans-serif'],
26 mono: ['JetBrains Mono', 'monospace'],
27 },
28 fontSize: {
29 sm: '0.875rem',
30 base: '1rem',
31 lg: '1.125rem',
32 xl: '1.25rem',
33 }
34 },
35 breakpoints: {
36 sm: '640px',
37 md: '768px',
38 lg: '1024px',
39 xl: '1280px',
40 }
41};
42
43function App() {
44 return (
45 <ThemeProvider theme={theme}>
46 <YourComponents />
47 </ThemeProvider>
48 );
49}Theme-aware Components:
1const Card = styled.div`
2 background: ${props => props.theme.colors.primary[50]};
3 padding: ${props => props.theme.spacing.lg};
4 border-radius: ${props => props.theme.radii.md};
5 box-shadow: 0 4px 6px ${props => props.theme.colors.primary[900]}20;
6
7 @media (min-width: ${props => props.theme.breakpoints.md}) {
8 padding: ${props => props.theme.spacing.xl};
9 }
10`;Dynamic Theme Switching:
1const ThemeToggle = () => {
2 const [theme, setTheme] = useState('light');
3
4 const themes = {
5 light: { ... },
6 dark: { ... },
7 highContrast: { ... }
8 };
9
10 return (
11 <ThemeProvider theme={themes[theme]}>
12 <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
13 Toggle Theme
14 </button>
15 <App />
16 </ThemeProvider>
17 );
18};TypeScript Theme Integration:
1declare module 'styled-components' {
2 export interface DefaultTheme {
3 colors: {
4 primary: Record<number, string>;
5 semantic: Record<string, string>;
6 };
7 spacing: Record<string, string>;
8 typography: {
9 fontFamily: Record<string, string[]>;
10 fontSize: Record<string, string>;
11 };
12 breakpoints: Record<string, string>;
13 }
14}CSS-in-JS performance optimization requires understanding both runtime characteristics and build-time optimizations.
Runtime Performance Strategies:
1. Component Memoization:
1import { memo } from 'react';
2
3// Prevent unnecessary re-renders
4const StyledButton = memo(styled.button`
5 background: ${props => props.theme.colors.primary};
6 // ... other styles
7`);2. Style Caching:
1// Use useMemo for expensive style calculations
2const ExpensiveComponent = ({ data }) => {
3 const styles = useMemo(() => css`
4 // Complex calculations based on data
5 transform: translateX(${data.position * 10}px);
6 background: linear-gradient(
7 ${data.angle}deg,
8 ${data.colors.map(c => c.hex).join(', ')}
9 );
10 `, [data.position, data.angle, data.colors]);
11
12 return <div css={styles}>Content</div>;
13};3. Conditional Styling:
1// Avoid unnecessary style generation
2const ConditionalButton = styled.button`
3 ${props => props.variant === 'primary' && css`
4 background: blue;
5 color: white;
6 `}
7
8 ${props => props.size === 'large' && css`
9 padding: 16px 32px;
10 font-size: 18px;
11 `}
12`;Build-time Optimizations:
1. Babel Plugin Configuration:
1// babel.config.js
2{
3 "plugins": [
4 ["styled-components", {
5 "ssr": true,
6 "displayName": true,
7 "preprocess": false,
8 "minify": true,
9 "transpileTemplateLiterals": true
10 }]
11 ]
12}2. Critical CSS Extraction:
1// Server-side rendering with critical CSS
2import { ServerStyleSheet } from 'styled-components';
3
4const sheet = new ServerStyleSheet();
5const html = renderToString(sheet.collectStyles(<App />));
6const styleTags = sheet.getStyleTags();3. Bundle Analysis:
1# Analyze bundle impact
2npm install --save-dev webpack-bundle-analyzer
3npm run build && npx webpack-bundle-analyzer build/static/js/*.jsPerformance Monitoring:
A comprehensive example demonstrating modern CSS-in-JS patterns including theming, variants, and performance optimizations.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>CSS-in-JS Modern Workflows</title>
7 <style>
8 /* Base styles for our CSS-in-JS simulation */
9 body {
10 margin: 0;
11 font-family: 'Inter', system-ui, -apple-system, sans-serif;
12 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
13 min-height: 100vh;
14 color: #333;
15 }
16
17 .container {
18 max-width: 1200px;
19 margin: 0 auto;
20 padding: 2rem;
21 }
22
23 .header {
24 text-align: center;
25 margin-bottom: 3rem;
26 }
27
28 .title {
29 font-size: 3rem;
30 font-weight: 800;
31 background: linear-gradient(135deg, #667eea, #764ba2);
32 -webkit-background-clip: text;
33 -webkit-text-fill-color: transparent;
34 background-clip: text;
35 margin-bottom: 1rem;
36 }
37
38 .subtitle {
39 font-size: 1.2rem;
40 color: #64748b;
41 max-width: 600px;
42 margin: 0 auto;
43 line-height: 1.6;
44 }
45
46 /* Theme System Simulation */
47 :root {
48 --color-primary-50: #eff6ff;
49 --color-primary-100: #dbeafe;
50 --color-primary-500: #3b82f6;
51 --color-primary-600: #2563eb;
52 --color-primary-900: #1e3a8a;
53
54 --spacing-xs: 0.5rem;
55 --spacing-sm: 0.75rem;
56 --spacing-md: 1rem;
57 --spacing-lg: 1.5rem;
58 --spacing-xl: 2rem;
59 --spacing-2xl: 3rem;
60
61 --radius-sm: 0.25rem;
62 --radius-md: 0.5rem;
63 --radius-lg: 0.75rem;
64 --radius-xl: 1rem;
65 }
66
67 [data-theme="dark"] {
68 --color-primary-50: #1e3a8a;
69 --color-primary-100: #1e40af;
70 --color-primary-500: #60a5fa;
71 --color-primary-600: #3b82f6;
72 --color-primary-900: #dbeafe;
73 }
74
75 /* Component System */
76 .component-showcase {
77 display: grid;
78 grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
79 gap: var(--spacing-xl);
80 margin-bottom: var(--spacing-2xl);
81 }
82
83 .card {
84 background: white;
85 border-radius: var(--radius-xl);
86 padding: var(--spacing-xl);
87 box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
88 0 10px 10px -5px rgba(0, 0, 0, 0.04);
89 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
90 }
91
92 .card:hover {
93 transform: translateY(-8px);
94 box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
95 }
96
97 .card-header {
98 margin-bottom: var(--spacing-lg);
99 }
100
101 .card-title {
102 font-size: 1.5rem;
103 font-weight: 700;
104 color: var(--color-primary-900);
105 margin-bottom: var(--spacing-sm);
106 }
107
108 .card-description {
109 color: #64748b;
110 line-height: 1.6;
111 }
112
113 /* Button System with Variants */
114 .btn {
115 display: inline-flex;
116 align-items: center;
117 justify-content: center;
118 font-weight: 600;
119 border-radius: var(--radius-md);
120 transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
121 border: none;
122 cursor: pointer;
123 text-decoration: none;
124 font-family: inherit;
125 margin: 0.25rem;
126 }
127
128 /* Button Sizes */
129 .btn--sm {
130 padding: var(--spacing-xs) var(--spacing-md);
131 font-size: 0.875rem;
132 }
133
134 .btn--md {
135 padding: var(--spacing-sm) var(--spacing-lg);
136 font-size: 1rem;
137 }
138
139 .btn--lg {
140 padding: var(--spacing-md) var(--spacing-xl);
141 font-size: 1.125rem;
142 }
143
144 /* Button Variants */
145 .btn--primary {
146 background: var(--color-primary-500);
147 color: white;
148 }
149
150 .btn--primary:hover {
151 background: var(--color-primary-600);
152 transform: translateY(-2px);
153 box-shadow: 0 10px 20px rgba(59, 130, 246, 0.3);
154 }
155
156 .btn--secondary {
157 background: var(--color-primary-100);
158 color: var(--color-primary-900);
159 }
160
161 .btn--secondary:hover {
162 background: var(--color-primary-50);
163 transform: translateY(-2px);
164 }
165
166 .btn--outline {
167 background: transparent;
168 color: var(--color-primary-500);
169 border: 2px solid var(--color-primary-500);
170 }
171
172 .btn--outline:hover {
173 background: var(--color-primary-500);
174 color: white;
175 transform: translateY(-2px);
176 }
177
178 .btn--ghost {
179 background: transparent;
180 color: var(--color-primary-500);
181 }
182
183 .btn--ghost:hover {
184 background: var(--color-primary-50);
185 transform: translateY(-2px);
186 }
187
188 /* Advanced Components */
189 .input-group {
190 margin-bottom: var(--spacing-lg);
191 }
192
193 .label {
194 display: block;
195 font-weight: 600;
196 color: var(--color-primary-900);
197 margin-bottom: var(--spacing-xs);
198 }
199
200 .input {
201 width: 100%;
202 padding: var(--spacing-sm) var(--spacing-md);
203 border: 2px solid #e2e8f0;
204 border-radius: var(--radius-md);
205 font-size: 1rem;
206 transition: all 0.2s;
207 background: white;
208 }
209
210 .input:focus {
211 outline: none;
212 border-color: var(--color-primary-500);
213 box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
214 }
215
216 .progress-bar {
217 width: 100%;
218 height: 12px;
219 background: #e2e8f0;
220 border-radius: var(--radius-lg);
221 overflow: hidden;
222 margin-bottom: var(--spacing-lg);
223 }
224
225 .progress-fill {
226 height: 100%;
227 background: linear-gradient(90deg, var(--color-primary-500), var(--color-primary-600));
228 transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
229 position: relative;
230 }
231
232 .progress-fill::after {
233 content: '';
234 position: absolute;
235 top: 0;
236 left: 0;
237 right: 0;
238 bottom: 0;
239 background: linear-gradient(
240 90deg,
241 transparent,
242 rgba(255, 255, 255, 0.3),
243 transparent
244 );
245 animation: shimmer 2s infinite;
246 }
247
248 @keyframes shimmer {
249 0% { transform: translateX(-100%); }
250 100% { transform: translateX(100%); }
251 }
252
253 /* Theme Controls */
254 .theme-controls {
255 position: fixed;
256 top: 2rem;
257 right: 2rem;
258 display: flex;
259 gap: var(--spacing-sm);
260 z-index: 100;
261 }
262
263 .theme-btn {
264 width: 48px;
265 height: 48px;
266 border-radius: 50%;
267 border: 3px solid white;
268 cursor: pointer;
269 transition: all 0.3s;
270 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
271 }
272
273 .theme-btn:hover {
274 transform: scale(1.1);
275 }
276
277 .theme-btn.active {
278 transform: scale(1.15);
279 box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.3);
280 }
281
282 .theme-light { background: linear-gradient(135deg, #667eea, #764ba2); }
283 .theme-dark { background: linear-gradient(135deg, #2d3748, #1a202c); }
284 .theme-custom { background: linear-gradient(135deg, #ed64a6, #9f7aea); }
285
286 /* Responsive Design */
287 @media (max-width: 768px) {
288 .container {
289 padding: 1rem;
290 }
291
292 .title {
293 font-size: 2rem;
294 }
295
296 .component-showcase {
297 grid-template-columns: 1fr;
298 gap: var(--spacing-lg);
299 }
300
301 .theme-controls {
302 position: static;
303 justify-content: center;
304 margin: var(--spacing-lg) 0;
305 }
306 }
307
308 /* Advanced Animations */
309 @keyframes fadeInUp {
310 from {
311 opacity: 0;
312 transform: translateY(30px);
313 }
314 to {
315 opacity: 1;
316 transform: translateY(0);
317 }
318 }
319
320 .animate-in {
321 animation: fadeInUp 0.6s ease-out forwards;
322 }
323
324 .stagger-1 { animation-delay: 0.1s; }
325 .stagger-2 { animation-delay: 0.2s; }
326 .stagger-3 { animation-delay: 0.3s; }
327 </style>
328</head>
329<body>
330 <div class="theme-controls">
331 <button class="theme-btn theme-light active" onclick="setTheme('light')" title="Light Theme"></button>
332 <button class="theme-btn theme-dark" onclick="setTheme('dark')" title="Dark Theme"></button>
333 <button class="theme-btn theme-custom" onclick="setTheme('custom')" title="Custom Theme"></button>
334 </div>
335
336 <div class="container">
337 <div class="header">
338 <h1 class="title animate-in">CSS-in-JS Workflows</h1>
339 <p class="subtitle animate-in stagger-1">
340 Modern component-driven styling with dynamic theming,
341 advanced variants, and performance optimizations
342 </p>
343 </div>
344
345 <div class="component-showcase">
346 <div class="card animate-in stagger-1">
347 <div class="card-header">
348 <h3 class="card-title">Button System</h3>
349 <p class="card-description">
350 Comprehensive button variants with size modifiers and interactive states
351 </p>
352 </div>
353
354 <div class="button-grid">
355 <button class="btn btn--primary btn--sm">Primary Small</button>
356 <button class="btn btn--primary btn--md">Primary Medium</button>
357 <button class="btn btn--primary btn--lg">Primary Large</button>
358
359 <button class="btn btn--secondary btn--md">Secondary</button>
360 <button class="btn btn--outline btn--md">Outline</button>
361 <button class="btn btn--ghost btn--md">Ghost</button>
362 </div>
363 </div>
364
365 <div class="card animate-in stagger-2">
366 <div class="card-header">
367 <h3 class="card-title">Form Components</h3>
368 <p class="card-description">
369 Styled form elements with focus states and validation
370 </p>
371 </div>
372
373 <form>
374 <div class="input-group">
375 <label class="label" for="email">Email Address</label>
376 <input class="input" type="email" id="email" placeholder="[email protected]">
377 </div>
378
379 <div class="input-group">
380 <label class="label" for="message">Message</label>
381 <textarea class="input" id="message" rows="3" placeholder="Your message here..."></textarea>
382 </div>
383
384 <button class="btn btn--primary btn--lg" type="submit">
385 Send Message
386 </button>
387 </form>
388 </div>
389
390 <div class="card animate-in stagger-3">
391 <div class="card-header">
392 <h3 class="card-title">Progress Indicators</h3>
393 <p class="card-description">
394 Animated progress bars with smooth transitions
395 </p>
396 </div>
397
398 <div>
399 <div class="progress-bar">
400 <div class="progress-fill" style="width: 75%" id="progress1"></div>
401 </div>
402
403 <div class="progress-bar">
404 <div class="progress-fill" style="width: 45%" id="progress2"></div>
405 </div>
406
407 <div class="progress-bar">
408 <div class="progress-fill" style="width: 90%" id="progress3"></div>
409 </div>
410
411 <button class="btn btn--outline btn--md" onclick="animateProgress()">
412 Animate Progress
413 </button>
414 </div>
415 </div>
416 </div>
417
418 <div class="card">
419 <div class="card-header">
420 <h3 class="card-title">🎨 CSS-in-JS Features Demonstrated</h3>
421 <p class="card-description">
422 This example showcases key CSS-in-JS concepts adapted to vanilla CSS
423 </p>
424 </div>
425
426 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;">
427 <div>
428 <h4 style="color: var(--color-primary-600); margin-bottom: 0.5rem;">✅ Design Tokens</h4>
429 <ul style="margin: 0; padding-left: 1rem; color: #64748b;">
430 <li>CSS custom properties for theming</li>
431 <li>Systematic spacing scales</li>
432 <li>Consistent color palettes</li>
433 </ul>
434 </div>
435
436 <div>
437 <h4 style="color: var(--color-primary-600); margin-bottom: 0.5rem;">✅ Component Variants</h4>
438 <ul style="margin: 0; padding-left: 1rem; color: #64748b;">
439 <li>Size modifiers (sm, md, lg)</li>
440 <li>Style variants (primary, secondary)</li>
441 <li>State management (hover, focus)</li>
442 </ul>
443 </div>
444
445 <div>
446 <h4 style="color: var(--color-primary-600); margin-bottom: 0.5rem;">✅ Dynamic Theming</h4>
447 <ul style="margin: 0; padding-left: 1rem; color: #64748b;">
448 <li>Runtime theme switching</li>
449 <li>Scoped theme variables</li>
450 <li>Smooth transitions</li>
451 </ul>
452 </div>
453
454 <div>
455 <h4 style="color: var(--color-primary-600); margin-bottom: 0.5rem;">✅ Performance Optimizations</h4>
456 <ul style="margin: 0; padding-left: 1rem; color: #64748b;">
457 <li>Efficient CSS custom properties</li>
458 <li>Hardware-accelerated animations</li>
459 <li>Minimal style recalculation</li>
460 </ul>
461 </div>
462 </div>
463 </div>
464 </div>
465
466 <script>
467 // Theme Management
468 let currentTheme = 'light';
469
470 const themes = {
471 light: {
472 '--color-primary-50': '#eff6ff',
473 '--color-primary-100': '#dbeafe',
474 '--color-primary-500': '#3b82f6',
475 '--color-primary-600': '#2563eb',
476 '--color-primary-900': '#1e3a8a'
477 },
478 dark: {
479 '--color-primary-50': '#1e3a8a',
480 '--color-primary-100': '#1e40af',
481 '--color-primary-500': '#60a5fa',
482 '--color-primary-600': '#3b82f6',
483 '--color-primary-900': '#dbeafe'
484 },
485 custom: {
486 '--color-primary-50': '#fdf2f8',
487 '--color-primary-100': '#fce7f3',
488 '--color-primary-500': '#ec4899',
489 '--color-primary-600': '#db2777',
490 '--color-primary-900': '#831843'
491 }
492 };
493
494 function setTheme(themeName) {
495 currentTheme = themeName;
496 const theme = themes[themeName];
497 const root = document.documentElement;
498
499 // Apply theme variables
500 Object.entries(theme).forEach(([property, value]) => {
501 root.style.setProperty(property, value);
502 });
503
504 // Update active theme button
505 document.querySelectorAll('.theme-btn').forEach(btn => {
506 btn.classList.remove('active');
507 });
508 document.querySelector(`.theme-${themeName}`).classList.add('active');
509
510 // Store theme preference
511 localStorage.setItem('preferred-theme', themeName);
512 }
513
514 // Progress Animation
515 function animateProgress() {
516 const progress1 = document.getElementById('progress1');
517 const progress2 = document.getElementById('progress2');
518 const progress3 = document.getElementById('progress3');
519
520 // Reset progress
521 progress1.style.width = '0%';
522 progress2.style.width = '0%';
523 progress3.style.width = '0%';
524
525 // Animate with staggered delays
526 setTimeout(() => progress1.style.width = '75%', 100);
527 setTimeout(() => progress2.style.width = '45%', 300);
528 setTimeout(() => progress3.style.width = '90%', 500);
529 }
530
531 // Initialize theme from localStorage
532 const savedTheme = localStorage.getItem('preferred-theme') || 'light';
533 setTheme(savedTheme);
534
535 // Intersection Observer for animations
536 const observerOptions = {
537 threshold: 0.1,
538 rootMargin: '0px 0px -50px 0px'
539 };
540
541 const observer = new IntersectionObserver((entries) => {
542 entries.forEach(entry => {
543 if (entry.isIntersecting) {
544 entry.target.classList.add('animate-in');
545 }
546 });
547 }, observerOptions);
548
549 // Observe all cards for animation
550 document.querySelectorAll('.card').forEach(card => {
551 observer.observe(card);
552 });
553
554 // Auto-animate progress on load
555 setTimeout(() => {
556 animateProgress();
557 }, 2000);
558 </script>
559</body>
560</html>This comprehensive example demonstrates modern CSS-in-JS workflows adapted to vanilla CSS and JavaScript:
🎯 CSS-in-JS Concepts Demonstrated:
1. Design Token System:
2. Component-Based Architecture:
3. Dynamic Theming:
4. Advanced Styling Patterns:
5. Performance Optimizations:
6. Interactive Features:
7. Modern Development Patterns:
This example bridges the gap between CSS-in-JS concepts and traditional CSS, demonstrating how modern styling approaches can be implemented across different technologies while maintaining consistency and performance.
Excellent work mastering CSS-in-JS modern workflows! You now understand component-driven styling, advanced theming systems, and performance optimization strategies. In our next lesson, we'll explore Advanced Visual Effects & Filters, where you'll learn to create stunning visual effects using CSS filters, backdrop-filter, blend modes, and advanced animation techniques. Get ready to push the boundaries of what's possible with modern CSS!
Your progress is saved in this browser, on this device only.