State of CSS 2022

Web styling features of today and tomorrow, as seen at Google IO 2022, plus some extras.

Adam Argyle
Adam Argyle

The year 2022 is set to be one of CSS's greatest years, in both features andcooperative browser feature releases, with a collaborative goal to implement 14features!

Note: Watch the talkThe State of CSS from Google I/O '22:

Overview

This post is the article form ofthe talk given at Google IO 2022. It's notmeant to be an in-depth guide on each feature, rather an introduction and briefoverview to pique your interest, providing breadth instead of depth. If yourinterest is piqued, check the end of a section for resource links to more information.

Table of contents

Use the list below to jump to topics of interest:

Browser compatibility

A primary reason so many CSS features are set to cooperatively release is due tothe efforts ofInterop 2022. Before studyingthe Interop efforts, it's important to look atCompat2021’s efforts.

Compat 2021

The goals for 2021, driven by developer feedback via surveys, were to stabilizecurrent features, improve the test suite and increase passing scores of browsersfor five features:

  1. sticky positioning
  2. aspect-ratio sizing
  3. flex layout
  4. grid layout
  5. transform positioning and animation

Test scores were raised across the board, demonstrating upgraded stability andreliability. Big congratulations to the teams here!

Interop 2022

This year, browsers met together to discuss the features and priorities theyintended to work on, uniting their efforts. They planned to deliver thefollowing web features for developers:

  1. @layer
  2. Color spaces and functions
  3. Containment
  4. <dialog>
  5. Form compatibility
  6. Scrolling
  7. Subgrid
  8. Typography
  9. Viewport units
  10. Web compat

This is an exciting and ambitious list that I can't wait to see unfold.

Fresh for 2022

Unsurprisingly, the state of CSS 2022 is dramatically impacted by the Interop2022 work.

Cascade layers

Browser Support

  • Chrome: 99.
  • Edge: 99.
  • Firefox: 97.
  • Safari: 15.4.

Source

Before@layer, the discovered order of loaded stylesheets was very important,as styles loaded last can overwrite previously loaded styles. This led tometiculously managed entry stylesheets, where developers needed to load lessimportant styles first and more important styles later. Entire methodologiesexist to assist developers in managing this importance, such asITCSS.

With@layer, the entry file can pre-define layers, and their order, ahead oftime. Then, as styles load, are loaded or defined, they can be placed within alayer, allowing a preservation of style override importance but without themeticulously managed loading orchestration.

The video shows how the defined cascade layers allow for a more liberated andfreestyle authoring and loading process, while still maintaining the cascade asneeded.

Chrome DevTools is helpful for visualizing which styles are coming from whichlayers:

Screenshot of the Styles sidebar of Chrome Devtools, highlighting how styles appear within new Layer groups.

Resources

Subgrid

Browser Support

  • Chrome: 117.
  • Edge: 117.
  • Firefox: 71.
  • Safari: 16.

Source

Beforesubgrid, a grid inside of another grid couldn't align itself to itsparent cells or grid lines. Each grid layout was unique. Many designers place asingle grid over their whole design and constantly align items within it, whichcouldn't be done in CSS.

Aftersubgrid, a child of a grid can adopt its parents’ columns or rows as itsown, and align itself or children to them!

In the following demo, the body element creates a classic grid of three columns:the middle column is calledmain, and the left and right columnsname theirlinesfullbleed. Then, each element in the body,<nav> and<main>, adopts thenamed lines from body by settinggrid-template-columns: subgrid.

​​body{display:grid;grid-template-columns:[fullbleed-start]auto[main-start]min(90%,60ch)[main-end]auto[fullbleed-end];}body >*{display:grid;grid-template-columns:subgrid;}

Lastly, children of<nav> or<main> can align or size themselves using thefullbleed andmain columns and lines.

.main-content{grid-column:main;}.fullbleed{grid-column:fullbleed;}

Devtools can help you see the lines and subgrids (Firefox only at the moment).In the following image, the parent grid and subgrids have been overlaid. It nowresembles how designers were thinking about the layout.

Screenshot of a subgrid demo, using the Chrome Devtools grid overlay tooling to show the lines defined by CSS.

In the elements panel of devtools you can see which elements are grids andsubgrids, which is very helpful for debugging or validating layout.

Screenshot of the Chrome Devtools Elements panel labelling which elements have grid or subgrid layouts.
Screenshot from Firefox Devtools

Resources

Container queries

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

Before@container, elements of a webpage could only respond to the size of thewhole viewport. This is great for macro layouts, but for micro layouts, wheretheir outer container isn't the whole viewport, it's impossible for the layoutto adjust accordingly.

After@container, elements can respond to a parent container size or style!The only caveat is the containers must declare themselves as possible querytargets, which is a small requirement for a large benefit.

/* establish a container */.day{container-type:inline-size;container-name:calendar-day;}

