or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-postcss-nested

PostCSS plugin to unwrap nested rules like how Sass does it

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/postcss-nested@7.0.x

To install, run

npx @tessl/cli install tessl/npm-postcss-nested@7.0.0

0

# PostCSS Nested

1

2

PostCSS Nested is a PostCSS plugin that unwraps nested CSS rules similar to Sass syntax. It enables developers to write nested CSS with parent selector references (&), automatic selector merging, at-rule bubbling, and custom root rule handling for breaking out of nesting contexts.

3

4

## Package Information

5

6

- **Package Name**: postcss-nested

7

- **Package Type**: npm

8

- **Language**: JavaScript

9

- **Installation**: `npm install postcss-nested`

10

11

## Core Imports

12

13

```javascript

14

const postcssNested = require('postcss-nested');

15

```

16

17

ESM:

18

19

```javascript

20

import postcssNested from 'postcss-nested';

21

```

22

23

## Basic Usage

24

25

```javascript

26

const postcss = require('postcss');

27

const postcssNested = require('postcss-nested');

28

29

// Basic usage with default options

30

const result = postcss([postcssNested()])

31

.process(css, { from: 'input.css' });

32

33

// With custom options

34

const result = postcss([

35

postcssNested({

36

bubble: ['phone'],

37

preserveEmpty: true,

38

rootRuleName: 'escape-nesting'

39

})

40

]).process(css, { from: 'input.css' });

41

```

42

43

Input CSS:

44

```css

45

.phone {

46

&_title {

47

width: 500px;

48

@media (max-width: 500px) {

49

width: auto;

50

}

51

body.is_dark & {

52

color: white;

53

}

54

}

55

img {

56

display: block;

57

}

58

}

59

60

.title {

61

font-size: var(--font);

62

63

@at-root html {

64

--font: 16px;

65

}

66

}

67

```

68

69

Output CSS:

70

```css

71

.phone_title {

72

width: 500px;

73

}

74

@media (max-width: 500px) {

75

.phone_title {

76

width: auto;

77

}

78

}

79

body.is_dark .phone_title {

80

color: white;

81

}

82

.phone img {

83

display: block;

84

}

85

86

.title {

87

font-size: var(--font);

88

}

89

html {

90

--font: 16px;

91

}

92

```

93

94

## Capabilities

95

96

### Plugin Factory Function

97

98

Creates a PostCSS plugin instance with optional configuration.

99

100

```javascript { .api }

101

/**

102

* Creates a PostCSS plugin for processing nested CSS rules

103

* @param {Options} opts - Optional configuration object

104

* @returns {PostCSSPlugin} PostCSS plugin object

105

*/

106

function postcssNested(opts = {}) {

107

// Returns PostCSS plugin

108

}

109

110

// PostCSS compatibility flag

111

postcssNested.postcss = true;

112

```

113

114

### Configuration Options

115

116

All configuration options are optional and have sensible defaults.

117

118

```javascript { .api }

119

interface Options {

120

/**

121

* Custom at-rules that should bubble to the top level.

122

* Default: ['media', 'supports', 'layer', 'container', 'starting-style']

123

*/

124

bubble?: string[];

125

126

/**

127

* Custom at-rules that should be unwrapped from nested contexts.

128

* Default: ['document', 'font-face', 'keyframes', '-webkit-keyframes', '-moz-keyframes']

129

*/

130

unwrap?: string[];

131

132

/**

133

* Whether to preserve empty selector rules after unwrapping.

134

* Useful for CSS modules compatibility.

135

* Default: false

136

*/

137

preserveEmpty?: boolean;

138

139

/**

140

* Custom name for the at-root directive for breaking out of nesting.

141

* Default: 'at-root'

142

*/

143

rootRuleName?: string;

144

}

145

```

146

147

### PostCSS Plugin Interface

148

149

The plugin returns a PostCSS plugin object with the required interface.

150

151

```javascript { .api }

152

interface PostCSSPlugin {

153

/** Plugin identifier for PostCSS */

154

postcssPlugin: 'postcss-nested';

155

156

/** Initial processing of at-root rules */

157

Once(root: Root): void;

158

159

/** Main rule processing function */

160

Rule(rule: Rule): void;

161

162

/** Final cleanup of at-root rules */

163

RootExit(root: Root): void;

164

}

165

```

166

167

## Key Features

168

169

### Nested Rules

170

Write CSS rules inside other rules, similar to Sass:

171

172

```css

173

/* Input */

174

.card {

175

padding: 10px;

176

177

.title {

178

font-size: 18px;

179

}

180

181

.content {

182

margin-top: 10px;

183

}

184

}

185

186

/* Output */

187

.card {

188

padding: 10px;

189

}

190

191

.card .title {

192

font-size: 18px;

193

}

194

195

.card .content {

196

margin-top: 10px;

197

}

198

```

199

200

### Parent Selector References

201

Use `&` to reference the parent selector:

202

203

```css

204

/* Input */

205

.button {

206

color: blue;

207

208

&:hover {

209

color: red;

210

}

211

212

&.active {

213

font-weight: bold;

214

}

215

216

.dark-theme & {

217

color: white;

218

}

219

}

220

221

/* Output */

222

.button {

223

color: blue;

224

}

225

226

.button:hover {

227

color: red;

228

}

229

230

.button.active {

231

font-weight: bold;

232

}

233

234

.dark-theme .button {

235

color: white;

236

}

237

```

