Nesting

Write related rules inside each other and css-pp flattens them into ordinary CSS. The output works in every browser, including those without native CSS nesting.

Children#

A nested selector without & is a descendant of its parent:

CSS
.card {
  padding: 1.5rem;
  h2 { margin: 0; }
  > footer { border-top: 1px solid #ddd; }
}
Output
.card{padding:1.5rem}
.card h2{margin:0}
.card > footer{border-top:1px solid #ddd}

The parent reference &#

& stands for the parent selector, wherever you put it:

CSS
.button {
  &:hover        { filter: brightness(1.1); }   /* .button:hover */
  &.primary      { background: #0ea5e9; }       /* .button.primary */
  &-icon         { width: 1em; }                /* .button-icon (BEM) */
  .dark &        { color: #fff; }               /* .dark .button */
  & + &          { margin-left: .5rem; }        /* .button + .button */
  &:has(svg)     { gap: .5rem; }                /* .button:has(svg) */
}

The older &{ ... } block, which styles the parent itself, still works and can go anywhere in the rule:

CSS
.panel {
  h3 { font-size: 1.1rem; }
  &{ background: #fff; padding: 1rem; }
}

Selector lists#

Lists on either side combine every parent with every child:

CSS
.alert, .toast {
  a, button { font-weight: 600; }
}
Output
.alert a,.toast a,.alert button,.toast button{font-weight:600}

Commas inside functional pseudo-classes are not list separators, so :is(), :where(), :not() and :has() work inside nested rules:

CSS
.nav {
  a:is(:hover, :focus-visible) { text-decoration: underline; }
  li:has(> a.active, > a[aria-current]) { font-weight: 600; }
}

@media inside rules#

Put a media query, @supports or @container block inside a rule and css-pp moves it out for you:

CSS
.sidebar {
  width: 18rem;
  @media (max-width: 768px) {
    width: auto;
    .widget { display: none; }
  }
}
Output
.sidebar{width:18rem}
@media (max-width:768px){
.sidebar{width:auto}
.sidebar .widget{display:none}
}

Order is preserved#

Declarations and nested rules are written out in the order you wrote them, the same way native nesting works. Declarations after a nested rule become a second rule for the same selector, so they still come later in the cascade.

Combining with the other features#

Nesting works with every css-pp feature:

CSS
.gallery {
  > figure {
    background-image: url(/img/photo-$[index].jpg);
    &:not(:seen) { opacity: 0; }
  }
  &:has(figure:overflowing) .hint { display: block; }
}