Back to The Frontend, Part 1


My current work project is ending in a few weeks, and I got one of those questions consultants get every now and then: What kind of project would you like next?

When I was starting my career, my projects were much shorter than they are now. They usually lasted from one month to six months… The longest was probably about a year. In contrast, my current project has lasted two and a half years, two years before that and two years in startup before that.

As projects used to be shorter the tech changed much more frequently. Okay… the web dev as a whole was different back then. It seemed like new techs were coming every month and old ones might be deprecated in a year.

So when I was asked that question and really started thinking about what I would like to do next, I noticed something surprising: my last frontend-heavy project was almost five years ago.

I have always considered myself a full-stack developer. I’m used to doing everything myself, and I genuinely enjoy creating new ideas end to end, so it comes naturally. But the truth is that I haven’t done day-to-day frontend development in years.

Still… it’s kind of like riding a bike, right?

Let’s start this series by looking at CSS, because CSS was the biggest motivation for writing this post. When I decided to check what had changed, I found an impressive amount of new features - many of them things I really wish had existed 20 years ago, back when I was building my first websites with spacer GIFs.


Container Queries

For a long time, there was only one way to make elements behave differently depending on available space: media queries.

Media queries are based on the viewport size, meaning the layout adapts to the browser window, not to the element itself. This often led to awkward breakpoints and tightly coupled layouts. And it is kind a limiting way of doing different views for different resolutions.

Container queries change that completely.

Instead of reacting to the viewport, components can now react to the size, style, or scroll state of their container.

Example

First, you define a container context using the container-type property:

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

In this example we are watching .card class inline-size and will react when that changes.

Now you can write a container query that reacts to the container’s width instead of the viewport:

@container (width > 600px) {
  .card h2 {
    font-size: 2em;
  }
}

If you want more control, you can name your container:

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

/* Or using the shorthand */
.card {
  container: card / inline-size;
}

And then target it explicitly:

@container card (width > 700px) {
  .card {
    font-size: 2em;
  }
}

Things get even more interesting when you use style queries. These allow you to react to styles or custom properties on the container and even combine conditions using boolean logic.

Example from MDN:

@container style(color: green) and style(background-color: transparent),
    not style(background-color: red),
    style(--themeBackground),
    style(--themeColor: blue) or style(--themeColor: purple),
    (width <= 100vw) and style(max-width: 600px) {
  /* <stylesheet> */
}

Wow…looks complex.

Lastly container queries can also react to scroll state, which enables patterns that previously required Javascript.

For example, you can now create a “back to top” button purely with CSS.

Example from MDN:

html {
  container-type: scroll-state;
  container-name: scroller;
}

@container scroller scroll-state(scrollable: top) {
  .back-to-top {
    translate: 0 0;
  }
}

This allows styles to change based on whether the container is scrollable and where the scroll position is.

Support

Chrome Safari Firefox

More info

MDN Container queries

Nesting

I used Less and Sass a long time ago, and one of my favorite features was always nesting.

Instead of writing long and convoluted selectors like this:

.container > .selector > table {
  margin: 2em;
}

You could use

.container {
  .selector {
    table {
      margin: 2em;
    }
  }
}

This always made more sense to me, especially coming from a programming background where nesting is the norm.

And now finally the nesting is in native CSS.

Example

You can write nested selectors directly:

.container {
  .selector {
    table {
      margin: 2em;
    }
  }
}

Or explicitly use the & nesting selector:

.container {
  & .selector {
    table {
      margin: 2em;
    }
  }
}

The & becomes especially useful when nesting pseudo-classes or modifiers.

Support

Chrome Safari Firefox

More info

MDN Nesting

:has()

There are so many new selectors in css but one that caught my eye is :has().

With :has(), you can select an element based on what it contains.

Example

You can use it to reduce padding on containers that contain images:

.container:has(img) {
  padding: 0;
}

But the most interesting use case is that you can effectively style a parent element based on its children, which was previously impossible in CSS:

div:has(.child-class) {
    background-color: yellow;
}

Support

Chrome Safari Firefox

More info

MDN :has()

Balanced text wrapping

Another small but very practical feature is balanced text wrapping. Making the text readable has always been important for CSS (and especially for blogs like this) and it has been one thing that I regulary Google (how to make blog text more readable). But CSS has added new text-wrap property called balance, which makes one thing better.

Example

.balance {
  text-wrap: balance;
}

That was…simple example. But it is just so. Balance makes text wrap neatly across the lines.

Support

Chrome Safari Firefox

More info

MDN Text-wrap

Scroll-based animations

I remember the first time I saw an animation on a website that progressed as you scrolled down the page. I was completely flabbergasted. I think it was on an Apple website.

Of course, I had to look under the hood to see how it worked. What I found was a combination of Javascript, a <canvas> element, and a lot of images. The code tracked the user’s scroll position and rendered a different image to the canvas depending on that position. I was amazed that something so complex could work so smoothly.

I never implemented that kind of animation myself. But now I could - and much more easily.

CSS has introduced scroll-timeline-name and scroll-timeline-axis (or the shorthand scroll-timeline), along with animation-timeline and the scroll() function. Together, these features allow animations to be driven directly by the user’s scroll position instead of time. This makes it possible to create effects like parallax scrolling and progress-based animations declaratively - using only CSS, without relying on Javascript.

Example

Here’s a simple example from MDN

#container {
  height: 300px;
  overflow-y: scroll;

  scroll-timeline: --square-timeline y;
  /* Firefox supports the non-standard vertical/horizontal syntax */
  scroll-timeline: --square-timeline vertical;

  position: relative;
}

#square {
  background-color: deeppink;
  width: 100px;
  height: 100px;
  position: absolute;
  bottom: 0;

  animation: rotateAnimation 1ms linear;
  animation-timeline: --square-timeline;
}

#stretcher {
  height: 600px;
  background: #dedede;
}

@keyframes rotateAnimation {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

Element container y scroll is added to —square-timeline variable and it is used in #square elements animation timeline. So when container is scrolled, square element rotateAnimation is playing. Kind a neat!

But you can do so much more with this and I highly encourage to go and look all the possibilities. Here’s an example of amazing animation (kind a game even) made with only this tech and css: https://codepen.io/leemeyer/pen/zxrmzQw

Support

Chrome Safari Firefox

More info

MDN Scroll-driven animations


Looking at the all the new features of CSS it seems that it’s becoming more and more amazing to work with. Many things that used to require preprocessors, hacks, or Javascript are now built directly into the platform.

In the next part of this series, I’ll look at other frontend topics such as frontend frameworks. See you then!