State of CSS 2022

State of CSS 2022

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

May 11, 2022 — Updated May 12, 2022

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

Overview #This post is the article form of the talk given at Google IO 2022. It’s not meant to be an in-depth guide on each feature, rather an introduction and brief overview to pique your interest, providing breadth instead of depth. If your interest 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 to the efforts of Interop 2022. Before studying the Interop efforts, it’s important to look at Compat 2021’s efforts.

Compat 2021 #The goals for 2021, driven by developer feedback via surveys, were to stabilize current features, improve the test suite and increase passing scores of browsers for five features:

sticky positioningaspect-ratio sizingflex layoutgrid layouttransform positioning and animationTest scores were raised across the board, demonstrating upgraded stability and reliability. Big congratulations to the teams here!


Interop 2022 #This year, browsers met together to discuss the features and priorities they intended to work on, uniting their efforts. They planned to deliver the following web features for developers:

@layerColor spaces and functionsContainment&LTdialog>Form compatibilityScrollingSubgridTypographyViewport unitsWeb compatThis 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 Interop 2022 work.

Cascade layers #Browser support: chrome 99, Supported 99 firefox 97, Supported 97 edge 99, Supported 99 safari 15.4, Supported 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 to meticulously managed entry stylesheets, where developers needed to load less important styles first and more important styles later. Entire methodologies exist to assist developers in managing this importance, such as ITCSS.

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

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

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


Resources #CSS Cascade 5 specificationCascade layers explainerCascade layers on MDNPost by Una Kravets, Cascade LayersPost by Ahmad Shadeed: Hello, CSS Cascade LayersSubgrid #Browser support: chrome, Not supported × firefox 71, Supported 71 edge, Not supported × safari, Not supported × Source

Before subgrid, a grid inside of another grid couldn’t align itself to its parent cells or grid lines. Each grid layout was unique. Many designers place a single grid over their whole design and constantly align items within it, which couldn’t be done in CSS.

After subgrid, a child of a grid can adopt its parents’ columns or rows as its own, 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 called main, and the left and right columns name their lines fullbleed. Then, each element in the body, &LTnav> and &LTmain>, adopts the named lines from body by setting grid-template-columns: subgrid.


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

Lastly, children of &LTnav> or &LTmain> can align or size themselves using the fullbleed and main 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 now resembles how designers were thinking about the layout.


In the elements panel of devtools you can see which elements are grids and subgrids, which is very helpful for debugging or validating layout.Screenshot from Firefox Devtools

Resources #Spec: by Bramus: Practical CSS Subgrid Video TutorialsContainer queries #Before @container, elements of a webpage could only respond to the size of the whole viewport. This is great for macro layouts, but for micro layouts, where their outer container isn’t the whole viewport, it’s impossible for the layout to 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 query targets, 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 the following video able to be queried by the event elements.Demo by Una Kravets


Here is the CSS for querying the calendar-day container for its size, then adjusting a layout and font sizes:

@container calendar-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 available in the column that it’s dragged to:Demo by Max Böck

Una is correct in assessing the situation as the new responsive. There are many exciting and meaningful design decisions to make when using @container.


Resources #Spec: https:/ new responsive: /new-responsive/#responsive-to-the-containerCalendar demo by Una: container queries: – How we built DesigncemberPost by Ahmad Shadeed: Say Hello To CSS Container Queriesaccent-color #Browser support: chrome 93, Supported 93 firefox 92, Supported 92 edge 93, Supported 93 safari 15.4, Supported 15.4 Source

Before accent-color, when you wanted a form with brand matching colors, you could end Up with complex libraries or CSS solutions that became hard to manage over time. While they gave you all the options, and hopefully included accessibility, the choice to use the built-in components or adopt your own becomes tedious to continue to choose.

After accent-color, one line of CSS brings a brand color to the built-in components. In addition to a tint, the browser intelligently chooses proper contrasting colors for ancillary parts of the component and adapts to system color schemes (light or dark).

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


To learn more about accent-color, check out my post on where I explore many more aspects of this useful CSS property.