These styles are what make the Mon, Tues, Wed, Thurs, and Fri columns in thefollowing video able to be queried by the event elements.

Demo byUna Kravets

Here is the CSS for querying thecalendar-day container for its size, thenadjusting a layout and font sizes:

@containercalendar-day(max-width:200px){.date{display:block;}.date-num{font-size:2.5rem;display:block;}}

Here's another example: one book component adapts itself to the space availablein the column that it's dragged to:

Demo byMax Böck

Una is correct in assessing the situation asthe newresponsive. Thereare many exciting and meaningful design decisions to make when using@container.

Resources

accent-color

Browser Support

  • Chrome: 93.
  • Edge: 93.
  • Firefox: 92.
  • Safari: 15.4.

Source

Beforeaccent-color, when you wanted a form with brand matching colors, youcould end up with complex libraries or CSS solutions that became hard to manageover time. While they gave you all the options, and hopefully includedaccessibility, the choice to use the built-in components or adopt your ownbecomes tedious to continue to choose.

Afteraccent-color, one line of CSS brings a brand color to the built-incomponents. In addition to a tint, the browser intelligently chooses propercontrasting colors for ancillary parts of the component and adapts to systemcolor schemes (light or dark).

/* tint everything */:root{accent-color:hotpink;}/* tint one element */progress{accent-color:indigo;}

Light and dark accented HTML elements side by side for comparison.

To learn more aboutaccent-color,check out my post onweb.dev where I explore many more aspects ofthis useful CSS property.

Resources

Color level 4 and 5

The web has been dominated by sRGB for the past decades, but in an expandingdigital world of high-definition displays and mobile devices pre-equipped withOLED or QLED screens, sRGB is not enough. Furthermore, dynamic pages that adaptto user preferences are expected, and color management has been a growingconcern for designers, design systems, and code maintainers.

Not in 2022 though—CSS has a number of new color functions and spaces:- Colors that reach into the HD color capabilities of displays.- Color spaces that match an intent, such as perceptual uniformity.- Color spaces for gradients that drastically change the interpolation outcomes.- Color functions to help you mix and contrast, and choose which space you do the work in.

Before all these color features, design systems needed to precalculate propercontrasting colors, and ensure appropriately vibrant palettes, all whilepreprocessors or JavaScript did the heavy lifting.

After all these color features, the browser and CSS can do all the work,dynamically and just in time. Instead of sending many KBs of CSS and JavaScriptto users to enable theming and data visualization colors, CSS can do theorchestrating and calculations. CSS is also better equipped to check for supportbefore usage or handle fallbacks gracefully.

@media(dynamic-range:high){.neon-pink{--neon-glow:color(display-p3101);}}@supports(color:lab(0%00)){.neon-pink{--neon-glow:lab(150%1600);}}

hwb()

Browser Support

  • Chrome: 101.
  • Edge: 101.
  • Firefox: 96.
  • Safari: 15.

Source

HWB stands for hue, whiteness, and blackness. It presents itself as ahuman-friendly way of articulating color, as it's just a hue and an amount ofwhite or black to lighten or darken. Artists who mix colors with white or blackmay find themselves appreciating this color syntax addition.

Using this color function results in colors from the sRGB color space, the sameas HSL and RGB. In terms of newness for 2022, this doesn’t give you new colors,but it may make some tasks easier for fans of the syntax and mental model.

Resources

Color spaces

The way colors are represented is done with a color space. Each color spaceoffers various features and trade-offs for working with color. Some may pack allthe bright colors together; some may line them up first based on theirlightness.

2022 CSS is set to offer 10 new color spaces, each with unique features toassist designers and developers in displaying, picking, and mixing colors.Previously, sRGB was the only option for working with color, but now CSS unlocksnew potential and a new default color space, LCH.

color-mix()

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 113.
  • Safari: 16.2.

Source

Beforecolor-mix(), developers and designers needed preprocessors likeSass to mix the colors before the browser saw them.Most color-mixing functions also didn't provide the option to specify whichcolor space to do the mixing in, sometimes resulting in confusing results.

Aftercolor-mix(), developers and designers can mix colors in the browser,alongside all their other styles, without running build processes or includingJavaScript. Additionally, they can specify which color space to do the mixingin, or use the default mixing color space of LCH.

Often, a brand color is used as a base and variants are created from it, such aslighter or darker colors for hover styles. Here's what that looks like withcolor-mix():

