Enabling Upcoming CSS Features with PostCSS

Pavels Jelisejevs
Share

Picking up where we left off in the previous article, “The PostCSS Guide to Improving Selectors and Media Queries“, we will have a look at more PostCSS plugins that expand the horizons of CSS. The earlier article focused on improving the structure of your stylesheets through extending selectors and media queries. This one will concentrate on implementing new properties and values from upcoming specifications. The plugins covered in this article implement different functionality and can be effectively used both together and separately, depending on your needs.

We’ll start with my personal favorite.

Taking Resets to a New Level

CSS3 introduced two nice features: the initial value and the all property. The initial value used with values of inherit and unset allow you to reset a property to its original value. The all property serves as a shorthand property to reset every property to one of these three states. While each is interesting on their own, when used together, they allow you to quickly reset all styles for a particular element and prevent it from inheriting styles from parent elements of the page. Another step to writing modular CSS!

Sadly, both of these features are still not supported by IE. But, as you might have already guessed, there’s a plugin for that.

Postcss-initial adds support for the initial value and the all: initial combination. Here’s how it works:

.article {
  font-size: initial;
  color: initial;
  padding: initial;
  margin: initial;
}

Gets compiled into:

.article {
  font-size: medium;
  font-size: initial;
  color: #000;
  color: initial;
  padding: 0;
  padding: initial;
  margin: 0;
  margin: initial;
}

By default, it leaves the original properties with initial for browsers that natively support this feature.

The all property, in turn, will be translated into a long list of reset properties.

.container {
  all: initial;
}

Is transpiled into:

.container {
  animation: none 0s ease 0s 1 normal none running;
  backface-visibility: visible;
  background: transparent none repeat 0 0 / auto auto padding-box border-box scroll;
  border: medium none currentColor;
  border-collapse: separate;
  border-image: none;
  border-radius: 0;
  border-spacing: 0;
  bottom: auto;
  box-shadow: none;
  box-sizing: content-box;
  caption-side: top;
  clear: none;
  clip: auto;
  color: #000;
  columns: auto;
  column-count: auto;
  column-fill: balance;
  column-gap: normal;
  column-rule: medium none currentColor;
  column-span: 1;
  column-width: auto;
  content: normal;
  counter-increment: none;
  counter-reset: none;
  cursor: auto;
  direction: ltr;
  display: inline;
  empty-cells: show;
  float: none;
  font-family: serif;
  font-size: medium;
  font-style: normal;
  font-variant: normal;
  font-weight: normal;
  font-stretch: normal;
  line-height: normal;
  height: auto;
  hyphens: none;
  left: auto;
  letter-spacing: normal;
  list-style: disc outside none;
  margin: 0;
  max-height: none;
  max-width: none;
  min-height: 0;
  min-width: 0;
  opacity: 1;
  orphans: 2;
  outline: medium none invert;
  overflow: visible;
  overflow-x: visible;
  overflow-y: visible;
  padding: 0;
  page-break-after: auto;
  page-break-before: auto;
  page-break-inside: auto;
  perspective: none;
  perspective-origin: 50% 50%;
  position: static;
  right: auto;
  tab-size: 8;
  table-layout: auto;
  text-align: left;
  text-align-last: auto;
  text-decoration: none;
  text-indent: 0;
  text-shadow: none;
  text-transform: none;
  top: auto;
  transform: none;
  transform-origin: 50% 50% 0;
  transform-style: flat;
  transition: none 0s ease 0s;
  unicode-bidi: normal;
  vertical-align: baseline;
  visibility: visible;
  white-space: normal;
  widows: 2;
  width: auto;
  word-spacing: normal;
  z-index: auto;
  all: initial;
}

In case you are using BEM or Suit, this plugin works well with postcss-autoreset which will automatically reset styles for block and component-level elements.

Custom Properties

When working on a layout, we often have to share some values across the stylesheet. For example, your brand color might be used as a background for a button, a text color for a link or a border for a text block. Currently to achieve this, we would need to repeat the color multiple times in each place it is used. Such repetition makes it tedious to keep the color palette consistent when changing the colors across the application.

CSS preprocessors such as Less and Sass have solved this problem using variables. Fortunately, W3C are working on a similar concept called custom properties. Although addressing the same problem, they work differently from variables in preprocessors. Less and Sass variables are resolved at compilation time. When you compile your Less or Sass to CSS, the compiler will look for a variable declaration corresponding to the current compilation scope and replace each instance with the corresponding value. This means, that the resolved value of a variable depends solely on where it is used in the code. Custom properties, in turn, are defined for elements in the DOM and are accessible only to their child elements. This means that the value of a variable depends on the position of an element in the DOM and can only be resolved at run time.

