The last time I seriously worked with CSS was back when Firefox was still a relevant browser (šŸ˜¢), and Internet Explorer 11 was just on its way out.

Flexbox had just become a popular way to create layouts, and CSS Grid was new and exciting, but not yet widely supported. Not that I could use either, since IEā€™s implementation of flexbox was so riddled with bugs that it was practically unusable.

But now. Weā€™re in 2024, and things have changed. A lot.

Here are a few of my favourite features of modern CSS, in no particular order:

CSS Variables

CSS variables were once only possible with a preprocessor like SASS or LESS, but now theyā€™re a native feature of CSS.

Here is how to define a variable:

:root {
  --primary-color: #ff0000;
}

Specifying the variable at the root level makes it available to the entire document.

The variable can then be used with the var() function:

div {
  color: var(--primary-color);
}

The neat thing is that variables follow the cascading logic of CSS, so you can update their value at any level of the document, and all elements that use that variable will automatically update.

See the Pen CSS Variables by Loris Bognanni (@codemade) on CodePen.

Nesting in CSS

This is again one of those things that you would have needed a CSS preprocessor for in the past, but is now a native feature of CSS. Useful for keeping your code concise and readable.

.parent {
  color: red;
  .child {
    color: blue;
  }
}

I especially find this useful when styling pseudo-elements and states:

button {
  background-color: blue;
  &:hover {
    background-color: red;
  }
  &:after {
    content: 'šŸš€';
  }
}

Centering a div is now very easy

Centerig a div has long been a recurring joke amongst web developers, but thanks to CSS Grid, itā€™s now a two-liner:

div.parent {
  display: grid;
  place-items: center;
}

Viewport units

Viewport units are a way to size elements based on the size of the viewport. They are particularly useful for creating responsive designs.

Viewport units include vw (viewport width), vh (viewport height), vmin (the smaller of the two), and vmax (the larger of the two). So for example, if you want an element to be half as wide as the viewport, you can do this:

.my-element {
  width: 50vw;
}

Here is an example that combines viewport units and CSS Grid to center a div in the middle of the screen:

See the Pen centering by Loris Bognanni (@codemade) on CodePen.

Math in CSS

CSS has a calc() function that allows you to perform calculations right in your stylesheets. This can be useful for things like calculating widths, margins, and padding.

The exciting thing about calc() is that it can combine different units of measurement, so you can do things like this:

.my-element {
  width: calc(50% - 20px);
}

You can of course use calc() with variables as well:

:root {
  --margin: 20px;
}
article {
  margin: calc(var(--margin) * 2);
}

CSS Containers, container queries, and container measurements

By telling CSS that a certain element is a container, you can now do things like size its children based on the containerā€™s size, or apply styles based on the containerā€™s size.

Similar to viewport units, container units are based on the size of the container element. They include cqw, cqh, cqmin, and cqmax.

See the Pen Untitled by Loris Bognanni (@codemade) on CodePen.

Container queries take this a step further by allowing you to apply styles based on the size of the container. This is particularly useful for creating responsive designs where the layout changes based on the size of the container, rather than the viewport.

aspect-ratio property

If you ever wanted to embed a video in your responsive page in the old days, you probably had to use some javascript to calculate the correct height based on the width of the video.

But now, you can use the aspect-ratio property:

.video {
  width: 100%;
  aspect-ratio: 16 / 9;
}

:has() selector

The :has() selector allows you to select an element based on its descendants. This can be useful for styling elements based on their content, or for selecting elements that contain a specific child element.

While before :has you would have to use javascript or some sort of preprocessing to achieve the same effect, now you can do it with a single line of CSS.

For example, here is a simple way to style a <label> element when its child <input> is checked:

See the Pen :has demo by Loris Bognanni (@codemade) on CodePen.