.color-mix-example{--brand:#0af;--darker:color-mix(var(--brand)25%,black);--lighter:color-mix(var(--brand)25%,white);}

and if you wanted to mix those colors in a different color space, like srgb,change it:

.color-mix-example{--brand:#0af;--darker:color-mix(insrgb,var(--brand)25%,black);--lighter:color-mix(insrgb,var(--brand)25%,white);}

Here follows a theming demo usingcolor-mix(). Try changing the brand colorand watch the theme update:

Enjoy mixing colors in various color spaces in your stylesheets in 2022!

Resources

color-contrast()

Beforecolor-contrast(), stylesheet authors needed to know accessible colorsahead of time. Often a palette would show black or white text on a color swatch,to indicate to a user of the color system which text color would be needed toproperly contrast with that swatch.

Screenshot of 3 Material palettes, showing 14 colors and their appropriate white or black contrast colors for text.
Example from2014 Material Design color palettes

Aftercolor-contrast(), stylesheet authors can offload the task entirely tothe browser. Not only can you employ the browser to automatically pick a blackor white color, you can give it a list of design system appropriate colors andhave it pick the first to pass your desired contrast ratio.

Here's a screenshot of anHWB color palette setdemo where the text colors areautomatically chosen by the browser based on the swatch color:

Screenshot of the HWB demo where each palette has a different pairing of light or dark text, as determined by the browser.
Try thedemo

The basics of the syntax look like this, where gray is passed to the functionand the browser determines if black or white have the most contrast:

color:color-contrast(gray);

The function can also be customized with a list of colors, from which it willpick the highest contrasting color from the selection:

color:color-contrast(grayvsindigo,rebeccapurple,hotpink);

Lastly, in case it's preferable not to pick the highest contrasting color fromthe list, a target contrast ratio can be provided, and the first color to passit is chosen:

color:color-contrast(var(--bg-blue-1)vsvar(--text-lightest),var(--text-light),var(--text-subdued)toAA/* 4.5 could also be passed */);

This function can be used for more than just text color, though I estimate thatwill be its primary use case. Think about how much easier it will be to deliveraccessible and legible interfaces once the choosing of proper contrasting colorsis built into the CSS language itself.

Resources

Relative color syntax

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 113.
  • Safari: 15.

Source

Before relative color syntax, to compute on color and make adjustments, thecolor channels needed to be individually placed into custom properties. Thislimitation also made HSL the primary color function for manipulating colorsbecause the hue, saturation, or lightness could all be adjusted in astraightforward way withcalc().

After relative color syntax, any color in any space can be deconstructed,modified, and returned as a color, all in one line of CSS. No more limitationsto HSL—manipulations can be done in any color space desired, and many lesscustom properties need to be created to facilitate it.

In the following syntax example, a base hex is provided and two new colors arecreated relative to it. The first color--absolute-change creates a new colorin LCH from the base color, then proceeds to replace the base color’s lightnesswith75%, maintaining the chroma (c) and hue (h). The second color--relative-change creates a new color in LCH from the base color, but thistime reduces the chroma (c) by 20%.

.relative-color-syntax{--color:#0af;--absolute-change:lch(fromvar(--color)75%ch);--relative-change:lch(fromvar(--color)lcalc(c-20%)h);}

It's akin to mixing colors, but it's more similar to alterations than it ismixing. You get to cast a color from another color, getting access to the threechannel values as named by the color function used, with an opportunity toadjust those channels. All in all, this is a very cool and powerful syntax forcolor.

In the following demo I've used relative color syntax to create lighter anddarker variants of a base color, and usedcolor-contrast() to ensure thelabels have proper contrast:

Screenshot with 3 columns, each column is either darker or lighter than the center column.
Try thedemo

This function can also be used for color palette generation. Here is a demowhere entire palettes are generated off a provided base color. This one set ofCSS powers all the various palettes, each palette simply provides a differentbase. As a bonus, since I've used LCH, look at how perceptually even thepalettes are—no hot or dead spots to be seen, thanks to this color space.

:root{--_color-base:#339af0;--color-0:lch(fromvar(--_color-base)98%10h);--color-1:lch(fromvar(--_color-base)93%20h);--color-2:lch(fromvar(--_color-base)85%40h);--color-3:lch(fromvar(--_color-base)75%46h);--color-4:lch(fromvar(--_color-base)66%51h);--color-5:lch(fromvar(--_color-base)61%52h);--color-6:lch(fromvar(--_color-base)55%57h);--color-7:lch(fromvar(--_color-base)49%58h);--color-8:lch(fromvar(--_color-base)43%55h);--color-9:lch(fromvar(--_color-base)39%52h);--color-10:lch(fromvar(--_color-base)32%48h);--color-11:lch(fromvar(--_color-base)25%45h);--color-12:lch(fromvar(--_color-base)17%40h);--color-13:lch(fromvar(--_color-base)10%30h);--color-14:lch(fromvar(--_color-base)5%20h);--color-15:lch(fromvar(--_color-base)1%5h);}
Screenshot of 15 palettes all generated dynamically by CSS.
Try thedemo

Hopefully by now you can see how color spaces and different color functions canall be used for different purposes, based on their strengths and weaknesses.

Resources

Gradient color spaces

Before gradient color spaces, sRGB was the default color space used. sRGB isgenerally reliable, but does have some weaknesses like thegray deadzone.

4 gradients in a grid, all from cyan to deeppink. LCH and LAB have more consistent vibrancy, where sRGB goes a bit desaturated in the middle.

After gradient color spaces, tell the browser which color space to use for thecolor interpolation. This gives developers and designers the ability to choosethe gradient they prefer. The default color space also changes to LCH instead ofsRGB.

The syntax addition goes after the gradient direction, uses the newin syntax,and is optional:

background-image:linear-gradient(torightinhsl,black,white);background-image:linear-gradient(torightinlch,black,white);

Here's a basic and essential gradient from black to white. Look at the range ofresults in each color space. Some reach dark black earlier than others, somefade to white too late.

11 color spaces shown comparing black to white.

In this next example, black is transitioned to blue because it's a known problemspace for gradients. Most color spaces creep into purple during colorinterpolation or, as I like to think of it, as colors travel inside their colorspace from point A to point B. Since the gradient will take a straight line frompoint A to point B, the shape of the color space drastically changes the stopsthat the path takes along the way.

Note:okLCH andokLAB are specialized color spaces that account for various drifts, like this one into purple, making them especially accurate for gradients.

11 color spaces shown comparing blue to black.

For more deep explorations, examples and comments, readthis Twitterthread.

Resources

inert

Browser Support

  • Chrome: 102.
  • Edge: 102.
  • Firefox: 112.
  • Safari: 15.5.

Source

Beforeinert, it was good practice to guide the user's focus to areas of thepage or app that needed immediate attention. This guided focus strategy becameknown as focus trapping because developers would place focus into an interactivespace, listen for focus change events and, if the focus left the interactivespace, then it was forced back in. Users on keyboards or screen readers areguided back to the interactive space to ensure the task is complete beforemoving on.

Afterinert, no trapping is required because you can freeze or guard entiresections of the page or app. Clicks and focus change attempts are just simplynot available while those parts of a document are inert. One could also think ofthis like guards instead of a trap, whereinert is not interested in makingyou stay somewhere, rather making other places unavailable.

A good example of this is the JavaScriptalert() function:

Website is shown as interactive, then an alert() is called, and the page is no longer active.

Notice in the preceding video how the page was mouse and keyboard accessibleuntil analert() was called. Once the alert dialog popup was shown, the restof the page was frozen, orinert. Users’ focus is placed inside the alertdialog and has nowhere else to go. Once the user interacts and completes thealert function request, the page is interactive again.inert empowersdevelopers to achieve this same guided focus experience with ease.

Here's a small code sample to show how it works:

<body>  <div>    <h2>Modal Title</h2>    <p>...<p>    <button>Save</button>    <button>Discard</button>  </div>  <main inert>    <!-- cannot be keyboard focused or clicked -->  </main></body>

A dialog is a great example, butinert is also helpful for things such as theslide-out side menu user experience. When a user slides out the side menu, it'snot OK to let the mouse or keyboard interact with the page behind it; that's abit tricky for users. Instead, when the side menu is showing, make the pageinert, and now users must close or navigate within that side menu, and won'tever find themselves lost somewhere else in the page with an open menu.

Resources

COLRv1 Fonts

Before COLRv1 fonts, the web hadOT-SVG fonts,also an open format for fonts with gradients and built-in colors and effects.These could grow very large though, and while they allowed editing the text,there wasn't much scope for customization.

AfterCOLRv1 fonts, the webhas smaller footprint, vector-scalable, repositionable, gradient-featuring, andblend-mode powered fonts that accept parameters to customize the font per usecase or to match a brand.

Comparison visualization and bar chart, showing how COLRv1 fonts are sharper and smaller.
Image sourced fromhttps://developer.chrome.com/blog/colrv1-fonts/

Here's an example from the Chrome Developer blog post about emojis. Maybe you'venoticed that if you scale up the font size on an emoji, it doesn't stay sharp.It's an image and not vector art. Often in applications when an emoji is used,it's swapped out for a higher quality asset. With COLRv1 fonts, the emojis arevector and beautiful:

Icon fonts could do some amazing things with this format, offering customduo-tone color palettes, and more. Loading a COLRv1 font is just like any otherfont file:

@importurl(https://fonts.googleapis.com/css2?family=Bungee+Spice);

Customizing the COLRv1 font is done with@font-palette-values, a special CSSat-rule for grouping and naming a set of customization options into a bundle forlater reference. Notice how you specify a custom name just like a customproperty, starting with--:

@importurl(https://fonts.googleapis.com/css2?family=Bungee+Spice);@font-palette-values--colorized{font-family:"Bungee Spice";base-palette:0;override-colors:0hotpink,1cyan,2white;}

With--colorized as an alias for the customizations, the last step is to applythe palette to an element that is using the color font family:

@importurl(https://fonts.googleapis.com/css2?family=Bungee+Spice);@font-palette-values--colorized{font-family:"Bungee Spice";base-palette:0;override-colors:0hotpink,1cyan,2white;}.spicy{font-family:"Bungee Spice";font-palette:--colorized;}
Screenshot of the Bungee Spice font with the word DUNE.
Bungee Spice font shown with custom colors, source fromhttps://developer.chrome.com/blog/colrv1-fonts/

With more and more variable fonts and color fonts becoming available, webtypography is on a very magnificent path towards rich customization and creativeexpression.

Resources

Viewport units

Graphic showing how the device screen and the browser window and an iframe, all have different viewports.

Before the new viewport variants, the web offered physical units to assist infitting viewports. There was one for height, width, smallest size (vmin), andlargest side (vmax). These worked well for many things, but mobile browsersintroduced a complexity.

On mobile, when loading a page, the status bar with the url is shown, and thisbar consumes some of the viewport space. After a few seconds and someinteractivity, the status bar may slide away to allow a bigger viewportexperience for the user. But when that bar slides out, the viewport height haschanged, and anyvh units would shift and resize as their target size changed.In later years, thevh unit specifically needed to decide which of the twoviewport sizes it was going to use, because it was causing jarring visual layoutissues on mobile devices. It was determined that thevh would always representthe largest viewport.

.original-viewport-units{height:100vh;width:100vw;--size:100vmin;--size:100vmax;}

After the new viewport variants, small, large, and dynamic viewport units aremade available, with the addition oflogicalequivalents to the physical ones. The idea isto give developers and designers the ability to choose which unit they want touse for their given scenario. Maybe it's ok to have a small jarring layout shiftwhen the status bar goes away, so thendvh (dynamic viewport height) could beused without worry.

A graphic with three phones to help illustrate DVH, LVH and SVH. The DVH   example phone has two vertical lines, one between the bottom of the search bar   and the bottom of the viewport and one between above the search bar (under the   system status bar) to the bottom of the viewport; showing how DVH can be either   of these two lengths. LVH is shown in the middle with one line between the   bottom of the device status bar and the button of the phone viewport. The last is   the SVH unit example, showing a line from the bottom of the browser search bar   to the bottom of the viewport

Here's a complete list of all the new viewport unit options made available withthe new viewport variants:

Height viewport units
​​.new-height-viewport-units{height:100vh;height:100dvh;height:100svh;height:100lvh;block-size:100vb;block-size:100dvb;block-size:100svb;block-size:100lvb;}
Width viewport units
.new-width-viewport-units{width:100vw;width:100dvw;width:100svw;width:100lvw;inline-size:100vi;inline-size:100dvi;inline-size:100svi;inline-size:100lvi;}
Smallest viewport side units
.new-min-viewport-units{--size:100vmin;--size:100dvmin;--size:100svmin;--size:100lvmin;}
Largest viewport side units
.new-max-viewport-units{--size:100vmax;--size:100dvmax;--size:100svmax;--size:100lvmax;}

Hopefully these will give developers and designers the flexibility needed toachieve their viewport responsive designs.

Resources

:has()

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 121.
  • Safari: 15.4.

Source

Before:has(), thesubjectof aselector was always at the end. For example, thesubject of this selector is a list item:ul > li. Pseudo selectors can alterthe selector but they don't change the subject:ul > li:hover orul >li:not(.selected).

After:has(), a subject higher in the element tree can remain the subjectwhile providing a query about children:ul:has(> li). It is easy to understandhow:has() got a common name of "parent selector", as the subject of theselector is now the parent in this case.

Here's a basic syntax example where the class.parent remains the subject butis only selected if a child element has the.child class:

.parent:has(.child){...}

Here's an example where a<section> element is the subject, but the selectoronly matches if one of the children has:focus-visible:

section:has(*:focus-visible){...}
Note::focus-within already behaves similarly tosection:has(*:focus-visible) and should be used instead.

The:has() selector starts to become a fantastic utility once more practicaluse cases become apparent. For example, it's not currently possible to select<a> tags when they wrap images, making it difficult to teach the anchor taghow to change its styles when in that use case. It is possible with:has()though:

a:has(>img){...}

These have all been examples where:has() only looks like a parent selector.Consider the use case of images inside of<figure> elements and adjustingstyles on the images if the figure has a<figcaption>. In the followingexample, figures with figcaptions are selected and then images within thatcontext.:has() is used and doesn't change the subject, as the subject we'retargeting is images not figures:

figure:has(figcaption)img{...}

The combinations are seemingly endless. Combine:has() withquantityqueries and adjustCSS grid layouts based on the number of children. Combine:has() withinteractive pseudo classstates and createapplications that respond in new creative ways.

Checking for support is made simple with@supports anditsselector()function, which tests if the browser understands the syntax before using it:

@supports(selector(:has(works))){/* safe to use :has() */}

Resources

2022 and beyond

There are still a number of things that will be hard to do after all theseamazing features land in 2022. The next section takes a look at some of theremaining problems and the solutions that are actively being developed toresolve them. These solutions are experimental, even though they may bespecified or available behind flags in browsers.

The upshot from the next sections should be comfort that the problems listedhave many people from many companies seeking resolution—not that these solutionsare going to be released in 2023.

Loosely typed custom properties

Browser Support

  • Chrome: 85.
  • Edge: 85.
  • Firefox: 128.
  • Safari: 16.4.

Source

CSScustom propertiesare amazing. They allow all sorts of things to be stored inside of a namedvariable, which then can be extended, calculated upon, shared, and more. Infact, they're so flexible, it would be nice to have some that are less flexible.

Consider a scenario where abox-shadow uses custom properties for its values:

box-shadow:var(--x)var(--y)var(--blur)var(--spread)var(--color);

This all works well until any one of the properties is changed into a value thatCSS doesn't accept there, such as--x: red. The entire shadow breaks if anyone of the nested variables is missing or is set to an invalid value type.

This is where@property comes in:--x canbecome a typed custom property, no longer loose and flexible, but safe with somedefined boundaries:

@property--x{syntax:'<length>';initial-value:0px;inherits:false;}

Now, when thebox-shadow usesvar(--x) and later--x: red is attempted,red will be ignored as it's not a<length>. This means the shadow continuesto work, even though an invalid value was given to one of its custom properties.Instead of failing, it reverts to itsinitial-value of0px.

Animation

In addition to type safety, it also opens up many doors for animation. Theflexibility of CSS syntax makes animating some things impossible, such asgradients.@property helps here because the typed CSS property can inform thebrowser about a developer's intent inside of otherwise overly complexinterpolation. It essentially limits the scope of possibility insomuch that abrowser can animate aspects of a style that it couldn't before.

Consider this demo example, where a radial gradient is used to make a portion ofan overlay, creating a spotlight focus effect. JavaScript sets the mouse x and ywhen the alt/opt key is pressed, and then changes the focal-size to a smallervalue such as 25%, creating the spotlight focus circle at the mouse position:

Try thedemo
.focus-effect{--focal-size:100%;--mouse-x:center;--mouse-y:center;mask-image:radial-gradient(circleatvar(--mouse-x)var(--mouse-y),transparent0%,transparentvar(--focal-size),black0%);}

Gradients can't be animated though. They are too flexible and too complex forthe browser to "just derive" how you want them to animate. With@property,though, one property can be typed and animated in isolation, for which thebrowser can easily understand the intent.

Video games that use this focus effect always animate the circle, from a largecircle to a pinhole circle. Here's how to use@property with our demo so thebrowser animates the gradient mask:

@property--focal-size{syntax:'<length-percentage>';initial-value:100%;inherits:false;}.focus-effect{--focal-size:100%;--mouse-x:center;--mouse-y:center;mask-image:radial-gradient(circleatvar(--mouse-x)var(--mouse-y),transparent0%,transparentvar(--focal-size),black0%);transition:--focal-size.3sease;}
Try thedemo

The browser is now able to animate the gradient size because we've reduced thesurface area of the modification to just one property and typed the value so thebrowser can intelligently interpolate the lengths.

@property can do so much more, but these small enablements can go a long way.

Resources

Was inmin-width ormax-width

Before media query ranges, a CSS media query usesmin-width andmax-width toarticulate over and under conditions. It may look like this:

@media(min-width:320px){}

After media query ranges, the same media query could look like this:

@media(width>=320px){}

A CSS media query using bothmin-width andmax-width may look like this:

@media(min-width:320px)and(max-width:1280px){}

After media query ranges, the same media query could look like this:

@media(320px<=width<=1280px){}

Depending on your coding background, one of those will look much more legiblethan the other. Thanks to the spec additions, developers will be able to choosewhich they prefer, or even use them interchangeably.

Resources

No media query variables

Before@custom-media, media queries had to repeat themselves over and over, orrely on preprocessors to generate the proper output based on static variablesduring build time.

After@custom-media, CSS allows aliasing media queries and the referencing ofthem, just like a custom property.

Naming things is very important: it can align purpose with the syntax, makingthings easier to share and easier to use in teams. Here are a few custom mediaqueries that follow me between projects:

@custom-media--OSdark(prefers-color-scheme:dark);@custom-media--OSlight(prefers-color-scheme:light);@custom-media--pointer(hover)and(pointer:coarse);@custom-media--mouse(hover)and(pointer:fine);@custom-media--xxs-and-above(width>=240px);@custom-media--xxs-and-below(width<=240px);

Now that they're defined, I can use one of them like this:

@media(--OSdark){:root{}}

Find afull list of custom mediaqueries I use inside my CSS customproperty libraryOpen Props.

Resources

Nesting selectors is so nice

Before@nest, there was a lot of repetition in stylesheets. It becameespecially unwieldy when selectors were long and each was targeting smalldifferences. The convenience of nesting is one of the most common reasons foradopting a preprocessor.

After@nest, the repetition is gone. Nearly every feature ofpreprocessor-enabled nesting will be made available built into CSS.

article{color:darkgray;}article >a{color:var(--link-color);}/* with @nest becomes */article{color:darkgray;  & >a{color:var(--link-color);}}
Note: Use the syntax today withPostCSS and thePostCSS Nesting plugin.

What's most important about nesting to me, besides not repeatingarticle inthe nested selector, is the styling context remains within one style block.Instead of bouncing from one selector, and its styles, to another selector withstyles (example 1), the reader can remain within the context of an article andsee the article owns links inside of it. The relationship and style intent arebundled together, soarticle gets to appear to own its own styles.

The ownership could also be thought of as centralization. Instead of lookingaround a stylesheet for relevant styles, they can all be found nested togetherwithin a context. This works with parent to child relationships, but also withchild to parent relationships.

Consider a component child that wants to adjust itself when in a differentparent context, as opposed to the parent owning the style and changing a child:

/* parent owns this, adjusting children */section:focus-within >article{border:1pxsolidhotpink;}/* with @nest becomes *//* article owns this, adjusting itself when inside a section:focus-within */article{@nestsection:focus-within > &{border:1pxsolidhotpink;}}

@nest helps overall with healthier style organization, centralization, andownership. Components can group and own their own styles, instead of having themspread amongst other style blocks. It may seem small in these examples, but itcan have very large impacts, for both convenience and legibility.

Warning: Nesting more than four or five levels can become more troublesome than repeating selectors. We advise you to not nest so deep, and instead begin a new selector blog and nest within it.

Resources

Scoping styles is really hard

Browser Support

  • Chrome: 118.
  • Edge: 118.
  • Firefox: behind a flag.
  • Safari: 17.4.

Source

Before@scope, many strategies existed because styles in CSS cascade, inherit,and are globally scoped by default. These features of CSS are very convenientfor many things, but for complex sites and applications, with potentially manydifferent styles of components, the global space and nature of the cascade canmake styles feel like they're leaking.

After@scope, not only can styles be scoped to only within a context, like aclass, they can also articulate where the styles end and do not continue tocascade or inherit.

In the following example,BEM naming conventionscoping can be reversed into the actual intent. The BEM selector is attemptingto scope the color of aheader element to a.card container with namingconventions. This requires that the header has this classname on it, completingthe goal. With@scope, no naming conventions are required in order to completethe same goal without marking up the header element:

.card__header{color:var(--text);}/* with @scope becomes */@scope(.card){header{color:var(--text);}}

Here's another example, less component-specific and more about the global scopenature of CSS. Dark and light themes have to coexist inside a stylesheet, whereorder matters in determining a winning style. Usually this means dark themestyles come after the light theme; this establishes light as the default anddark as the optional style. Avoid the ordering and scope battling with@scope:

​​@scope(.light-theme){a{color:purple;}}@scope(.dark-theme){a{color:plum;}}

To complete the story here,@scope also allows the establishing of where thestyle scope ends. This can't be done with any naming convention or preprocessor;it's special and only something CSS built-in to the browser can do. In thefollowing example,img and.content styles are exclusively applied when achild of a.media-block is a sibling or parent of.content:

@scope(.media-block)to(.content){img{border-radius:50%;}.content{padding:1em;}}

Resources

No CSS way for a masonry layout

Before CSS masonry with grid, JavaScript was the best way to achieve a masonrylayout, as any of the CSS methods with columns or flexbox would inaccuratelyrepresent the content order.

After CSS masonry with grid, no JavaScript libraries will be required and thecontent order will be correct.

Screenshot of the masonry layout which shows numbers traveling along the top, then going down.
Image and demo from Smashing Magazine
https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/

The preceding demo is achieved with the following CSS:

.container{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:masonry;}

It's comforting to know that this is on the radar as a missing layout strategy,plus you cantry it today inFirefox.

Resources

CSS can't help users reduce data

Browser Support

  • Chrome: behind a flag.
  • Edge: behind a flag.
  • Firefox: not supported.
  • Safari: not supported.

Source

Before theprefers-reduced-data media query, JavaScript and a server couldchange their behavior based on a user’s operating system or browser "data saver"option, but CSS could not.

After theprefers-reduced-data media query, CSS can join the user experienceenhancement and play its part in saving data.

@media(prefers-reduced-data:reduce){picture,video{display:none;}}

The preceding CSS is used inthis media scrollcomponent and the savingscan be huge. Depending on how large the visiting viewport is, the more savingsto be had on page load. Saving continues as users interact with the mediascrollers. The images all haveloading="lazy" attributes on them and that,combined with CSS hiding the element entirely, means a network request for theimage is never made.

Screenshot of a TV show carousel interface with many thumbnails and titles shown.

For my testing, on a medium sized viewport, 40 requests and 700kb of resourceswere initially loaded. As a user scrolls the media selection, more requests andresources are loaded. With CSS and the reduced data media query, 10 requests and172kb of resources are loaded. That's half a megabyte of savings and the userhasn't even scrolled any of the media, at which point there are no additionalrequests made.

Screenshot of a TV show carousel interface with no thumbnails and many titles shown.

There are more advantages to this reduced data experience than just datasavings. More titles can be seen and there's no distracting cover images tosteal attention. Many users browse in a data saver mode because they pay permegabyte of data—it's really nice to see CSS able to help out here.

Resources

Scroll snap features are too limited

Before these scroll snap proposals, writing your own JavaScript to manage acarousel, slider, or gallery could quickly get complex, with all the observersand state management. Also, if not careful, the natural scrolling speeds couldget normalized by script, making user interaction feel a bit unnatural andpotentially clunky.

New APIs

snapChanging()

As soon as the browser has released a snap child, this event fires. This allowsUI to reflect the lack of a snap child and the indeterminate snap state of thescroller, as it's now being used and will land somewhere new.

document.querySelector('.snap-carousel').addEventListener('snapchanging',event=>{console.log('Snap is changing',event.snappedTargetsList);});
snapChanged()

As soon as the browser has snapped to a new child and the scroller is rested,this event fires. This lets any UI that depends on the snapped child to updateand reflect the connection.

document.querySelector('.snap-carousel').addEventListener('snapchanged',event=>{console.log('Snap changed',event.snappedTargetsList);});
scroll-start

Scrolling doesn't always begin at the start. Consider swipeable components whereswiping left or right triggers different events, or a search bar that on pageload is initially hidden until you scroll to the top. This CSS property letsdevelopers specify that a scroller should begin at a specific point.

:root{--nav-height:100px}.snap-scroll-y{scroll-start-y:var(--nav-height);}
:snap-target

This CSS selector will match elements in a scroll snap container that arecurrently snapped by the browser.

.card{--shadow-distance:5px;box-shadow:0var(--shadow-distance)5pxhsl(00%0%/25%);transition:box-shadow350msease;}.card:snapped{--shadow-distance:30px;}

After these scroll snap proposals, making a slider, carousel, or gallery is mucheasier as the browser now offers conveniences for the task, eliminatingobservers and scroll orchestration code in favor of using built-in APIs.

It's still very early days for these CSS and JS features, but be on the lookoutfor polyfills that can help adoption, and testing, of them soon.

Resources

Cycling between known states

Beforetoggle(), only states built into the browser already could be leveragedfor styling and interaction. The checkbox input, for example, has:checked, aninternally managed browser state for the input that CSS is able to use forchanging the element visually.

Aftertoggle(), custom states can be created on any element for CSS to changeand use for styling. It allows groups, cycling, directed toggling, and more.

In the following example, the same effect of a list item strikethrough oncomplete is achieved but without any checkbox elements:

<ul class='ingredients'>   <li>1 banana   <li>1 cup blueberries  ...</ul>

And the relevant CSStoggle() styles:

li{toggle-root:checkself;}li:toggle(check){text-decoration:line-through;}

If you're familiar with state machines, you may notice how much crossover thereis withtoggle(). This feature will let developers build more of their stateinto CSS, hopefully resulting in clearer and more semantic ways of orchestratinginteraction and state.

Resources

Customizing select elements

Before<selectmenu>, CSS didn't have the ability to customize<option>elements with rich HTML or change much about the display of a list of options.This led developers to load external libraries that recreated much of thefunctionality of a<select>, which ended up being a lot of work.

After<selectmenu>, developers can provide rich HTML for options elements andstyle them as much as they need, while still meeting accessibility requirementsand providing semantic HTML.

In the following example, taken from the<selectmenu>explainerpage, a new select menu is createdwith some basic options:

<selectmenu>  <option>Option 1</option>  <option>Option 2</option>  <option>Option 3</option></selectmenu>

CSS can target and style the element's parts:

.my-select-menu::part(button){color:white;background-color:red;padding:5px;border-radius:5px;}.my-select-menu::part(listbox){padding:10px;margin-top:5px;border:1pxsolidred;border-radius:5px;}

A select looking menu with red accent colors.

You can try the<selectmenu> element on Chromium in Canary with the webexperiments flag enabled. Watch out in 2023 and beyond for customizable selectmenu elements.

Resources

Anchoring an element to another

Beforeanchor(), position absolute and relative were position strategiesprovided for developers to have child elements move around within a parentelement.

Afteranchor(), developers can position elements to other elements, regardlessof them being a child or not. It also allows developers to specify which edge toposition against, and other niceties for creating position relationships betweenelements.

The explainer has a few great examples and code samples provided, if you'reinterested in learning more.

Resources

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2022-05-11 UTC.