By this point, you should frown or raise an eyebrow. If a value of a variable is only known at runtime, how can it be resolved by a PostCSS plugin? The truth is, it can’t. It does, however, provide a way to use a subset of that functionality. If we define all of our custom properties in the :root element, these properties will be accessible to all elements on the page. This means, that we can resolve them at compile time.

Here’s a simple example of how it may look:

:root {
  --text-color: red;
  --background: blue;
}

h1 {
  color: var(--text-color);
  font-size: var(--font-size, 20px);
}
button {
  background-color: var(--background);
}

Will be compiled into:

h1 {
  color: red;
  font-size: 20px;
}
button {
  background-color: blue;
}

Note that the --font-size variable is not defined, so it’s replaced with a fallback value of 20px. The important part here is to keep all of the custom properties inside of :root. The ones defined elsewhere will be ignored as they cannot be adequately handled by the plugin. You can start with that, and expand your usage when more browsers start to support it. Chrome already supports them starting from 49.

Logical Properties

If you have ever developed an international website that spans cultures with different writing directions, you know what is involved when maintaining several versions of the interface, such as left-to-right or right-to-left. To address this need, W3C introduced a new concept of logical properties. A way to abstract ourselves from thinking in terms of physical directions such as right or left, but rather in logical ones – start and end. The specification is still very much a work in progress, but you can already try some of this stuff out using the postcss-logical-props plugin.

It supports generating left-to-right and right-to-left versions of the website using certain logical properties, such as border-block-start and border-block-end, offset-block-start and offset-block-end. These properties are compiled into their left or right alternatives. You can instruct the plugin to compile both the LTR and RTL versions of the stylesheet and then switch them in the application when a user changes the language.

For example, if you have the following CSS:

.text {
  border-block-start: 1px solid blue;
  text-align: start;
  padding-block-end: 10px;
  margin-block-start: 20px;
}

Calling the plugin with options { dir: 'LTR' } would yield the following result:

.text {
  border-left: 1px solid blue;
  text-align: left;
  padding-right: 10px;
  margin-left: 20px;
}

While using { dir: 'RTL' } would give you a mirror image:

.text {
  border-right: 1px solid blue;
  text-align: right;
  padding-left: 10px;
  margin-right: 20px;
}

New Color Features

PostCSS provides a whole group of plugins that deliver new features for working with colors.

Color Adjustment

The postcss-color-function plugin implements the new color() function. This function allows you to modify a base color using one or multiple “color adjustor” functions. Each of the color adjustors can manipulate a color in a specific way.

Here are a few examples of how it can be used:

body {
  color: color(red green(50));
  color: color(red blue(50) a(50%));
  color: color(red tint(50%));
  color: color(red shade(50%));
}

Will be compiled into the following colors

body {
  color: rgb(255, 50, 0);
  color: rgba(255, 0, 50, 0.5);
  color: rgb(255, 128, 128);
  color: rgb(128, 0, 0);
}

A complete list of color adjusters can be found in the specification. This plugin can be extremely effective together with custom properties. You can define a set of base colors and calculate others based on them. This way if you’ll decide to change your base colors, the rest of the palette will be updated accordingly.

HWB Color Notation

HWB stands for Hue-Whiteness-Blackness, an alternative method for defining colors. It describes a color using a hue value from 0 to 360 and then adds a degree of whiteness and blackness from 0% to 100%. This notation is similar to HSL and somewhat easier to comprehend than RGB. The postcss-color-hwb plugin implements the new hwb() function which is used to define HWB colors. A couple of examples:

body {
  color: hwb(0, 0%, 0%);
  color: hwb(120, 40%, 20%);
  color: hwb(240, 0%, 100%, 0.5);
}

Will generate the following colors:

body {
  color: rgb(255, 0, 0);
  color: rgb(102, 204, 102);
  color: rgba(0, 0, 0, 0.5);
}

Gray() Function

The CSS colors module also introduced a handy gray() function. It can be used to generate gray colors without specifying any redundant information, such as all three channels in an RGB color. The postcss-color-gray plugin implements a polyfill for the function and is extremely straightforward to use:

body {
  color: gray(0);
  color: gray(25);
  color: gray(50%);
  color: gray(128, 20%);
}

This code above will generate different shades of gray:

body {
  color: rgb(0, 0, 0);
  color: rgb(25, 25, 25);
  color: rgb(127, 127, 127);
  color: rgba(128, 128, 128, 0.2);
}

Wrapping It All Up

This is by all means not a complete list of CSS plugins available, but rather a selection of some the more interesting ones. You can explore more of them at postcss.parts.

CSS is booming and PostCSS is booming with it. Yes, we are all eagerly waiting for native browser support for these new features, but PostCSS gives us a promising opportunity to adopt and evaluate these features early. The general advice here would be to try to take a step back from the familiar usage of preprocessors and take a fresh perspective on writing stylesheets. Try to come up with a list of available features that would make you more productive and use them in your workflow. You might soon realize that these are the exact features you were missing all along.