Day 99: native nesting

posted on

It’s time to get me up to speed with modern CSS. There’s so much new in CSS that I know too little about. To change that I’ve started #100DaysOfMoreOrLessModernCSS. Why more or less modern CSS? Because some topics will be about cutting-edge features, while other stuff has been around for quite a while already, but I just have little to no experience with it.


Nesting in CSS is coming soon! For me personally not the killer feature, at least compared to cascade layers or container queries, but still exciting. Let’s see how it works.

The most important thing to know about native nesting in CSS is that the nested selector always must start with a symbol (. # : [ * + > ~) because of limitations in browser parsing engines.

The following code doesn't work:

/* Doesn't work */

ul {
  li {
    border-color: green;
  }
}

To work around that limitation the nested selector can start with an &.

ul {
  & li {
    border-color: green;
  }
}

/* 
  Same as: 
  ul li { } 
*/

Besides this limitation, everything works as expected for me. Here are some of the things I've tested:

a {
  &:hover {
    background-color: aqua;
  }

  &:focus-visible {
    background-color: aqua;
  }
}

/* 
  Same as:
  a:hover { }
  a:focus-visible { } 
*/
a {
  &:is(:hover, :focus-visible) {
    background-color: aqua;
  }
}

/* 
  Same as:
  a:is(:hover, :focus-visible) { } 
*/
h2 {
  font-family: sans-serif;

  &::first-letter {
    color: red;
  }
}

/* 
  Same as:
  h2 { } 
  h2::first-letter { } 
*/
h2 {
  & + p {
    background-color: red;
  }
}

/* 
  Same as:
  h2 + p { } 
*/
h2 {
  .parent & {
    background-color: aqua;
  }
}

/* 
  Same as:
  .parent h2 { } 
*/
h2 {
  @media (min-width: 400px) {
    background: red;
  }
}

/* 
  Same as:
  @media (min-width: 400px) {
    h2 { } 
  }
*/
h2 {
  @media (min-width: 400px) {
    background: red;

    &::before {
      content: "!";
      color: #fff;
    }

    & ~ p {
      & span {
        background-color: #000;
      }

      :is(span) {
        color: #fff;
      }
    }
  }
}

/* 
  Same as:
  @media (min-width: 400px) {
    h2 { } 
    h2::before { }
    h2 ~ p span { }
    h2 ~ p :is(span) { }
  }
*/
div {
  & & & h3 {
    background-color: green;
  }
}

/* 
  Same as:
  div div div h3 { }
*/
h3 {
  :is(div) & {
    color: #fff;
  }
}

/* 
  Same as:
  :is(div) h3 { }
*/
a {
  &[download] {
    border: 1px solid red;
  }
}

/* 
  Same as:
  a[download] { }
*/

You can try it today in Chrome Dev, Safari Technology Preview, or Polypane 13.

See on CodePen

Further reading

Overview: 100 Days Of More Or Less Modern CSS