What is new with CSS in 2022?

# The Hotlist (Cross-Browser Support)

I’m quite confident the following features will see support across all browsers sometime in 2022. Some features already have support in one or more browsers, with others to follow. Learning one of the following CSS features listed below in 2022 will pay off.

# Container Queries

Container Queries — part of css-contain-3 — allow authors to style elements according to the size or appearance of a container. For size-based container queries this is similar to a @media query, except that it will evaluate against the size of a parent container instead of the viewport.

For style-based container queries, you conditionally apply styles based on the calculated value of another CSS property.

main, aside {
  container: inline-size;
}

.media-object {
  display: grid;
  grid-template: `img` auto `content` auto / 100%;
}

@container (inline-size > 45em) {
  .media-object {
    grid-template: `img content` auto / auto 1fr;
  }
}

 

More info: CSS Container Queries: A First Look + Demo →

Relevant Issues:

# Cascade Layers

With Cascade Layers, you can control cascade ordering for same-origin rules properly. You do this by layering your styles using the @layer at-rule. Layers appear before specificity and order of appearance in the cascade.

@import(reset.css) layer(reset); /* 1st layer */

@layer base { /* 2nd layer */
  form input {
    font-size: inherit; 
  }
}

@layer theme { /* 3rd layer */
  input {
    font-size: 2rem;
  }
}

Cascade layers will be supported in Chromium 99 and Firefox 97. They’re also supported in Safari TP 133 (behind a flag), and I expect them to ship in the first quarter of 2022.

More info: The Future of CSS: Cascade Layers (CSS @layer) →

Relevant Issues:

# Color Functions

The css-color-5 specification comes with a few niceties when it comes to working with color. It adds two new functions: color-mix(), color-contrast(), and extends existing ones with relative color syntax.

 

The color-mix() function allows you to mix two colors in a given color space.

.text-primary-dark {
  color: color-mix(var(--theme-primary), black 10%);
}
.text-primary-darker {
  color: color-mix(var(--theme-primary), black 20%);
}

 

The color-contrast() function allows you select the best color from a list of colors that meets or exceeds the contrast criteria when compared to a certain base color.

/* Compares wheat against tan, sienna, and #d2691e */
/* Sienna will be selected as it has a contstast of 4.273 against wheat, which exceeds the threshold of AA-large (3) */
color-contrast(wheat vs tan, sienna, #d2691e to AA-large)

 

With the relative color syntax you can manipulate and convert any color to any format.

:root {
  --color: #ff0000;
}

.selector {
  /* change the transparency */
  color: hsl(from var(--color) h s l / .5);
  
  /* change the hue */
  color: hsl(from var(--color) calc(h + 180deg) s l);
  
  /* change the saturation */
  color: hsl(from var(--color) h calc(s + 5%) l);
}

These additions plug functionalities typically provided by CSS Preprocessors. Already supported in WebKit/Safari.

More Info:

Relevant Issues:

 

# Viewport Units

When working with Viewport Units there’s this longstanding and extremely annoying bug in Safari on iOS where it does not play nice with the vh unit. Setting a container to 100vh will result in an element that’s a wee bit too tall: MobileSafari ignores parts of its UI when calculating 100vh.

Leaving the vh unit as it is, the CSS Working Group introduced several new viewport definitions and accompanying new viewport relative lengths in the css-values-4 specification:

  • svh/svw: 1% of the Small Viewport height/width
  • lvh/lvw: 1% of the Large Viewport height/width
  • dvh/dvw: 1% of the Dynamic Viewport height/width

Also available as logical variants, such as svi/svb

More Info: The Large, Small, and Dynamic Viewports →

Relevant Issues:

The same spec also introduces logs of new mathematical functions to use in calculations. Definitely worth checking out.

 

# :has()

The CSS :has() relational pseudo-class is part of selectors-4. It allows you to more finely select elements, as an element will only match if any of the extra selectors passed into :has() matches at least one element. It’s often dubbed “the parent selector” but it’s way more than that.

/* Matches <a> elements that contain an <img> child */
a:has(img) { … }

/* Matches <a> elements that directly contain an <img> child */
a:has(> img) { … }

/* Matches <section> elements that don’t contain any heading elements: */
section:not(:has(h1, h2, h3, h4, h5, h6))

/* Matches <h1> elements only if they have a <p> element directly following them */
h1:has(+ p) { … }

More Info: The CSS :has() selector is way more than a “Parent Selector” →

Relevant Issues:

 

# Overscroll Behaviour

With the CSS overscroll-behavior property you can override the default behavior when “overscrolling” a container. With it, you can prevent a full reload when a pull-to-refresh gesture is performed, disable rubber banding, contain scrolling within one layer, etc.

Long supported in Firefox (version 36) and Chromium (version 63). Safari is — finally! — catching up.

More info: Customizing Pull-to-Refresh and Overflow Effects with CSS overscroll-behavior

Relevant Issues:

 

# Subgrid

When nesting Grids it’s difficult to line nested grid items up with the main grid. That’s where subgrid comes in: by setting grid-template-columns or grid-template-rows to subgrid, it will line up with the parent grid.

.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: subgrid;
}

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