Resources #Spec: /accent-color/Post by Bramus: Tint User-Interface Controls with CSS accent-colorColor level 4 and 5 #The web has been dominated by sRGB for the past decades, but in an expanding digital world of high-definition displays and mobile devices pre-equipped with OLED or QLED screens, sRGB is not enough. Furthermore, dynamic pages that adapt to user preferences are expected, and color management has been a growing concern 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 proper contrasting colors, and ensure appropriately vibrant palettes, all while preprocessors 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 JavaScript to users to enable theming and data visualization colors, CSS can do the orchestrating and calculations. CSS is also better equipped to check for support before usage or handle fallbacks gracefully.

@media (dynamic-range: high) {
.neon-pink {
–neon-glow: color(display-p3 1 0 1);
}@supports (color: lab(0% 0 0)) {
.neon-pink {
–neon-glow: lab(150% 160 0);

hwb() #Browser support: chrome 101, Supported 101 firefox 96, Supported 96 edge 101, Supported 101 safari 15, Supported 15 Source

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

Using this color function results in colors from the sRGB color space, the same as 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 #Spec: by Stefan Judis: hwb() – a color notation for humans?Color spaces #The way colors are represented is done with a color space. Each color space offers various features and trade-offs for working with color. Some may pack all the bright colors together; some may line them Up first based on their lightness.

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

color-mix() #Browser support: chrome, Not supported × firefox 88, Supported 88 edge, Not supported × safari 15, Supported 15 Source

Before color-mix(), developers and designers needed preprocessors like Sass to mix the colors before the browser saw them. Most color-mixing functions also didn’t provide the option to specify which color space to do the mixing in, sometimes resulting in confusing results.

After color-mix(), developers and designers can mix colors in the browser, alongside all their other styles, without running build processes or including JavaScript. Additionally, they can specify which color space to do the mixing in, 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 as lighter or darker colors for hover styles. Here’s what that looks like with color-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(in srgb, var(–brand) 25%, black);
–lighter: color-mix(in srgb, var(–brand) 25%, white);

Here follows a theming demo using color-mix(). Try changing the brand color and watch the theme update:

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

Resources #Spec: demo: theming demo: by Fabio Giolito: Create a color theme with these upcoming CSS featurescolor-contrast() #Browser support: chrome, Not supported × firefox, Not supported × edge, Not supported × safari 15, Supported 15 Source

Before color-contrast(), stylesheet authors needed to know accessible colors ahead 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 to properly contrast with that swatch.Example from 2014 Material Design color palettes

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

Here’s a screenshot of an HWB color palette set demo where the text colors are automatically chosen by the browser based on the swatch color:Try the demo

The basics of the syntax look like this, where gray is passed to the function and 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 will pick the highest contrasting color from the selection:

color: color-contrast(gray vs indigo, rebeccapurple, hotpink); Lastly, in case it’s preferable not to pick the highest contrasting color from the list, a target contrast ratio can be provided, and the first color to pass it is chosen:

color: color-contrast(
var(–text-lightest), var(–text-light), var(–text-subdued)
to AA /* 4.5 could also be passed */
); This function can be used for more than just text color, though I estimate that will be its primary use case. Think about how much easier it will be to deliver accessible and legible interfaces once the choosing of proper contrasting colors is built into the CSS language itself.

Resources #Spec: color syntax #Browser support: chrome, Not supported × firefox, Not supported × edge, Not supported × safari 15, Supported 15 Source

Before relative color syntax, to compute on color and make adjustments, the color channels needed to be individually placed into custom properties. This limitation also made HSL the primary color function for manipulating colors because the hue, saturation, or lightness could all be adjusted in a straightforward way with calc().

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 limitations to HSL—manipulations can be done in any color space desired, and many less custom properties need to be created to facilitate it.

In the following syntax example, a base hex is provided and two new colors are created relative to it. The first color –absolute-change creates a new color in LCH from the base color, then proceeds to replace the base color’s lightness with 75%, maintaining the chroma (c) and hue (h). The second color –relative-change creates a new color in LCH from the base color, but this time reduces the chroma (c) by 20%.

.relative-color-syntax {
–color: #0af;
–absolute-change: lch(from var(–color) 75% c h);
–relative-change: lch(from var(–color) l calc(c-20%) h);
} It’s akin to mixing colors, but it’s more similar to alterations than it is mixing. You get to cast a color from another color, getting access to the three channel values as named by the color function used, with an opportunity to adjust those channels. All in all, this is a very cool and powerful syntax for color.

In the following demo I’ve used relative color syntax to create lighter and darker variants of a base color, and used color-contrast() to ensure the labels have proper contrast:Try the demo

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

:root {
–_color-base: #339af0; –color-0: lch(from var(–_color-base) 98% 10 h);
–color-1: lch(from var(–_color-base) 93% 20 h);
–color-2: lch(from var(–_color-base) 85% 40 h);
–color-3: lch(from var(–_color-base) 75% 46 h);
–color-4: lch(from var(–_color-base) 66% 51 h);
–color-5: lch(from var(–_color-base) 61% 52 h);
–color-6: lch(from var(–_color-base) 55% 57 h);
–color-7: lch(from var(–_color-base) 49% 58 h);
–color-8: lch(from var(–_color-base) 43% 55 h);
–color-9: lch(from var(–_color-base) 39% 52 h);
–color-10: lch(from var(–_color-base) 32% 48 h);
–color-11: lch(from var(–_color-base) 25% 45 h);
–color-12: lch(from var(–_color-base) 17% 40 h);
–color-13: lch(from var(–_color-base) 10% 30 h);
–color-14: lch(from var(–_color-base) 5% 20 h);
–color-15: lch(from var(–_color-base) 1% 5 h);

Try the demoHopefully by now you can see how color spaces and different color functions can all be used for different purposes, based on their strengths and weaknesses.

Resources #Spec: color spaces #Before gradient color spaces, sRGB was the default color space used. sRGB is generally reliable, but does have some weaknesses like the gray dead zone.

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

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

background-image: linear-gradient(
to right in hsl,
black, white
);background-image: linear-gradient(
to right in lch,
black, white

Here’s a basic and essential gradient from black to white. Look at the range of results in each color space. Some reach dark black earlier than others, some fade to white too late.

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

For more deep explorations, examples and comments, read this Twitter thread.

Resources #Spec: comparing gradients: notebook: #Browser support: chrome 60, Supported 60 firefox 81, Supported 81 edge 79, Supported 79 safari 15.5, Supported 15.5 Source

Before inert, it was good practice to guide the user’s focus to areas of the page or app that needed immediate attention. This guided focus strategy became known as focus trapping because developers would place focus into an interactive space, listen for focus change events and, if the focus left the interactive space, then it was forced back in. Users on keyboards or screen readers are guided back to the interactive space to ensure the task is complete before moving on.

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

A good example of this is the JavaScript alert() 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 accessible until an alert() was called. Once the alert dialog popup was shown, the rest of the page was frozen, or inert. Users’ focus is placed inside the alert dialog and has nowhere else to go. Once the user interacts and completes the alert function request, the page is interactive again. inert empowers developers to achieve this same guided focus experience with ease.

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

&LTdiv class=”modal”>
&LTh2>Modal Title&LT/h2>
&LTmain inert>
&LT!– cannot be keyboard focused or clicked –>
&LT/body> A dialog is a great example, but inert is also helpful for things such as the slide-out side menu user experience. When a user slides out the side menu, it’s not OK to let the mouse or keyboard interact with the page behind it; that’s a bit tricky for users. Instead, when the side menu is showing, make the page inert, and now users must close or navigate within that side menu, and won’t ever find themselves lost somewhere else in the page with an open menu.

Resources #Spec: Developers: Introducing inertCOLRv1 Fonts #Before COLRv1 fonts, the web had OT-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.

After COLRv1 fonts, the web has smaller footprint, vector-scalable, repositionable, gradient-featuring, and blend-mode powered fonts that accept parameters to customize the font per use case or to match a brand.Image sourced from

Here’s an example from the Chrome Developer blog post about emojis. Maybe you’ve noticed 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 are vector and beautiful:

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

@import url(; Customizing the COLRv1 font is done with @font-palette-values, a special CSS at-rule for grouping and naming a set of customization options into a bundle for later reference. Notice how you specify a custom name just like a custom property, starting with –:

@import url(;

@font-palette-values –colorized {
font-family: “Bungee Spice”;
base-palette: 0;
override-colors: 0 hotpink, 1 cyan, 2 white;
} With –colorized as an alias for the customizations, the last step is to apply the palette to an element that is using the color font family:

@import url(;

@font-palette-values –colorized {
font-family: “Bungee Spice”;
base-palette: 0;
override-colors: 0 hotpink, 1 cyan, 2 white;

.spicy {
font-family: “Bungee Spice”;
font-palette: –colorized;
} Bungee Spice font shown with custom colors, source from more and more variable fonts and color fonts becoming available, web typography is on a very magnificent path towards rich customization and creative expression.

Resources #Github: Developers: developer explainer video: units #Before the new viewport variants, the web offered physical units to assist in fitting viewports. There was one for height, width, smallest size (vmin), and largest side (vmax). These worked well for many things, but mobile browsers introduced a complexity.

On mobile, when loading a page, the status bar with the url is shown, and this bar consumes some of the viewport space. After a few seconds and some interactivity, the status bar may slide away to allow a bigger viewport experience for the user. But when that bar slides out, the viewport height has changed, and any vh units would shift and resize as their target size changed. In later years, the vh unit specifically needed to decide which of the two viewport sizes it was going to use, because it was causing jarring visual layout issues on mobile devices. It was determined that the vh would always represent the largest viewport.

.original-viewport-units {
height: 100vh;
width: 100vw;
–size: 100vmin;
–size: 100vmax;
} After the new viewport variants, small, large, and dynamic viewport units are made available, with the addition of logical equivalents to the physical ones. The idea is to give developers and designers the ability to choose which unit they want to use for their given scenario. Maybe it’s ok to have a small jarring layout shift when the status bar goes away, so then dvh (dynamic viewport height) could be used without worry.

Here’s a complete list of all the new viewport unit options made available with the 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 to achieve their viewport responsive designs.

Resources #Spec: by Bramus: The Large, Small, and Dynamic Viewports:has() #Browser support: chrome, Not supported × firefox, Not supported × edge, Not supported × safari 15.4, Supported 15.4 Source

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

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

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

.parent:has(.child) {…} Here’s an example where a &LTsection> element is the subject, but the selector only matches if one of the children has :focus-visible:

section:has(*:focus-visible) {…} The :has() selector starts to become a fantastic utility once more practical use cases become apparent. For example, it’s not currently possible to select &LTa> tags when they wrap images, making it difficult to teach the anchor tag how 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 &LTfigure> elements and adjusting styles on the images if the figure has a &LTfigcaption>. In the following example, figures with figcaptions are selected and then images within that context. :has() is used and doesn’t change the subject, as the subject we’re targeting is images not figures:

figure:has(figcaption) img {…} The combinations are seemingly endless. Combine :has() with quantity queries and adjust CSS grid layouts based on the number of children. Combine :has() with interactive pseudo class states and create applications that respond in new creative ways.

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

@supports (selector(:has(works))) {
/* safe to use :has() */
} Resources #Spec: CSS :has() selector is way more than a “parent selector”: and beyond #There are still a number of things that will be hard to do after all these amazing features land in 2022. The next section takes a look at some of the remaining problems and the solutions that are actively being developed to resolve them. These solutions are experimental, even though they may be specified or available behind flags in browsers.

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

Loosely typed custom properties #Browser support: chrome 85, Supported 85 firefox, Not supported × edge 85, Supported 85 safari, Not supported × Source

CSS custom properties are amazing. They allow all sorts of things to be stored inside of a named variable, which then can be extended, calculated upon, shared, and more. In fact, they’re so flexible, it would be nice to have some that are less flexible.

Consider a scenario where a box-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 that CSS doesn’t accept there, such as –x: red. The entire shadow breaks if any one of the nested variables is missing or is set to an invalid value type.

This is where @property comes in: –x can become a typed custom property, no longer loose and flexible, but safe with some defined boundaries:

@property –x {
syntax: ‘&LTlength>’;
initial-value: 0px;
inherits: false;
} Now, when the box-shadow uses var(–x) and later –x: red is attempted, red will be ignored as it’s not a &LTlength>. This means the shadow continues to work, even though an invalid value was given to one of its custom properties. Instead of failing, it reverts to its initial-value of 0px.

Animation #In addition to type safety, it also opens Up many doors for animation. The flexibility of CSS syntax makes animating some things impossible, such as gradients. @property helps here because the typed CSS property can inform the browser about a developer’s intent inside of otherwise overly complex interpolation. It essentially limits the scope of possibility insomuch that a browser 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 of an overlay, creating a spotlight focus effect. JavaScript sets the mouse x and y when the alt/opt key is pressed, and then changes the focal-size to a smaller value such as 25%, creating the spotlight focus circle at the mouse position:Try the demo.focus-effect {
–focal-size: 100%;
–mouse-x: center;
–mouse-y: center; mask-image: radial-gradient(
circle at var(–mouse-x) var(–mouse-y),
transparent 0%,
transparent var(–focal-size),
black 0%

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

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

.focus-effect {
–focal-size: 100%;
–mouse-x: center;
–mouse-y: center;

mask-image: radial-gradient(
circle at var(–mouse-x) var(–mouse-y),
transparent 0%,
transparent var(–focal-size),
black 0%

transition: –focal-size .3s ease;
} Try the demoThe browser is now able to animate the gradient size because we’ve reduced the surface area of the modification to just one property and typed the value so the browser can intelligently interpolate the lengths.

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

Resources #Spec: demo: Tricks: in min-width or max-width #Before media query ranges, a CSS media query uses min-width and max-width to articulate 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 both min-width and max-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 ) {

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

Resources #Spec: plugin: media query variables #Before @custom-media, media queries had to repeat themselves over and over, or rely on preprocessors to generate the proper output based on static variables during build time.

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

Naming things is very important: it can align purpose with the syntax, making things easier to share and easier to use in teams. Here are a few custom media queries 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 );

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

@media (–OSdark) {
:root {

} Find a full list of custom media queries I use inside my CSS custom property library Open Props.

Resources #Spec: plugin: selectors is so nice #Before @nest, there was a lot of repetition in stylesheets. It became especially unwieldy when selectors were long and each was targeting small differences. The convenience of nesting is one of the most common reasons for adopting a preprocessor.

After @nest, the repetition is gone. Nearly every feature of preprocessor-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);

What’s most important about nesting to me, besides not repeating article in the nested selector, is the styling context remains within one style block. Instead of bouncing from one selector, and its styles, to another selector with styles (example 1), the reader can remain within the context of an article and see the article owns links inside of it. The relationship and style intent are bundled together, so article gets to appear to own its own styles.

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

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

/* parent owns this, adjusting children */
section:focus-within> article {
border: 1px solid hotpink;
}/* with @nest becomes */

/* article owns this, adjusting itself when inside a section:focus-within */
article {
@nest section:focus-within> & {
border: 1px solid hotpink;

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

Resources #Spec: plugin: by Bramus: The future of CSS: Nesting SelectorsScoping styles is really hard #Before @scope, many strategies existed because styles in CSS cascade, inherit, and are globally scoped by default. These features of CSS are very convenient for many things, but for complex sites and applications, with potentially many different styles of components, the global space and nature of the cascade can make styles feel like they’re leaking.

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

In the following example, BEM naming convention scoping can be reversed into the actual intent. The BEM selector is attempting to scope the color of a header element to a .card container with naming conventions. This requires that the header has this classname on it, completing the goal. With @scope, no naming conventions are required in order to complete the 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 scope nature of CSS. Dark and light themes have to coexist inside a stylesheet, where order matters in determining a winning style. Usually this means dark theme styles come after the light theme; this establishes light as the default and dark 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 the style 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 the following example, img and .content styles are exclusively applied when a child of a .media-block is a sibling or parent of .content:

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

Resources #Spec: CSS way for a masonry layout #Before CSS masonry with grid, JavaScript was the best way to achieve a masonry layout, as any of the CSS methods with columns or flexbox would inaccurately represent the content order.

After CSS masonry with grid, no JavaScript libraries will be required and the content order will be correct.Image and demo from Smashing Magazine

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 can try it today in Firefox.

Resources #Spec: Magazine: can’t help users reduce data
Read More
Share this on to discuss with people on this topicSign Up on now if you’re not registered yet.



Hey! look, i give tutorials to all my users and i help them!
Get Connected!
One of the Biggest Social Platform for Entrepreneurs, College Students and all. Come and join our community. Expand your network and get to know new people!


No comments yet
Knowasiak We would like to show you notifications so you don't miss chats & status updates.
Allow Notifications