@argyleink shared a topic about the features of CSS at the 4th LondonCSS 2020 CSS event. After looking at the PPT of this topic, there are some interesting new things in it. Based on this PPT, I have sorted out nearly 24 new features of CSS, which interested students can continue to read.

What follows is divided into six sections: Web dynamics, Web typography, Web performance, Web accessibility, Web beautification, and more.

Some of the new CSS features discussed below are still experimental, so if you’re interested, you won’t have to read them. You can also choose parts of the book that interest you. (^_^)

CSS London 2020

CSS London 2020 isn’t a one-off event, it’s the fourth edition as OF this writing, and each edition has a different topic. There are three main topics in this event:

  • @argyleink的WHAT’S NEW WITH CSS?

  • @stephanie Eckles STYLE STAGE AND MODERN CSS The Style Stage is a modern version of the CSS Zen Garden, allowing CSS practitioners at any level to redesign styles based on the same HTML while increasing their familiarity with modern CSS features

  • @Cassie Evans’s PAINTING WITH SVG: Introduces SVG’s drawing related features, and @Cassie Evans has another article on SVG that is well worth the time: Making Lil ‘Me-Part 1

Now, let’s get back to today’s topic: new CSS features you shouldn’t miss in 2020.

Web dynamic effect

Motion Blur

Motion Blur is a concept that I first encountered. Motion Blur is a blur effect that only blurs an object when it is moving. Usually this blur is applied in the direction in which the object is moving. This is what happens when you try to take a picture of a moving object.

When you take a picture of an object (or a person) like the one below, this blur occurs because the object (the person motion) is moving faster than the exposure time required for the camera to take the picture, so the object will appear multiple times in the final photo because it is moving at a critical moment.

Motion blur is rarely considered when creating motion effects, and is also absent in CSS and SVG animations, so most Web animations look rather blunt. In other words, if we add some blur to the motion of the animation, it will make the motion more realistic.

To this end, @Argyleink proposed an implementation of the W3C specification related to motion blur. He recommended a method for developers (CSSer), Add motion-rendering and Motion-shutter-Angle to CSS animation.

.animated-layer {/* Animated animation */ Animation: rotate.5s Linear infinite; / * to the dynamic fuzzy * motion - rendering engine requests can accept inherit | initial | auto | none | * / motion blur values - rendering: the blur; /* This is similar to the shutter Angle of a camera. Used to control the fuzzy quantity or fuzzy strength * motion - shutter - Angle can be subjected to arbitrary Angle values inherit | initial | auto = 180 deg | < a href="https://codepen.io/airen/pen/oNxQoLw">0deg, ... , 720deg] */ motion-shutter-angle: 180deg; } @keyframes rotate { to { transform: rotate(1turn); }}Copy the code

Here’s a simple example:

It doesn’t look like motion blur is there.

Here is a picture with motion effects provided in the document to show you the effect of motion blur:

While motion-rendering and motion-shutter-Angle are just a proposal and are still a long way from TR, for certain scenes (animated scenes) you can use CSS filters to simulate the effect, such as this one:

The code is simple:

.blur { animation: blur 250ms; } @keyframes blur { 0% { -webkit-filter: blur(0px); } 50% { -webkit-filter: blur(5px); } 100% { -webkit-filter: blur(0px); }}Copy the code

In addition, the motion-Rendering and Motion-shutter-Angle properties that are currently available are just a proposal. In the comments discussed on Github, there are also suggestions to change these two properties to:

Filter: motion-blur(5px) motion-squash(2px) // or transform-fiilter: motion-blur(180deg) // or Transition-filter: motion-blur(180deg)Copy the code

That is to say, the above provided CSS dynamic fuzzy properties are not completely determined, with the development of the CSS dynamic fuzzy properties, everything is possible.

Taking a look at the SVG world, it is much easier to simulate dynamic blur effects in SVG than in the CSS world. You can use filters in SVG to simulate dynamic blur effects:

The image above is from @Lucas Bebber’s Motion Blur Effect with SVG tutorial.

If you are interested in learning more about filters in SVG, you can read:

* SVG 1.1: Filter Effects

  • SVG Filters 101

Interestingly, @Michelle Barker wrote a Demo at Codepen that uses CSS box-shadow to simulate dynamic effects:

If you really want motion blur in your animations, but you’re afraid to use CSS or SVG-related features that aren’t supported in major browsers, here’s a JavaScript library: MotionBlurJS:

Look at the dynamic blur effect implemented with MotionBlurJS:


Web developers often encounter animations that use scrolling to trigger certain elements. For example, the page scroll bar is rolled to a certain position and the title is fixed at the top. The top of the page shows your progress (scroll indicator); Or something we call parallax scrolling and so on. In the past, most of these effects have been achieved with JavaScript, using DOM events to view the scroll position and change the style of the element based on that position. If possible, it is better to use IntersectionObserver. The introduction of this aspect can be read:

  • Trust is Good, Observation is Better — Intersection Observer V2

  • How to do scroll-linked animations the right way

However, there is now a draft CSS for this, namely Scrok-Linked Animations. That said, in the future, we can use the @scrolltimeline property of CSS directly to implement some of the aforementioned animation effects.

@scroll-timeline = @scroll-timeline <timeline-name> { <declaration-list> }
Copy the code

Two simple examples are provided in the specification, for example, the impact of two spheres. This action is controlled by the scroll position. Simply put, as the page rolls down, the left and right balls move slowly toward the center until they collide and form a circle. On the other hand, as the page scrolls up, the middle circle slowly separates from the left and right circles.

The CSS code looks something like this:

@media (prefers-reduced-motion: no-preference) {
    div.circle {
        animation-duration: 1s;
        animation-timing-function: linear;
        animation-timeline: collision-timeline;

    #left-circle {
        animation-name: left-circle;

    #right-circle {
        animation-name: right-circle;

    #union-circle {
        animation-name: union-circle;
        animation-fill-mode: forwards;
        animation-timeline: union-timeline;

    @scroll-timeline collision-timeline {
        source: selector(#container);
        orientation: block;
        start:  200px;
        end: 300px;

    @scroll-timeline union-timeline {
        source: selector(#container);
        orientation: block;
        start:  250px;
        end: 300px;

    @keyframes left-circle {
        to { transform: translate(300px) }

    @keyframes right-circle {
        to { transform: translate(350px) }

    @keyframes union-circle {
        to { opacity: 1 }
Copy the code

If you’re using JavaScript, you can do something like this:

if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
    const scrollableElement = document.querySelector('#container');

    const collisionTimeline = new ScrollTimeline({
        source: scrollableElement,
        start: CSS.px(200),
        end: CSS.px(300)

    const left = leftCircle.animate({ transform: 'translate(300px)' }, 1000);
    left.timeline = collisionTimeline;

    const right = leftCircle.animate({ transform: 'translate(350px)' }, 1000);
    right.timeline = collisionTimeline;

    const union = unionCircle.animate({ opacity: 1 }, { duration: 1000, fill: "forwards" });
    union.timeline = new ScrollTimeline({
        source: scrollableElement,
        start: CSS.px(250),
        end: CSS.px(300)
Copy the code

Here’s another example of a rolling timer:

In the example above, we used a gradient to simulate an effect, but with @Scrolltimeline we could do something like this:

@media (prefers-reduced-motion: no-preference) { @scroll-timeline progress-timeline { source: selector(#body); start: 0; end: 100%; } @keyframes progress { to { width: 100%; } } #progress { width: 0px; height: 30px; background: red; animation: 1s linear forwards progress progress-timeline; }}Copy the code

If you use the Web Animation API, you can do something like this:

if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
    var animation = div.animate({ width: '100%' }, { duration: 1000, fill: "forwards" });
    animation.timeline = new ScrollTimeline(
            start: 0,
            end: CSS.percent(100)
Copy the code

@Argyleink also provided a simple example in the slide he shared. As you scroll through the page, you’ll notice that the/between two numbers is constantly rotated:

The above example effect is based on the Web Animation API to implement the @Scrolltimeline effect, but it still needs its corresponding Polyfill.

import 'https://flackr.github.io/scroll-timeline/dist/scroll-timeline.js' const counter = document.querySelector('main')  const slashes = document.querySelectorAll('.slash') slashes.forEach(slash => { slash.animate({ transform: <a href="https://drafts.csswg.org/scroll-animations-1/">'rotateZ(0)','rotateZ(4turn)'] },{ duration: 1000, fill: 'both', timeline: new ScrollTimeline({ scrollSource: counter, fill: 'both', }), }) })Copy the code

For more information about the @scrok-timeline, see [scrok-Linked Animations(drafts.csswg.org/scroll-anim…

Web typography

Let’s take a look at some of the new features that will be available in the layout:


The CSS Grid layout is the only two-dimensional layout in the Web layout pattern, and it’s the one I personally agree with (at least so far I haven’t found one more powerful than the Grid). If you’ve never worked with a Grid layout, you can think of it as the original table layout, because there are many concepts that are very similar.

As Web layout technology continues to evolve and browsers continue to evolve, Grid layouts are now being used more and more, especially this year, with Grid and Flexbox becoming more and more common:

The above data is from MDN. For more details, please read MDN Browser Compatibility Report 2020

To take you back to the 1990s, when the Web was dominated by table layouts, it was common to find tables nested within tables when using a table layout:

The Grid layout is similar, and you will also encounter Grid nested grids.

<! -- HTML --> <div class="grid"> <div class="item"> <div class="subitem"></div> </div> </div> /* CSS */ .grid { display: grid; grid-template-columns: repeat(9, 1fr); grid-template-rows: repeat(4, minmax(100px, auto)); } .item { grid-column: 2 / 7; grid-row: 2 / 4; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 80px); } .subitem { grid-column: 2 / 4; grid-row: 1 / 4; }Copy the code

The grid is nested within the grid, and the grid orbits are independent of each other, but there are some problems with the alignment of elements in the subgrid. The CSS Grid Layout Module Level 2(drafts.csswg.org/css-grid-2) has added the subGrid attribute (Firefox 71 supports this attribute).

We can set the subgrid in grid-template-columns and grid-template-rows when the grid is nested. In this way, we can change the code in the above example to:

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

.item {
    grid-column: 2 / 7;
    grid-rows: 2 / 4;

    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: subgrid;

.subitem {
    grid-column: 3 / 6;
    grid-row: 1 / 4;
Copy the code

In this way, the subgrid inherits the grid track of its parent grid, which in turn affects its dimension (size) when any type of auto-tuning is used (for example, auto, min-content, max-content, etc.).

Subgrid is useful for some of our UI layouts:

Let’s look at a concrete example of a subgrid. Note, please use Firefox 71+ to view the above Demo, see the following effects:

Subgrid, like grid, is a complex system. It may take more articles to explain subgrid. If you are interested in the Subgrid, here are some other articles to read:

  • CSS Grid Layout Module Level 2: subgrid

  • Hello subgrid!

  • CSS Grid Level 2: Here Comes Subgrid

  • MDN: Subgrid

  • Irregular-shaped Links with Subgrid

Media query conditions and environment variables that can be used for dual and foldable screens

With the continuous development of technology, we are faced with more and more personalized terminals, such as dual-screen and foldable screen devices on the market now or will be:

As Web developers, we will eventually have to deal with the adaptation of these terminals. So far, the CSS world has been able to handle the adaptation by using screen-spanning media to query conditions and env(fold-left), env(fold-top), env(fold-height), and env(fold-width) environment variables:

With these features, we can easily achieve a layout like this:

If you’re interested, read the following articles, which are some of the most detailed tutorials on dual and foldable screens on the web:

  • How android foldable screens have changed interaction design and development

  • The changes that the collapsible Web might bring to us

  • Web API for dual and collapsible screens

Waterfall flow layout

The navigation navigation Layout is also one of the typical Web layouts:

Although you can simulate waterfall flow layouts using CSS multi-column layouts, Flexbox layouts, Grid layouts, etc., many students prefer to use a few JavaScript libraries such as Masoonry for waterfall flow layouts.

In order to enable waterfall flow to be implemented directly with native CSS, there has been a proposal in the community back in 2017 to implement waterfall flow with native CSS. Unfortunately, this is still only an experimental property and is only supported in Firefox Nightly browser.

.masonry {
    display: grid;
    gap: 20px;
    grid: masonry / repeat(auto-fill, minmax(250px, 1fr));
Copy the code

Take this Demo for example.

In order to be able to view the Demo in Firefox Nightly browser, you need to make sure it’s enabled. If not, enter about:config in the Address bar of Firefox Nightly browser, then search for layer.css.grid-template-address-value.enabled and set it to true:

Then restart the browser and view the Demo. You should see something like this:


Gap, Gap, Gap, Gap, Gap You can’t avoid dealing with block to block and line to line spacing in a Web layout.

In the world of CSS, to control the spacing between elements, the outer distance in the box model, namely the margin attribute, is generally used. However, in many cases, the use of margin to control the spacing between elements cannot well meet the demands of designers. For example, an element has spacing between elements, but no spacing between its parent container. For such scenarios, using the GAP attribute is much easier to control than using margin.

Note that the figure above is from the article Next Generation Web STYLING.

The biggest feature of the CSS gap property itself is that the gap is relative to the flow, which means that it dynamically changes according to the direction of the content flow. For example, when the writing pattern changes, the gap changes automatically.

In the early days of CSS, there were many types of gap, which were called differently in different container formats, such as column-gap on multi-column Containers:

body {
    column-gap: 35px;
Copy the code

But on Grid Containers they are also called grid-row-gap and grid-column-gap.

In addition, it can be applied to Flexbox Containers, although Flexbox didn’t have flex-row-gap or Flex-column-gap properties in the early days.

Note that there is no gap property in the Flexbox Module, but this does not affect our use of the gap property in the Flexbox layout because it is integrated into the CSS Box Alignment Module Level 3 Module. And gap is a shorthand property for row-gap and column-gap:

We can now use gap in multi-column layouts, Flexbox layouts and grid layouts like this:

// Multi__column {gap: 5ch} // Flexbox layout. Flexbox {display: flex; Gap: 20px} // Grid layout. Grid {display: Grid; gap: 10vh 20% }Copy the code

As we can see from the above example, gap is shorthand for row-gap and column-gap, and gap can take either one or two values. When gap has only one value, it means that row-gap and column-gap have the same value. When gap has two values, the first value is row-gap and the second value is column-gap.

.gap { gap: 10px; } // equivalent to.gap {row-gap: 10px; column-gap: 10px } .gap { gap: 20px 30px; } // equivalent to.gap {row-gap: 20px; column-gap: 30px; }Copy the code

In particular, while CSS has added gap properties (row-gap and column-gap), the earlier grid-row-gap and grid-column-gap properties in the Grid are also available.


Aspect-ratio is an attribute used to calculate the aspect ratio of an element Box in the CSS Box Sizing Module Level 4. Before this property, CSS used other methods to simulate aspect ratio effects. Such as:

.aspectration { position: relative; Height: 0; height: 0; /* The height of the container is controlled by the padding. */ width: 100%; }. Aspectration < a href = "https://codepen.io/rachelandrew/pen/WNrRZaV" > data - thewire = "16:9"] {padding - top: 56.25%; } .aspectration[data-ratio="4:3"]{ padding-top: 75%; }Copy the code

If your browser supports aspect-ratios, you can use them as follows:

div {
    aspect-ratio: 1 / 1;
Copy the code

IO /airen/pen/v…


:target and :target-within are both pseudo-elements in the Selectors Level 4 module. Perhaps many students are more familiar with :target, and even use the characteristics of :target pseudo-element to make UI interaction effects such as Tab, Accordion and Modal.

For example, the following accordion effect is made using the target pseudo-element:

Here’s a quick look at :target and :target-within.

In some document languages, the URL of the document can be further pointed to specific elements in the document through fragments of the URL. The element pointed to in this way is the target element of the document. Where the fragment identifier is the part of the URL immediately followed by a #, such as #top or # FontNote1. You may already use them to create in-page navigation, such as the common “jump links.” With the: Target pseudo-class, we can highlight the part of the document that corresponds to this segment, and we can do this without JavaScript.

For example, here’s a simple example:

<! -- HTML --> <h3>Table of Contents</h3> <ol> <li><a href="#p1">Jump to the first paragraph! </a></li> <li><a href="#p2">Jump to the second paragraph! </a></li> <li><a href="#nowhere">This link goes nowhere, because the target doesn't exist.</a></li> </ol> <h3>My Fun Article</h3> <p id="p1">You can target <i>this paragraph</i>  using a URL fragment. Click on the link above to try out! </p> <p id="p2">This is <i>another paragraph</i>, also accessible from the links above. Isn't that delightful? </p> /* CSS */ p:target { background-color: gold; } /* Add a pseudo-element to the target element */ p:target::before {font: 70% sans-serif; Content: "excellent"; color: limegreen; margin-right: .25em; } /* Use italic style in target element */ p:target I {color: red; }Copy the code

Click on the link in the example and you can see something like this:

The :target-within pseudo-class applies to elements to which the :target pseudo-class applies, and to elements whose descendants in the Flat Tree (including non-element nodes, such as text nodes) match the conditions matching :target-within.

article:target-within { background-color: hsl(var(–surfaceHSL) / 10%); } There are a number of other pseudo-class selectors that have been added to the Level 4 selector module. If you are interested in these new types of selectors, listen to @Adam Argyle and @ana Tudor’s CSS Podcast. The 14th edition of the Podcast is devoted to pseudo-class selectors.

CSS Logical Properties

If you have read the article “Left to Right in the Web” or have been exposed to the characteristics of CSS writing mode, you will find that the physical properties we are familiar with before are difficult to meet the layout requirements in different language environments, such as English and Arabic, Japanese and Mongolian, etc., the margin-left we set may not be margin-left. Width may not be width.

At this point, CSS logical properties are especially important. In other words, the emergence of logical properties will bring about a big change in the box model we are familiar with:

Here is the correspondence between the familiar physical and logical properties:

For the difference between block axis and inline axis, let’s use the same diagram:

The block and inline axes are related to the CSS writing mode writing-mode and direction and the HTML dir. In other words:

  • Block axis: Defines the flow of web site documents (blocks of elements). The writing mode of CSS writing affects the direction of the block axis

  • Inline axis: It mainly defines the direction of the flow of text on the site, that is, how the text is read. The DIRECTION of the CSS or THE DIR of the HTML can affect the direction of the inline axis

For more information on the effects of writing-mode, direction, and dir on Web typography, see Left to Right on the Web.

Max min () () and clamp ()

The min(), Max (), and clamp() functions are called “comparison functions.” This was discussed in detail in the article Chat min(), Max () and Clamp () functions. I won’t go into details here, just three pictures to show their functions.

We can use min() to set the maximum:

As opposed to min(), Max () returns the maximum value. Use Max () to set a minimum:

Clamp (), unlike min() and Max (), returns an interval value. Clamp () takes three arguments, namely clamp(MIN, VAL, MAX), where MIN represents the minimum value, VAL the preferred value, and MAX the maximum value. Between them:

  • If VAL is between MIN and MAX, use VAL as the return value of the function.

  • If VAL is greater than MAX, use MAX as the return value of the function;

  • If VAL is less than MIN, then MIN is used as the return value of the function

Here is an example of clamp() trying to drag the size of the browser window and you can see something like this:

Let’s look at a few more features related to text:

Leading – the trim and text – edge

Line-height in Web typography has long been a source of confusion and headache for Web developers, mainly because it is such a complex system in CSS. His calculations always involve a number of factors:

@Iamvdo’s Deep Dive CSS: Font Metrics, Line-height and Vertical-align explains this in depth!

When restoring the UI, the line height of the text always makes it difficult to calculate the spacing between blocks:

To solve this problem, the CSS Inline Layout Module Level 3 adds leading-trim and text-edge properties. This allows us to remove the extra spacing in each font so that we can better calculate the spacing between adjacent block elements.

h1 { 
    leading-trim: both;
    text-edge: cap alphabetic;
Copy the code

The example above starts by using text-edge to tell the browser that the desired text edge is cap height and Alphabetic baseline. Then use leading-trim to trim both sides of the text.

Note that leading-trim only affects text boxes; it does not cut the text in them.

The two simple lines of CSS in the example create a clean text box containing text (independent of the line-height related features). This helps achieve more precise spacing and creates a better visual hierarchy.

Select * from CSS where text-edge and leading-trim are acceptable:

text-edge: leading | <a href="https://matthiasott.com/notes/the-thing-with-leading-in-css?ref=heydesigner"> text | cap | ex | ideographic | ideographic-ink ] [ text | alphabetic | ideographic | ideographic-ink ]?

leading-trim: normal | start | end | both
Copy the code

In addition to the specification, if you are interested in the leading-trim feature, you can also read the following articles:

  • [The Thing With Leading in CSC]

  • Leading-Trim: The Future of Digital Typesetting

  • Rethinking line-sizing and leading-trim

: : grammar – error and: : spelling error

::grammar-error and ::spelling-error are two very interesting pseudo-element selectors. Grammar error means grammatical error, and Spelling error means Spelling error. In fact, these two phenomena are often seen when we write text, may be due to hand error, a word or punctuation mark wrong, or even grammatical mistakes. For this, we always want to have some kind of reminder, such as color difference, adding some underline, etc. :

The highlighted pseudo-elements in the CSS Pseudo-Elements Module Level 4 are shown here:

  • ::grammar-error: The browser adds a style to the grammatically incorrect section of text

  • ::spelling-error: The browser adds a style to a piece of text that is spelled incorrectly

Not all attributes in CSS can be applied to these two pseudo-elements, so far, Only color, background-color, cursor, text-text-color, text-shadow, outline, text-decoration, fil-color, stroke-color And stroke-width can be used for these two pseudo-elements.

:root::spelling-error { 
    text-decoration-line: spelling-error; 

:root::grammar-error  { 
    text-decoration-line: grammar-error; 

<a href="https://drafts.csswg.org/css-values-4">spellcheck]::spelling-error {
    text-decoration: wavy underline var(--neon-red);

[grammarcheck]::grammar-error {
    text-decoration: wavy underline var(--neon-blue);
Copy the code

Added relative units: CAP, LH, RLH, VI and VB

[CSS units and values (drafts.csswg.org/css-values-…) the units are:

But cap, lh, RLH, vi and VB are not mentioned in the relative units.

From the above table, cap, LH, and RLH are all calculated based on the font and row level of the element. I use the following image to describe the Cap Height, Line Height, etc of a font:

Web performance

Contain and the content – the visibility

These two properties belong to the CSS container module, and their biggest feature should be to help Web developers improve the performance of Web pages:

When the contents of the container change, the browser will examine all the elements on the page, considering that other elements might change as well. Browsers have been doing this for a long time, and people are used to it. On the other hand, developers know if the element they are currently modifying is independent and affects other elements. So if the developer can tell the browser this information through CSS, then the browser doesn’t have to worry about other elements, that’s the perfect thing. The contain attribute in the CSS container module provides this capability.

Take a look at An example from @Manuel Rego Casasnovas in An Introduction to CSS Containment:

Let’s say a page has many elements, in this example we have 10,000 such elements:

<div class="item"> <div>Lorem ipsum... </div> </div>Copy the code

Use JavaScript’s textContent API to dynamically change the contents of div. Item > div:

const NUM_ITEMS = 10000; const NUM_REPETITIONS = 10; function log(text) { let log = document.getElementById("log"); log.textContent += text; } function changeTargetContent() { log("Change \"targetInner\" content..." ); // Force layout. document.body.offsetLeft; let start = window.performance.now(); let targetInner = document.getElementById("targetInner"); targetInner.textContent = targetInner.textContent == "Hello World!" ? "BYE" : "Hello World!" ; // Force layout. document.body.offsetLeft; let end = window.performance.now(); let time = window.performance.now() - start; log(" Time (ms): " + time + "\n"); return time; } function setup() { for (let i = 0; i < NUM_ITEMS; i++) { let item = document.createElement("div"); item.classList.add("item"); let inner = document.createElement("div"); inner.style.backgroundColor = "#" + Math.random().toString(16).slice(-6); inner.textContent = "Lorem ipsum..." ; item.appendChild(inner); wrapper.appendChild(item); }}Copy the code

Without contain, even if the changes are on a single element, the browser’s rendering of the layout will take a lot of time because it traverses the entire DOM tree (in this case, the DOM tree is huge because it has 10,000 DOM elements) :

In this case, the size of the div is fixed, and what we change in the internal div will not overflow it. Therefore, we can apply contain: strict to a project so that when changes occur inside the project, the browser does not need to access other nodes; it can stop checking the content on that element and avoid going outside.

The Content-visibility property in the CSS container module can significantly affect the speed of the first download and first render. In addition, you can immediately interact with newly rendered content without waiting for the rest of the content to load. This property forces the user agent to skip tags and draw elements that are not on the screen. In fact, it works like lazy loading, except instead of loading resources, they are rendered.

Simply put, the CONTent-visibility property of CSS skips off-screen content renderings, including layouts and Paint, until the actual Layout rendering is needed. So you can use it to load faster with the initial user, and you can interact with the content on the screen faster.

Above, from content-Visibility by @una Kravets and @Vladimir Levin: The new CSS property that free-living your rendering performance(web.dev/content-vis… : Auto attribute can improve the initial loading performance of free-living content areas by up to 7 times.

For an introduction to this aspect, you can also read:

  • CSS Containment in Chrome 52

  • Helping Browsers Optimize With The CSS Contain Property

  • An introduction to CSS Containment

  • Let’s Take a Deep Dive Into the CSS Contain Property

  • [CSS Containment] ((developer.mozilla.org/zh-CN/docs/…).

  • content-visibility: the new CSS property that boosts your rendering performance

  • Short note on content-visibility: hidden

  • Using content-visibility: hidden

  • Using content-visibility: auto

Data services

The Data service is Data Saver. What does that mean? Without explaining it, I’ll just use a code to describe it:

@media (prefers-reduced-data: reduce) { header { background-image: url(/grunge.avif); }}Copy the code

Everyone is familiar with @media (Priss-reduced-data: reduce). Yes, it’s what we call CSS media queries. With a slight difference, this media query is based on the user’s preferences in the Settings on the device. For example, when the user turns on “Low Data Mode” on the device, the grunge.avif image is loaded, which can help the iPhone application reduce network Data usage:

So far, CSS media queries have provided multiple media features that can be determined by the user’s preferences on the device. For example, starting with iOS13+, the iPhone offers DarkMode (Gibas-color-scheme) :

For example, the Group-reduced-motion media query is used to detect whether the user’s system has animation attenuation enabled:

CSS Media Queries Level 5(www.w3.org/TR/mediaque…) Added to the module.

In addition to the above, there are some media queries that we rarely see, such as:

@media (hover: hoveer) {}

@media (hover: none) and (pointer: coarse) {}

@media (hover: none) and (pointer: fine) {}

@media print and (min-resolution: 300dpi) {}

@media (scan: interlace) {}

@media (update) {}

@media(environment-blending: additive){}

@media (color) {}
Copy the code

Variable font

Variable fonts are an interesting CSS feature, often referred to as “variable fonts”. Here’s a Demo:

The goal of variable fonts is to make your site perform better while giving users more options and extensions. Variable fonts are similar to vector graphics, allowing different values to be defined for various font axes. In variable font design, there are generally five registration axes, including font, font width, italic, and optical size. Each registration axis has a corresponding four-letter tag that maps to existing CSS properties:

In addition to registration axes, font designers can also include custom axes. Custom axes make variable fonts more creative because there is no limit to the scope, definition, or number of custom axes. Similar to the registration axis, the custom axis has a corresponding four-letter marker. However, the letter marker for the custom axis must be uppercase. For example, if you define a registration axis that is grade, the corresponding letter marker is GRAD.

Like the code for the above example effect:

.text {
    font-weight: 800;
    font-style: italic;
    font-variation-settings: "SSTR" 183, "INLN" 648, "TSHR" 460, "TRSB" 312, "TWRM" 638, "SINL" 557, "TOIL" 333, "TINL" 526, "WORM" 523;
    transition: font-variation-settings .28s ease;

.text:hover {
    font-weight: 400;
    font-style: normal;
    font-variation-settings: "SSTR" 283, "INLN" 248, "TSHR" 160, "TRSB" 112, "TWRM" 338, "SINL" 257, "TOIL" 133, "TINL" 426, "WORM" 223;
Copy the code

In Firefox, we can also adjust the value of the relevant variable font registry axis provided by the “Font” option in the developer tools:

After adjusting, you get the new code:

p { font-size: 60px; line-height: 37px; Letter - spacing: 0.113 em. font-variation-settings: "SSTR" 450, "INLN" 741, "TSHR" 292, "TRSB" 497, "TWRM" 173, "SINL" 557, "TOIL" 728, "TINL" 526, "WORM" 523, "TFLR" 362, "TRND" 516, "SWRM" 536, "TSLB" 509; font-weight: 491; }Copy the code

The corresponding effect is as follows:

Web accessibility

: focus – visible and focus – the within

It’s been easy for me to confuse :focus-within with :focus-visible. In fact :focus-within and :focus-visible are both user action pseudo-class selectors in CSS selectors Level 4. Earlier in Exploring CSS Selectors Level 4, we talked about :focus-within, but not about: Focus-visible.

Also, as mentioned in the CSS: Focus-within tutorial, : Focus-within is a very handy way to get focus. Elements of the :focus-within pseudo-class are valid when the element itself or its descendants gain focus. The behavior of a pseudo-class selector is essentially a parent selector behavior, where the state of the child elements affects the style of the parent. Since this “parent selector” behavior needs to be triggered by user behavior, it is “post-rendered” and does not conflict with existing rendering mechanisms.

If you’re confused by all this, check out the Demo below. You’ll find that when

The code to implement the above effect is very simple:

Form :focus within {box-shadow: 0px 0.2em 2.5em #c4c4c4; The transform: scale (1.025); }Copy the code

For the :focus-visible pseudo-class, this applies when the elements match the :focus pseudo-class and the client’s (UA) heuristic determines that the focus should be visible. This selector effectively displays different forms of focus depending on the user’s input mode (mouse vs. keyboard).

Simply put, pressing the TAB key on the keyboard gives you a different focus than clicking the mouse. Such as:

*/ a:focus {} /* * 1\. If the link has focus, but the browser usually does not display the default focus style, it will override the focus style * 2\. */ a:focus:not(:focus-visible) {} /* A :focus:not(:focus-visible) {}Copy the code

Let’s look at a specific case. In this example, clicking the link with the mouse is different from pressing the TAB key on the keyboard to bring the link into focus:

Web beautification

Color Level 4 and Level 5

The CSS Color Level 4 and Level 5 modules give us some new properties for Color use, such as:

  • < HWB()> : HWB(short for white-White-black) is another way of specifying colors, similar to HSL, which describes an initial hue, followed by a certain amount of white and black mixed into the base hue

  • <lab()> and < LCH ()> : Lab is composed of a brightness channel and two color channels. In the Lab color space, each color is represented by three numbers: L(brightness), A (component from green to red), and B (component from blue to yellow). Lch represents the brightness, saturation and hue of the color, respectively

  • <gray()> : Gray is a fully desaturated color, and the gray() function notation simplifies the specification of this common set of colors, so only one numeric parameter is required to specify the gray level of the color

  • <color()> : This function allows you to specify a color in a specific color space

  • <device-cmyk()> : This function is a combination of cmyK (cyan, magenta, yellow, and black) to generate a specific color on the device

  • : Matches the color according to the user operating system

  • Color-mix () : This function takes two specifications and returns the result of their mixing in a specified amount in a given color space

  • Color-contrast () : This function uses first one color (usually a background color), then a list of two or more colors, from which it selects the color with the highest brightness contrast to a single color

  • Color-adjust () : This function accepts a specification and returns the resulting color extension adjusted for that color in the given color space via the specified transformation function: According to the existing color (this is known as the “original color”) in the function of the target color in the color space is RGB () < >, < rgba () >, < an HSL () >, < hsla () >, < HWB () >, < lab () > and < LCH () > the extension of the color

For Web developers, one of the biggest impressions is the big change in syntax rules:

Here are two examples:

// Color Level 4
.colour {
    --fn-notation: hsl(2rad 50% 50% / 80%);
    --neon-pink: color(display-p3 1 0 1);
    --gray: lch(50% 0 0);
    --fallback: color(lab 25 25 25, display-p3 1 0 1, #ccc);

// Color Level 5
.colour {
    --pink: color-mix(red, white);
    --halfpink: color(var(--pink) / 50%);
    --halfred: rgb(from #f00 / 50%);
    --darkred: hsl(from red h s calc(l * .25));
Copy the code

@media (dynamic-range: high) {display-p3;}

@media (dynamic-range: high) { .neon-red { --neon-glow: color(display-p3 1 0 0); } .neon-pink { --neon-glow: color(display-p3 1 0 1); } .neon-purple { --neon-glow: color(display-p3 0 0 1); } .neon-blue { --neon-glow: color(display-p3 0 1 1); } .neon-green { --neon-glow: color(display-p3 0 1 0); } .neon-yellow { --neon-glow: color(display-p3 1 1 0); } .neon-white { --neon-glow: color(display-p3 1 1 1); }}Copy the code

Note that the colors in the display-P3 color space are more vibrant than those in the sRGB color space:

Alternatively, display-p3 is a superset of sRGB, about 35% larger:

In the preview version of Safari 97, you can see the display-p3 effect:

Similarly, the sRGB color space can be converted to the display-p3 color space in the color() function, as shown in the figure below:

Here’s an example of display-p3 from @Adam Argyle at Codepen:


:: Marker is also a pseudo-element of CSS and is now included in the CSS Lists Module Level 3 specification. The specification covers properties related to lists and calculators, For example, we are familiar with list-style-type, list-style-position, list-style, list-item, counter increment, counter-reset, counter() and counters().

Setting the list-item value on DISPLAY generates a Markers and several attributes that control the position and style of the Markers. It also defines a counter (a special numeric object), which is often used to generate the default content of the Markers.

You may not be familiar with Markers, but you should be familiar with the attributes that are associated with a List. A CSS List can include the following attributes:

In fact, the ::marker of CSS is similar to the pseudo-element ::before (or ::after), and the effect of marker can be controlled by content and attr() together. It should be remembered that the following points should be done to generate the content of personalized Marker:

Display: inline list-item display: inline list-item display: inline list-item

You need to explicitly set list-style type to none

Add content using content (you can also add content using attr() with data-*)

For example, here’s a small example:

In addition, before ::marker was supported by the browser, it was common to use CSS counters to implement some ordered list with personalization, such as the following effect:

Is not very interesting, there is about :: Marker pseudo-elements more detailed introduction, also can read: “Custom Bullets with CSS :: Marker” article.


Here’s a picture:

The above effect is achieved using text-emphasis in CSS. In the past, we would add some decorative effects to the text, except for bold weight, font style, text-shadow, and lines above and below the text (of course, You can also use other CSS to achieve special effects. But it’s a bit difficult to achieve the effect shown above. But with the advent of text-emphasis, this becomes much simpler.

Text-emphasis is a feature of the CSS Text Decoration Module specification. There are other attributes associated with text-emphasis at Level 3, such as text-text-style and text-text-emphasis. Text-emphasis is the shorthand for the two attributes. There is also a text-text-position attribute that specifies the position of the token:

.emphasis {
    text-emphasis: triangle rebeccapurple;
    text-emphasis-position: under;
Copy the code

The text-emphasis-skip attribute was added to the Level 4 specification.

The specific effects are as follows:


The color-Scheme property comes from CSS Color Adjustment Module Level 1. If you’ve ever implemented DarkMode for iOS in your own projects, you’ve probably used a CSS media lookup with a groups-color-scheme.

:root {
    --color: #fff;
    --color-bg: #000;

@media (prefers-color-scheme: dark) {
    --color: #000;
    --color-bg: #fff;

body {
    color: var(--color);
    background-color: var(--color-bg)
Copy the code

While this allows users to choose which skin they like, it doesn’t cover everything. This can be supplemented with the color-scheme attribute. This property allows the user to automatically adjust color modes, such as dark mode, contrast adjustment, or a specific desired color scheme, through user agent control. These values are negotiated with the user’s preferences to produce the selected color scheme that affects user interface (UI) content, such as the default color for form controls and scrollbars, and the usage value for CSS system colors.

There are two ways to use color-scheme. Let’s look at the first one:

:root {
    color-scheme: dark light;
Copy the code

On the :root element, rendering using a color-scheme affects the surface color of the canvas (the global background color), the initial value of the color property, and the system color used. It should also affect the window scrollbar color.

Another way to use it is on labels:

<meta name="color-scheme" content="dark light" />
Copy the code

To comply with the color-scheme CSS property, you first need to download the CSS (if it is by reference) and parse it. To help the user agent render the page background immediately with the desired color scheme, you can also provide a color scheme value in the element.

Since both meta tags and CSS attributes (if applied to :root) will eventually result in the same behavior, I recommend specifying the color scheme through meta tags so that the browser can adopt the preferred scheme more quickly.

Finally, a Demo of the color scheme provided by @tomayac is provided. Below is the corresponding effect under dark and light:

This is just a brief description of the color-scheme property, if you want to learn more about it, you need to read some tutorials:

CSS Color Adjustment Module Level 1: color-scheme

Improved dark mode default styling with the color-scheme CSS property and the corresponding meta tag

Don’t Forget the color-scheme Property


& > and @ nest

Many of you have used CSS processors such as Sass and LESS. One of the most important features of these processors is the nesting of selectors, such as Sass:

.parent { & > .child { color: red; } } .child { .parent & { color: blue; }}Copy the code

Compiled CSS:

.parent > .child {
    color: red;

.parent .child {
    color: blue;
Copy the code

Nesting Module Nesting Module Nesting Module Nesting Module Nesting Module Similar to the CSS Custom properties (variables) feature, which first appeared in CSS processors, native CSS now supports this feature.

That is, for the near future (if you have a postcsS-preset -env configured in your project build, it can be used now) :

article, section { & p { color: blue; }}Copy the code

Is equivalent to:

:is(article, section) p { 
    color: blue; 
Copy the code

That is:

article p,
section p {
    color: blue
Copy the code

It can also be used in combination with & > :

article, section { & > p { color: blue; }}Copy the code

Is equivalent to:

article > p, 
section > p{
    color: blue; 
Copy the code

Let’s look at some other effective nesting methods:

.foo { color: blue; & > .bar { color: red; }} /* equals */. Foo {color: blue; } .foo > .bar { color: red; } .foo { color: blue; &.bar { color: red; }} /* equals */. Foo {color: blue; } .foo.bar { color: red; } .foo, .bar { color: blue; & + .baz, &.qux { color: red; }} /* equals */.foo,.bar {color: blue; } :is(.foo, .bar) + .baz, :is(.foo, .bar).qux { color: red; }Copy the code

But it would be invalid to write:

/* Invalid because there is no nested selector */. Foo {color: red; .bar { color: blue; }} /* invalid because & is not in the first compound selector */. Foo {color: red; .bar & { color:blue; }} /* invalid because the second selector in the list does not contain nested selectors */. Foo {color: red; &.bar, .baz { color: blue; }}Copy the code

It can also be used in conjunction with @nest. The following types of nesting work:

.foo { color: red; @nest & > .bar { color: blue; }} /* equals */. Foo {color: red; } .foo > .bar { color: blue; } .foo { color: red; @nest .parent & { color: blue; }} /* equals */. Foo {color: red; } .parent .foo { color: blue; } .foo { color: red; @nest :not(&) { color: blue; }} /* equals */. Foo {color: red; } :not(.foo) { color: blue; }Copy the code

But nesting like this is invalid:

/* Invalid because there is no nested selector */. Foo {color: red; @nest .bar { color: blue; }} /* Invalid because not all selectors in the list contain nested selectors */. Foo {color: red; @nest & .bar, .baz { color: blue; }}Copy the code

Note that when using @nest, remember to use it in combination with & to be effective.


One of the most exciting things about CSS Houdini is the API for customizing CSS properties and values. This API enhances CSS custom properties (commonly called CSS variables) by giving them semantic meaning (defined by syntax) and even fallback values.

In simple terms, you can register a custom property using CSS Houdini’s CSS custom property and the value’s css.registerProperty () :

    name: '--colorPrimary',
    syntax: '<color>',
    initialValue: 'magenta',
    inherits: false
Copy the code

This allows you to use the registered –colorPrimary custom property:

.card {
    background-color: var(--colorPrimary); /* magenta */

.highlight-card {
    --colorPrimary: yellow;
    background-color: var(--colorPrimary); /* yellow */

.another-card {
    --colorPrimary: 23;
    background-color: var(--colorPrimary); /* magenta */
Copy the code

Now or in the future, we can register a custom property directly using the @property in CSS:

@property --gradient-start {
    syntax: "<color>";
    initial-value: white;
    inherits: false;
Copy the code

It can be used directly in CSS as follows:

.el {
    --gradient-start: white;
    background: linear-gradient(var(--gradient-start), black);
    transition: --gradient-start 1s;

.el:hover {
    --gradient-start: red;
Copy the code

Take this example (see Chrome 85+) :

In the CSS world, there is another set of specifications related to CSS Custom Properties and that is CSS Custom Properties for Cascading Variables Module Level 1. Use — declare the custom property in the selector block, and then use the var() function to reference the declared custom property as the value of the CSS property:

:root {
    --color: #f09

body {
    color: var(--color)
Copy the code

By now, CSS custom properties (also known as CSS variables) have been used by major browsers and can be seen in some large Web applications. For example, @Adam Argyle uses CSS custom properties to simulate a set of easing functions, which can be used in CSS Animation:

:root {--ease-in-quad: cubic- Bezier (0.55, 0.085, 0.68, 0.53); -- Ease-in-cubic: Cubic - Bezier (0.55, 0.055, 0.675, 0.19); -- Ease-in-quart: Cubic - Bezier (0.895, 0.03, 0.685, 0.22); -- Ease-in-Quint: Cubic - Bezier (0.755, 0.05, 0.855, 0.06); -- Ease-in-expo: Cubic - Bezier (0.95, 0.05, 0.795, 0.035); -- Ease-in-circ: Cubic - Bezier (0.6, 0.04, 0.98, 0.335); -- Ease-out-Quad: Cubic - Bezier (0.25, 0.46, 0.45, 0.94); -- Ease-out cubic: cubic- Bezier (0.215, 0.61, 0.355, 1); -- Ease-out-Quart: Cubic - Bezier (0.165, 0.84, 0.44, 1); -- Ease-out-Quint: Cubic - Bezier (0.23, 1, 0.32, 1); -- Ease-out-expo: Cubic - Bezier (0.19, 1, 0.22, 1); -- Ease-out-circ: Cubic - Bezier (0.075, 0.82, 0.165, 1); -- Ease-in-out Quad: Cubic - Bezier (0.455, 0.03, 0.515, 0.955); -- Ease-in-out cubic: cubic bezier(0.645, 0.045, 0.355, 1); -- Ease-in-out-of-quart: Cubic - Bezier (0.77, 0, 0.175, 1); -- Ease-in-out Quint: Cubic - Bezier (0.86, 0, 0.07, 1); --ease-in-out-expo: cubic-bezier(1, 0, 0, 1); -- Ease-in-out circ: Cubic - Bezier (0.785, 0.135, 0.15, 0.86); --easing: var(--ease-in-quad); } .animation__target { animation: ani 5s var(--easing) alternate infinite; } // JavaScript const handlerElement = document.getElementById("easing"); handlerElement.addEventListener("change", function (e) { document.documentElement.style.setProperty("--easing", e.target.value); });Copy the code

Click here to see the effect:


:: Cue and ::cue(selector) are new to me. These two pseudo-elements are in The WebVTT: The Web Video Text Tracks Format module.

The biggest difference between ::cue and ::cue pseudo-elements is the pseudo-element with parameters. Specific functions:

  • The :: CUE pseudo-element (without parameters) matches any WebVTT node object list constructed by the element, but the property corresponding to the background symbol must be applied to the WebVTT cue background box, not the WebVTT node object list

  • The ::cue(selector) is a pseudo-element with parameters. It must have a parameter consisting of CSS selectors. It matches the WebVTT internal node object constructed by the element, which also matches the given CSS selector

There are only some properties in CSS that can be applied to ::cue and ::cue pseudo-elements. For example, color, opacity, visibility, text-decoration, text-shadow, background, outline, font, line-height, white-space, and text-combine-upri GHT and Ruby-Position.

::cue {
    color: white;
    background-color: hsl(0 0% 0% / 90%);
Copy the code

To be honest, I haven’t read all of The WebVTT: The Web Video Text Tracks Format module and don’t fully understand it. This is just for the purpose of introduction, if your work is related to WebVTT, it should have some effect on you; If you’re interested in it, dig deeper.


This post was inspired by the slides @Argyleink shared at CSS 2020 London. In this article, I have sorted out some new CSS features mentioned by @Argyleink in the PPT, and I have added some new features from my own perspective, almost nearly 24. Of course, some of the features listed above may not be new to some of you (and some of them may not be new to me either), but they are well worth mentioning because they are already or will soon be used in your own projects.

Because there are many features to talk about, and many features to talk clearly, if we talk thoroughly, we need more than one article to talk, so the article only does a little to introduce jade, if you are interested in the corresponding features, you can targeted to understand. In addition, there are some new features you must have been exposed to that weren’t covered in the article, but if so, please share them with us in the comments.

Finally, I would like to recommend a few articles related to this topic:

What’s new with CSS?

CSS News July 2020

What’s New in Web 2020

The Web in 2020: Extensibility and Interoperability

Next-generation web styling