Already supported in Firefox. Chromium is a WIP.

More info: Practical CSS Subgrid Video Tutorials →

Relevant Issues:

 

# Accent Color

Part of css-ui-4 is accent-color

CSS accent-color from the CSS UI specification is here to tint elements with one line of CSS, saving you from customization efforts by providing a way to bring your brand into elements.
form {
  accent-color: hotpink;
}

Using it, you can even create some pixel art using checkboxes ????

Supported in Chromium 93+ and Firefox 92+.

More info: CSS accent-color

Relevant Issues:

 

# Media Query Ranges

Thanks to new additions in mediaqueries-4, certain media queries can be rewritten using Media Query Ranges, a syntax that uses ordinary mathematical comparison operators.

/* Old Way */
@media (max-width: 750px) {
	…
}
/* New Way */
@media (width <= 750px) {
	…
}

I didn’t pick up any signal that Safari is actively pursuing this, but let’s assume I’m wrong on this one …

More info: Media Queries Level 4: Media Query Range Contex →

Relevant Issues:

 

# The Notlist (Experimental / Single-Browser Support)

The following features won’t be supported in most browsers, or perhaps only behind feature flags. You can invest your time in them if you want, but we won’t see full browser support by the end of 2022.

# Nesting

Last summer the First Public Working Draft of css-nesting-1 got published, a module that introduces the ability to nest one style rule inside another.

???? Working Draft (WD)?

The Working Draft (WD) Maturity Level is the first official phase of the W3C Recommendation Track, and is considered the design phase of a W3C spec. In this phase the CSS Working Group will explore, revise and refine the contents of the module. The first published version of the WD is called the “First Public Working Draft”, which kicks off the WD phase.

From thereon a spec can become a Candidate Recommendation (CR) to finally land on being a Recommendation (REC). In between those three stages there are two transition stages: Last Call Working Draft (LCWD) and Proposed Recommendation (PR)

In visual form, the Recommendation Track looks something like this:

See An Inside View of the CSS Working Group at W3C for more details on all phases.

table.colortable {
  & td {
    text-align: center;
    &.c { text-transform: uppercase }
    &:first-child, &:first-child + td { border: 1px solid black }
  }
  & th {
    text-align: center;
    background: black;
    color: white;
  }
  @nest footer & {
    font-size: 0.8em;
  }
}

It’s a groundbreaking addition to CSS that I really wanted to include in the hotlist above. Unfortunately, I expect only Chromium (and maybe one other vendor) to gain support. Let’s hope I’m wrong on this one. Perhaps a preprocessor extension can open the way to actively using this one in 2022.

More info: The future of CSS: Nesting Selectors →

Relevant Issues:

 

# @scope

With css-cascade-5 (which introduced Cascade Layers) cut off, work on css-cascade-6 has already begun. It introduces Scoping in the Cascade, a way to scope styles to part of the DOM tree.

<div class="dark-theme">
  <a href="#">plum</a>
  <div class="light-theme">
    <a href="#">also plum???</a>
  </div>
</div>
/* When .light-theme and .dark-theme get nested, you may not get the expected result */
.light-theme a { color: purple; }
.dark-theme a { color: plum; }

/* By scoping, we can fix this, as the a elements will be styled by their nearest scope */
@scope (.light-scheme) {
  a { color: darkmagenta; }
}
@scope (.dark-scheme) {
  a { color: plum; }
}

You can also define a scoping limit (lower boundary) for the scope.

It currently is way too early to further dig into it. I expect Chromium to experiment with it by the end of 2022.

 

# @when / @else