0
# CSS Modules Integration
1
2
CSS Modules support with automatic export generation, named exports, and lazy loading integration.
3
4
## Capabilities
5
6
### CSS Modules Exports
7
8
When CSS Modules are enabled (via css-loader), style-loader automatically generates JavaScript exports for CSS class names.
9
10
```javascript { .api }
11
/**
12
* CSS Modules exports interface
13
* Default export contains all class name mappings
14
* Named exports provide individual class access
15
*/
16
interface CSSModuleExports {
17
[className: string]: string; // Named exports for each class
18
default?: Record<string, string>; // Default export with all classes
19
}
20
```
21
22
**Usage Example:**
23
24
```css
25
/* component.module.css */
26
.header {
27
color: blue;
28
font-size: 24px;
29
}
30
31
.button {
32
background: #007bff;
33
border: none;
34
padding: 8px 16px;
35
}
36
37
.active {
38
background: #28a745;
39
}
40
```
41
42
```javascript
43
// webpack.config.js
44
module.exports = {
45
module: {
46
rules: [
47
{
48
test: /\.module\.css$/i,
49
use: [
50
"style-loader",
51
{
52
loader: "css-loader",
53
options: { modules: true }
54
}
55
],
56
},
57
],
58
},
59
};
60
61
// component.js - Default import
62
import styles from "./component.module.css";
63
console.log(styles.header); // "component_header__1a2b3c"
64
console.log(styles.button); // "component_button__4d5e6f"
65
66
// component.js - Named imports
67
import { header, button, active } from "./component.module.css";
68
const element = document.createElement("div");
69
element.className = `${header} ${button} ${active}`;
70
```
71
72
### Lazy CSS Modules
73
74
When using lazy injection types, CSS Modules exports are extended with `use()` and `unuse()` methods.
75
76
```javascript { .api }
77
/**
78
* Lazy CSS Modules exports interface
79
* Extends standard CSS Modules with lazy loading controls
80
*/
81
interface LazyCSSModuleExports extends CSSModuleExports {
82
/**
83
* Activate lazy-loaded styles
84
* @param insertOptions - Optional runtime insertion configuration
85
* @returns Self for chaining
86
*/
87
use(insertOptions?: InsertOptions): LazyCSSModuleExports;
88
89
/**
90
* Deactivate lazy-loaded styles
91
* Uses reference counting - styles removed when count reaches 0
92
*/
93
unuse(): void;
94
95
/** CSS Modules class name mappings (also available as named exports) */
96
locals?: Record<string, string>;
97
}
98
99
interface InsertOptions {
100
insertInto?: HTMLElement;
101
insertAt?: "top" | "bottom" | number;
102
}
103
```
104
105
**Usage Example:**
106
107
```css
108
/* modal.lazy.css */
109
.modal {
110
position: fixed;
111
top: 50%;
112
left: 50%;
113
transform: translate(-50%, -50%);
114
background: white;
115
border-radius: 8px;
116
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
117
}
118
119
.overlay {
120
position: fixed;
121
top: 0;
122
left: 0;
123
width: 100%;
124
height: 100%;
125
background: rgba(0, 0, 0, 0.5);
126
}
127
```
128
129
```javascript
130
// webpack.config.js
131
module.exports = {
132
module: {
133
rules: [
134
{
135
test: /\.lazy\.css$/i,
136
use: [
137
{
138
loader: "style-loader",
139
options: { injectType: "lazyStyleTag" }
140
},
141
{
142
loader: "css-loader",
143
options: { modules: true }
144
}
145
],
146
},
147
],
148
},
149
};
150
151
// Modal component
152
import modalStyles from "./modal.lazy.css";
153
154
class Modal {
155
constructor() {
156
this.isOpen = false;
157
}
158
159
open() {
160
if (!this.isOpen) {
161
// Activate modal styles when opening
162
modalStyles.use();
163
164
// Create modal elements with CSS module classes
165
this.overlay = document.createElement("div");
166
this.overlay.className = modalStyles.overlay;
167
168
this.modal = document.createElement("div");
169
this.modal.className = modalStyles.modal;
170
171
document.body.appendChild(this.overlay);
172
document.body.appendChild(this.modal);
173
174
this.isOpen = true;
175
}
176
}
177
178
close() {
179
if (this.isOpen) {
180
// Remove modal elements
181
document.body.removeChild(this.overlay);
182
document.body.removeChild(this.modal);
183
184
// Deactivate modal styles when closing
185
modalStyles.unuse();
186
187
this.isOpen = false;
188
}
189
}
190
}
191
```
192
193
### Named Exports with Lazy Loading
194
195
Named exports work seamlessly with lazy loading capabilities.
196
197
```javascript { .api }
198
// Both default and named imports support lazy loading
199
import lazyStyles, { className1, className2 } from "./styles.lazy.css";
200
201
// All these references point to the same lazy loading controls
202
lazyStyles.use(); // Activate styles
203
className1.use(); // Same as above - all refer to same module
204
lazyStyles.unuse(); // Deactivate styles
205
```
206
207
**Usage Example:**
208
209
```javascript
210
// theme-switcher.js
211
import lightTheme, { header, button } from "./light-theme.lazy.css";
212
import darkTheme from "./dark-theme.lazy.css";
213
214
class ThemeSwitcher {
215
constructor() {
216
this.currentTheme = "light";
217
lightTheme.use(); // Activate default theme
218
}
219
220
switchTheme(theme) {
221
// Deactivate current theme
222
if (this.currentTheme === "light") {
223
lightTheme.unuse();
224
} else {
225
darkTheme.unuse();
226
}
227
228
// Activate new theme
229
if (theme === "light") {
230
lightTheme.use();
231
} else {
232
darkTheme.use();
233
}
234
235
this.currentTheme = theme;
236
}
237
238
applyClasses(element) {
239
// Named exports are available even when styles are lazy
240
if (this.currentTheme === "light") {
241
element.className = `${header} ${button}`;
242
} else {
243
element.className = `${darkTheme.header} ${darkTheme.button}`;
244
}
245
}
246
}
247
```
248
249
### Reference Counting
250
251
Lazy CSS modules use reference counting to manage activation/deactivation safely.
252
253
```javascript { .api }
254
/**
255
* Reference counting behavior for lazy CSS modules
256
* - use() increments reference count and activates styles on first call
257
* - unuse() decrements reference count and deactivates styles when count reaches 0
258
* - Multiple use() calls require matching unuse() calls
259
*/
260
```
261
262
**Usage Example:**
263
264
```javascript
265
import styles from "./component.lazy.css";
266
267
// Component A uses styles
268
styles.use(); // Ref count: 1, styles activated
269
270
// Component B also uses the same styles
271
styles.use(); // Ref count: 2, styles remain active
272
273
// Component A unmounts
274
styles.unuse(); // Ref count: 1, styles remain active
275
276
// Component B unmounts
277
styles.unuse(); // Ref count: 0, styles deactivated and removed
278
```
279
280
### CSS Modules Configuration Examples
281
282
```javascript
283
// Complete CSS Modules setup with different injection types
284
285
// Standard CSS Modules (automatic injection)
286
module.exports = {
287
module: {
288
rules: [
289
{
290
test: /\.module\.css$/i,
291
use: [
292
"style-loader",
293
{
294
loader: "css-loader",
295
options: {
296
modules: {
297
localIdentName: "[name]_[local]__[hash:base64:5]"
298
}
299
}
300
}
301
],
302
},
303
],
304
},
305
};
306
307
// Lazy CSS Modules for conditional loading
308
module.exports = {
309
module: {
310
rules: [
311
{
312
test: /\.lazy\.css$/i,
313
use: [
314
{
315
loader: "style-loader",
316
options: { injectType: "lazyStyleTag" }
317
},
318
{
319
loader: "css-loader",
320
options: {
321
modules: {
322
localIdentName: "[name]_[local]__[hash:base64:5]",
323
exportLocalsConvention: "camelCase"
324
}
325
}
326
}
327
],
328
},
329
],
330
},
331
};
332
```
333
334
## Best Practices
335
336
1. **Use named imports** for better tree shaking and IDE support
337
2. **Lazy load large stylesheets** that aren't always needed
338
3. **Match use/unuse calls** to prevent memory leaks
339
4. **Use reference counting** wisely in component-based applications
340
5. **Configure localIdentName** for readable class names in development