238

239

### At-Rule Bubbling

240

Media queries and other at-rules bubble to the root level:

241

242

```css

243

/* Input */

244

.sidebar {

245

width: 300px;

246

247

@media (max-width: 768px) {

248

width: 100%;

249

250

.menu {

251

display: none;

252

}

253

}

254

}

255

256

/* Output */

257

.sidebar {

258

width: 300px;

259

}

260

261

@media (max-width: 768px) {

262

.sidebar {

263

width: 100%;

264

}

265

266

.sidebar .menu {

267

display: none;

268

}

269

}

270

```

271

272

### At-Rule Unwrapping

273

Certain at-rules are unwrapped and flattened:

274

275

```css

276

/* Input */

277

.component {

278

color: black;

279

280

@keyframes slide {

281

from { transform: translateX(-100%); }

282

to { transform: translateX(0); }

283

}

284

}

285

286

/* Output */

287

.component {

288

color: black;

289

}

290

291

@keyframes slide {

292

from { transform: translateX(-100%); }

293

to { transform: translateX(0); }

294

}

295

```

296

297

### At-Root Directive

298

Break out of nested contexts using `@at-root`:

299

300

```css

301

/* Input */

302

.page {

303

color: black;

304

305

.content {

306

padding: 20px;

307

308

@at-root {

309

.modal {

310

position: fixed;

311

top: 50%;

312

left: 50%;

313

}

314

}

315

}

316

}

317

318

/* Output */

319

.page {

320

color: black;

321

}

322

323

.page .content {

324

padding: 20px;

325

}

326

327

.modal {

328

position: fixed;

329

top: 50%;

330

left: 50%;

331

}

332

```

333

334

### At-Root with Selector

335

Use `@at-root` with a selector for targeted escaping:

336

337

```css

338

/* Input */

339

.theme {

340

.component {

341

color: blue;

342

343

@at-root html {

344

--primary-color: blue;

345

}

346

}

347

}

348

349

/* Output */

350

.theme .component {

351

color: blue;

352

}

353

354

html {

355

--primary-color: blue;

356

}

357

```

358

359

### At-Root with Filters

360

Control which rules escape using `with` and `without` filters:

361

362

```css

363

/* Input */

364

@media screen {

365

.component {

366

color: black;

367

368

@at-root (without: media) {

369

.global {

370

position: fixed;

371

}

372

}

373

}

374

}

375

376

/* Output */

377

@media screen {

378

.component {

379

color: black;

380

}

381

}

382

383

.global {

384

position: fixed;

385

}

386

```

387

388

## Configuration Examples

389

390

### Custom Bubble Rules

391

Add custom at-rules to bubble to the root:

392

393

```javascript

394

postcss([

395

postcssNested({

396

bubble: ['custom-query', 'my-rule']

397

})

398

])

399

```

400

401

```css

402

/* Input */

403

.element {

404

color: black;

405

406

@custom-query (min-width: 600px) {

407

color: blue;

408

}

409

}

410

411

/* Output */

412

.element {

413

color: black;

414

}

415

416

@custom-query (min-width: 600px) {

417

.element {

418

color: blue;

419

}

420

}

421

```

422

423

### Custom Unwrap Rules

424

Add custom at-rules to unwrap:

425

426

```javascript

427

postcss([

428

postcssNested({

429

unwrap: ['custom-keyframes']

430

})

431

])

432

```

433

434

### Preserve Empty Rules

435

Keep empty parent rules for CSS modules:

436

437

```javascript

438

postcss([

439

postcssNested({

440

preserveEmpty: true

441

})

442

])

443

```

444

445

```css

446

/* Input */

447

.parent {

448

.child {

449

color: red;

450

}

451

}

452

453

/* Output with preserveEmpty: true */

454

.parent {

455

}

456

457

.parent .child {

458

color: red;

459

}

460

461

/* Output with preserveEmpty: false (default) */

462

.parent .child {

463

color: red;

464

}

465

```

466

467

### Custom Root Rule Name

468

Use a custom name for the at-root directive:

469

470

```javascript

471

postcss([

472

postcssNested({

473

rootRuleName: 'escape'

474

})

475

])

476

```

477

478

```css

479

/* Input */

480

.nested {

481

color: black;

482

483

@escape {

484

.global {

485

position: fixed;

486

}

487

}

488

}

489

490

/* Output */

491

.nested {

492

color: black;

493

}

494

495

.global {

496

position: fixed;

497

}

498

```

499

500

## Error Handling

501

502

The plugin provides helpful error messages for common issues:

503

504

- **Syntax errors in selectors**: Reports missed semicolons and malformed selectors

505

- **Invalid at-root parameters**: Clear error messages for unrecognized parameter formats

506

- **Selector parsing failures**: Detailed error context when selectors cannot be parsed

507

508

## Dependencies

509

510

- **postcss**: ^8.2.14 (peer dependency)

511

- **postcss-selector-parser**: ^7.0.0 (direct dependency)

512

513

## Node.js Compatibility

514

515

- **Node.js**: >=18.0