5 Modern Features That Make CSS Easy

If you are a web developer, you’re probably familiar with one of the oldest jokes in this space. CSS experts will roll their eyes when hearing about your alignment issues, because, as they’ll be more than happy to highlight, there are like 10 different proven methods to align a button at the center.

.absolute-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.grid-container {
  display: grid;
  place-items: center;
}

.flex-container {
  display: grid;
  justify-content: center;
  align-items: center;
}

.margin-auto {
  margin-left: auto;
  margin-right: auto;
}

.text-center {
  text-align: center;
}

Of course, none of those methods will work for you, hence the frustration we all experience when styling our apps.

All jokes aside, knowing at least the basics of CSS is a prerequisite these days. What you might not realize is that Cascading Style Sheets is far from being an old, boring standard. There are lots of modern features emerging constantly, all aiming to improve your dev experience.

So let’s make sure you are making the most of the modern tools available today, and look at 5 useful CSS features you might not be familiar with.

SASS vs CSS

I’ve been involved in frontend development for almost 15 years now, and during this time I always picked SASS over CSS because of one specific feature - Rule nesting. Well, the great news is that this is natively supported in the browsers now, and starting December 2023 it has coverage across all major browsers.

1. CSS Nesting

Taking a look at an example, you can see that thanks to nesting rules and the nesting selector your stylesheets become more readable, modular, and maintainable.

/* No nesting */

form {
  display: flex;
}

form.disabled {
  display: none;
}

form label {
  font-size: 13px;
}

form label input {
  margin-top: 5px;
}
/* Nesting */

form {
  display: flex;

  &.disabled {
    display: none;
  }

  label {
    font-size: 13px;

    input {
      margin-top: 5px;
    }
  }
}

It also potentially helps reduce the size of CSS files, thereby decreasing the amount of data downloaded by users.

2. Custom Properties aka CSS Variables

And, since we are talking about preprocessors, another big step towards going full CSS native is the support for custom properties, also known as CSS variables.

It’s fairly common for most web projects to have a specific theme with fixed colors, dimensions, and fonts. So we can define custom properties in the root pseudo-class, by prefixing them with two dashes.

:root {
  --primary-color: #f19ed2;
  --secondary-color: #91ddcf;
  --font-size: 16px;
  --spacing: 24px;
  --border-radius: 8px;
}

Then, we can reference these variables anywhere in our styling rules via the var() function. Note that var() conveniently offers a simple way to provide fallback values when the given variable is not yet defined.

button {
  background-color: var(--primary-color, #000);
  color: #fff;
  padding: calc(var(--spacing) / 2);
  border-radius: var(--border-radius);
}

CSS variables are quite established in the space, and they’ve been widely available in all major browsers for a long time.

3. Container Queries

Container Queries on the other hand enjoy good support, but are newer to the scene.

These are a game-changer for responsive design, allowing components to adapt their layout based on the available space of their parent container.

.container {
  container-type: inline-size;
}

.box {
  background-color: #fff;
  text-align: center;
  transition: all 0.3s ease;
}

@container (min-width: 500px) {
  .box {
    background-color: #000;
    color: white;
    font-size: 1.5rem;
  }
}

Of course, most of us are used to media queries, which allow us to apply styles based on the characteristics of the device’s viewport.

.box {
  flex: 1 1 100%;
}

@media (min-width: 600px) {
  .box {
    flex: 1 1 45%;
  }
}

@media (min-width: 992px) {
  .box {
    flex: 1 1 30%;
  }
}

Container Queries, on the other hand, apply styles based on the size of a container element rather than the viewport.

4. has()

Another feature I am really excited about is the has() selector. This one solves one of the oldest CSS problems. Styling parent elements based on the state of its children.

form:has(input:disabled) {
  background: red;
  cursor: not-allowed;
}

In the past, you had to fallback on a JavaScript workaround for such scenarios, but now you can easily add in your styles natively, and remove a potential JavaScript dependency.

In the same line, you can use the is() pseudo-class to simplify complex selectors and improve readability.

:is(section, article, aside) is (h1, h2, h3) {
  color: blue;
}

/* equivalent of: */
section h1,
section h2,
section h3,
article h1,
article h2,
article h3,
aside h1,
aside h2,
aside h3 {
  color: blue;
}

This can be combined in various ways to capture nested structures, and both “has” and “is” have good browser support these days.

5. Grid Layout

This article would not be complete without the mention of the Grid Layout. Floating and positioning are somewhat abstract concepts, especially for people with little CSS experience. The Grid takes all this complexity away by providing a two-dimensional layout system that allows you to create complex responsive designs.

.container {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  grid-template-columns: 1fr 3fr;
  grid-template-rows: auto 1fr auto;
}

.header {
  grid-area: header;
}

.sidebar {
  grid-area: sidebar;
}

.main {
  grid-area: main;
}

.footer {
  grid-area: footer;
}

The layout is based on rows and columns, with intuitive rules you can use to create the structures you need.

While the Grid system has been available for quite a while, 2024 brings on the addition of the Subgrid.

.grid {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-template-rows: repeat(4, minmax(100px, auto));
}

.item {
  display: grid;
  grid-column: 2 / 7;
  grid-row: 2 / 4;
  grid-template-columns: subgrid;
  grid-template-rows: repeat (3, 80px);
}

.subitem {
  grid-column: 3 / 6;
  grid-row: 1 / 3;
}

Now you can create smaller grids inside a bigger grid so you can tackle more complicated designs with more ease.

The Grid is a powerful tool with many use cases and applications. It can feel overwhelming at first, but you’ll find a lot of good documentation out there. However, if you are interested in my deep dive into the Grid for Dummies, please let me know in the comments.

If you feel like you learned something, you should watch some of my youtube videos or subscribe to the newsletter.

Until next time, thank you for reading!