The PostCSS Guide to Improving Selectors and Media Queries

Share this article

Recent changes to the CSS specifications have introduced quite a few interesting features. Unfortunately, some of them are still in a draft state and the ones that aren’t lack broad browser support. As usual, it will take a while for the new suggestions to be reviewed, accepted and implemented by the browsers. However, we can spare ourselves the long wait and try out some of these features already using PostCSS.

PostCSS has a variety of plugins aiming at implementing polyfills for the newest CSS features. Since the spectrum of these plugins is broad, it would be difficult to cover all of them in a single article. Instead, we will narrow our gaze to a selection of plugins that focus on adding new capabilities to selectors and media queries. Many of them will allow us to significantly improve the structure of our stylesheets while others may just add some sweet syntax sugar on top.

We won’t get into the details of configuring and installing PostCSS in this article. This has already been covered in “An introduction to PostCSS” and “Improving the quality of your CSS with PostCSS“. For a quick reference you can also check out the PostCSS page on GitHub.

Rule Nesting

Let’s start with what is likely to be the most basic feature of all — and certainly familiar to every pre-processor user — nesting. The postcss-nesting plugin implements style rule nesting according to the W3C nesting module proposal.

The proposal introduces a new & nesting selector which references the parent selector. As opposed to Less or Sass, according to the specification, this selector is mandatory and must be the first basic selector in a chain of selectors to enable nesting. Any selectors which do not contain the nesting selector will be ignored. For example:

.article {
  color: #333;

  &.popular {
    background: #DDD;
  }

  & .title {
    font-weight: bold;
  }
}

Will be translated into:

.article {
  color: #333
}
.article.popular {
  background: #DDD
}
.article .title {
  font-weight: bold
}

Note that the following code is invalid, since it doesn’t use the & selector as specified.

.article {
  color: #333;

  /* no nested-selector */
  .popular {
    background: #DDD;
  }

  /* the nested selector is not the fist selector in the chain */
  .latest & {
    border: 1px solid red;
  }
}

To allow inserting of the parent selector into any place in a selector (instead of just the beginning), the proposal defines an alternative syntax using the nesting at-rule @nest. We can fix the .latest & selector from the previous example in the following way:

.article {
  color: #333;

  @nest .latest & {
    border: 1px solid red;
  }
}

Which will compile into:

.article {
  color: #333
}
.latest .article {
  border: 1px solid red
}

The @nest syntax is also a tad more expressive then just &.

Custom Selectors

When writing CSS, we tend to write a lot of repeating selectors. This may be simple boilerplate code to select all links or any button, or a more complex selector that needs to be repeated over and over. This can introduce a lot of code duplication along with all the related problems of code maintenance. The new CSS extensions specification introduces a way to store selectors in variables and reference them from other parts of the stylesheets. Thus, a repeating selector can be defined only once and safely reused elsewhere.

PostCSS has a postcss-custom-selectors plugin that implements this feature. Here’s a simple example that defines a selector for all header elements:

@custom-selector :--heading h1, h2, h3, h4, h5, h6;

:--heading {
  font-weight: bold;
}

It will compile into:

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: bold;
} 

Custom selectors are implemented as pseudo-classes, hence the odd looking :-- syntax.

Custom selectors can be effectively used together with basic selectors. For example:

.article :--heading .author {
  color: blue;
}

Gets compiled into:

.article h1 .author,
.article h2 .author,
.article h3 .author,
.article h4 .author,
.article h5 .author,
.article h6 .author {
  color: blue;
}

You can even combine multiple custom selectors in a single selector to achieve more complex permutations.

@custom-selector :--links a, a:focus, a:visited, a:hover, a:active;
article :--heading :--links {
  color: #333;
}

Will result in:

article h1 a,
article h2 a,
article h3 a,
article h4 a,
article h5 a,
article h6 a,
article h1 a:focus,
article h2 a:focus,
article h3 a:focus,
article h4 a:focus,
article h5 a:focus,
article h6 a:focus,
article h1 a:visited,
article h2 a:visited,
article h3 a:visited,
article h4 a:visited,
article h5 a:visited,
article h6 a:visited,
article h1 a:hover,
article h2 a:hover,
article h3 a:hover,
article h4 a:hover,
article h5 a:hover,
article h6 a:hover,
article h1 a:active,
article h2 a:active,
article h3 a:active,
article h4 a:active,
article h5 a:active,
article h6 a:active {
  color: #333;
}

This example might be slightly exaggerated, but it gives a nice demonstration of the power of this feature.

New Pseudo-Classes

The Selectors Level 4 specification introduced a whole bunch of new pseudo-classes, but due to the dynamic nature of most of them, only several are available as PostCSS plugins.

The :matches() Pseudo-class

The postcss-selector-matches plugin implements the new :matches() pseudo-class. It is a functional class that filters only the elements that match the selectors in the argument. If you pass multiple selectors, then the element has to match at least one of them. In short:

button:matches(:hover, :focus) {
  color: red;
}

Is compiled into:

button:hover, button:focus {
  color: red;
}

The :not() Pseudo-class

The :not() pseudo-class filters elements that do not match any of the given arguments. It is implemented in the postcss-selector-not plugin.

section:not(:first-child, :last-child) {
  background: white;
}

Results in:

section:not(:first-child):not(:last-child) {
  background: white;
}

The postcss-pseudo-class-any-link plugin implements the :any-link pseudo-class. It was introduced to help resolve the confusion around the :link pseudo class. Unlike the latter, it matches all links — including visited ones.

a:any-link {
  color: blueviolet;
}

Is compiled into:

a:link,a:visited {
  color: blueviolet;
}

Improvements to Media Queries

