or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

css-modules.mdindex.mdinjection-types.mdloader-configuration.mdruntime-api.md

css-modules.mddocs/

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