There’s a couple of plugins in the arsenal of PostCSS designed to make media queries easier to use. They are postcss-custom-media and postcss-media-minmax.

Custom Media

Writing media queries yields the same problem as regular selectors — they are repeated frequently across stylesheets. Probably even more frequently than plain selectors. Luckily, there’s a solution similar to custom selectors. The postcss-custom-media plugin implements the custom media query specification which adds the ability to save media queries into variables.

The syntax closely resembles custom selectors. Here’s a simple example:

@custom-media --medium-viewport (min-width: 768px) and (max-width: 992px);

@media (--medium-viewport) {
  /* your styles here */
}

Is compiled into:

@media (min-width: 768px) and (max-width: 992px) {
  /* your styles here */
}

Of course, you can use multiple custom media queries at once:

@custom-media --landscape (orientation: landscape);
@media (--medium-viewport) and (--landscape) {
  /* your styles here */
}

Will result in:

@media (min-width: 768px) and (max-width: 992px) and (orientation: landscape) {
  /* your styles here */
}

As you can see, now it’s much easier to change your definition of a “medium viewport” as well as look for any related CSS code.

Min and Max Syntax

Although media queries are a great thing, the min- and max- syntax has received a lot of hatred from the community. W3C responded by introducing a more intuitive syntax using comparison operators. The postcss-media-minmax plugin adds support for >, >=, < and <= operators. It also allows us to use the value-in-the-middle syntax, that is, min-value < property < max-value.

We can simplify the previous examples using the new syntax, which will yield the same result.

@custom-media --medium-viewport (768px <= width <= 992px);

@media (--medium-viewport) {
  /* your styles here */
}

Of course, the plugin will also work without using custom media.

Don’t Stop Here

Unfortunately, not many of the new CSS features can be implemented as a PostCSS plugin because of their dynamic nature and dependency on the DOM. We’ll need to wait for native support for most of them. But there are still plenty of plugins to explore and test in your workflow. This article focused on the plugins related to selectors and media queries, but we hope to bring you overviews of other novelties as well. If you’re curious and impatient, you can find more of them at postcss.parts. Keep exploring!

Frequently Asked Questions (FAQs) about PostCSS Selectors and Media Queries

What are the benefits of using PostCSS over other CSS processors?

PostCSS offers several advantages over other CSS processors. It is highly modular, meaning you can select the plugins you need for your project, making it more efficient and lightweight. It also allows you to use future CSS syntax today, and it has a large and active community that continually develops new plugins and updates existing ones. Furthermore, PostCSS is faster than other processors like Sass and Less, and it integrates well with build tools like Webpack and Gulp.

How can I use PostCSS to improve my CSS selectors?

PostCSS provides several plugins that can help you improve your CSS selectors. For example, the postcss-nested plugin allows you to use nested rules, similar to Sass. The postcss-selector-matches plugin can simplify complex :matches() pseudo-classes, and the postcss-selector-not plugin can simplify :not() pseudo-classes. These plugins can make your CSS more readable and maintainable.

Can I use PostCSS to create responsive designs?

Yes, you can use PostCSS to create responsive designs. The postcss-media-minmax plugin allows you to use the min-width and max-width features of media queries, which are essential for responsive design. The postcss-custom-media plugin lets you define custom media queries, making your code more readable and maintainable.

How can I use PostCSS to optimize my CSS?

PostCSS offers several plugins for optimizing your CSS. For example, the cssnano plugin can minify your CSS, reducing its size and improving your website’s load time. The postcss-merge-rules plugin can merge adjacent CSS rules, reducing the size of your CSS. The postcss-discard-comments plugin can remove comments from your CSS, further reducing its size.

How can I use PostCSS to improve my workflow?

PostCSS can significantly improve your workflow. It integrates well with build tools like Webpack and Gulp, allowing you to automate tasks like compiling your CSS, minifying it, and prefixing it. It also supports source maps, which can help you debug your CSS. Furthermore, because PostCSS is modular, you can select the plugins you need for your project, making your workflow more efficient.

Can I use PostCSS with other CSS processors like Sass and Less?

Yes, you can use PostCSS with other CSS processors like Sass and Less. You can use PostCSS to add features that these processors lack, or to optimize the CSS they produce. For example, you can use the Autoprefixer plugin to add vendor prefixes to the CSS produced by Sass or Less.

How can I start using PostCSS?

To start using PostCSS, you first need to install it using npm, the Node.js package manager. Then, you can install the plugins you need for your project. You can use PostCSS from the command line, or you can integrate it with build tools like Webpack and Gulp.

What are some good resources for learning PostCSS?

There are many good resources for learning PostCSS. The official PostCSS documentation is a great place to start. It provides a comprehensive overview of PostCSS and its plugins. The PostCSS GitHub page also has a lot of useful information, including a list of all available plugins. There are also many tutorials and articles available online that can help you get started with PostCSS.

Can I contribute to the PostCSS community?

Yes, you can contribute to the PostCSS community. You can contribute by developing new plugins, updating existing ones, reporting bugs, or improving the documentation. The PostCSS community is very active and welcoming, and contributing to it can be a great way to learn more about PostCSS and improve your skills.

What is the future of PostCSS?

The future of PostCSS looks very promising. It is continually being developed and improved, and new plugins are being released regularly. It is also gaining popularity among developers, and it is being used in more and more projects. Furthermore, because PostCSS allows you to use future CSS syntax today, it is a great tool for staying up-to-date with the latest developments in CSS.

Pavels JelisejevsPavels Jelisejevs
View Author

Pavels is a software developer from Riga, Latvia, with a keen interest for everything web-related. His interests range from back-end to front-end development, as well as analysis and automation. If you have something to discuss, you can always reach him via Facebook or LinkedIn.

AdvancedCSSpatrickcPostCSS
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week