By Ali Tao F(X) Team – Big Tapir

A few days ago in F(X) Team. Midnight knowledge hall and we talked about the topic of CSS, that is, CSS new features.

This topic focuses on the CSS aspect of the features, and as much as possible to organize some of the properties that you can use now or will use in the near future. In addition, although the title is “new features,” quite a few of these features are not “new.” They may already be in your project, or you may have seen them and just didn’t know about them. Next, with you to briefly review these sex, I hope you can like, but also hope to help you work at ordinary times.

For video recording, you can check the f-X-Team Weibo video (weibo recording is required).


The above video is recorded by Dingdingqun live. If you like, you can watch the above video directly. The corresponding PPT can be downloaded here. (CSS – New – the Features. PDF)!

Introduction to the

If you’ve been paying attention to the CSS status reports for 2019 and 2020, you’ll notice that there’s one new feature in the reports:


The image above captures the status report of CSS 2020!

Is there a kind of familiar feeling, or some are so strange. None of this is new to me. However, I would like to say that what is mentioned in the above picture (or report) is only the tip of the iceberg, and many of them are not shown in the questionnaire and report. Examples include pseudo-class selectors, content visibility, container queries, and so on.

Well! Start with CSS selectors! (^_^)

CSS pseudo-class selector

CSS options developed today, can be said to be a huge system:


For the record, this image is from @Linxz’s blog. If you can’t see it clearly, click here to get the original image.

The current specification describing CSS selectors includes CSS (referring to CSS 2.1), followed by selectors at Level 3 and Level 4. Pseudo-class selectors appear in all three parts of the specification, and they are an incremental process, especially in Level 3 and Level 4, where many excellent selectors have been added to the original basis. Several modern pseudo-class selectors are available from Levele 4:


:is():where()

Start with :is() and :where(). @elad Shechter once tweeted a quiz question about :is() and: Where () selectors:


If this is the first time you’ve seen a test like this, quiz yourself. Which answer would you choose (green, Purple, red, or blue)? If you chose Purple, congratulations, you’re right.


IO /airen/full/…

Two pseudo-class selectors appear in the example :is() and :where(), which you may not have encountered before:

:is(.header..main) .p {
	color: purple
}

:where(.header..main) .p {
	color: red;
}
Copy the code

In fact, these two selectors are equivalent to:

.header .p..main .p {
	color: purple;
}

.header .p..main .p {
	color: red;
}
Copy the code

The only difference is the selector weights. The :where() priority is always 0, but :is() priority is determined by the highest priority selector in its list of selectors. So in the above example, we can use the following figure to clearly describe their relationship:


Above address from: twitter.com/eladsc/stat…

That is, the same element selected by the following three selectors:

.header .p..main .p{/ /... }:is(.header..main) .p{/ /... }:where(.header..main) .p{/ /... }Copy the code

The difference is that they have different weights, among which:

.header .p..main .p{/ /... }:is(.header..main) .p{/ /... }Copy the code

Both selectors in the example have the same weight (that is, 020); Among them:

:where(.header..main) .p{/ /... }.p{/ /... }Copy the code

The selector in the example code above has the same weight (that is, 010) :


The appearance of :is() and: Where () pseudo-class selectors will make our selectors more concise, such as this :is() example:

// Level 0
h1 {
  font-size: 30px;
}
//  Level 1 
section h1.article h1.aside h1.nav h1 {
  font-size: 25px;
}
// Level 2 
section section h1.section article h1.section aside h1.section nav h1.article section h1.article article h1.article aside h1.article nav h1.aside section h1.aside article h1.aside aside h1.aside nav h1.nav section h1.nav article h1.nav aside h1.nav nav h1, {
  font-size: 20px;
}
Copy the code

Using :is() can be described like this:

// Level 0
h1 {
  font-size: 30px;
}
// Level 1
:is(section.article.aside.nav) h1 {
  font-size: 25px;
}
// Level 2 
:is(section.article.aside.nav)
:is(section.article.aside.nav) h1 {
  font-size: 20px;
}
Copy the code

Come back, please look at the following test question, what is the color of p text?

<! --HTML -->
<main class="main">
	<p class="p">What is the text color? </p>
</main>

// CSS
:where(.header..main) .p {
	color: red;
}

.p {
	color: blue;
}

:is(.header..main) .p {
	color: purple;
}

.header .p..main .p {
	color: green;
}
Copy the code

You can check out the answer here: codepen. IO /airen/full/…

:not():has()

Perhaps you usually in the development of the front page, encounter a requirement like the following:


Hopefully the last card doesn’t have a margin-bottom (or the first card doesn’t have a margin-top). For such scenarios, the :not() selector is very advantageous. Why do you say that?

In the absence of the :not() selector, you might think of something like this:

.card + .card {
	margin-top: 20px; } / / or.card {
	margin-bottom: 20px;
}

.card.card--last{// May also be used.card:last-child
	margin-bottom: 0;
}
Copy the code

Instead of using the :not() selector, we can do this:

.card:not(:last-child) {
	margin-bottom: 20px
}
Copy the code

The effect is as follows:


The Demo URL: codepen. IO/airen/full /…

While CSS selectors have become very powerful, there has never been a selector in CSS that selects from child elements to parent elements (parent selectors) :


Someone suggested a selector like Parents ()!

While there is no :parents() selector so far, thankfully, the :has() selector is coming, which can be used to select parent elements. Brian Kardell, a member of the Igalia team that is implementing this selector for Chrome, explains the :has() selector in detail in his new Can I: Has () article.

<section><! -- Section border color is blue,margin-bottom is 30px --> 
  <h1>H1 Level Title</h1>
</section>  

<section><! -- Section border color is #09f,margin-bottom is 30px --> 
  <h1>H2 Level Title</h1>
</section>  

<section><! -- Section border color is red --> 
  <p>Text Paragraphs</p>
</section>/* CSS */ / will match section elements with h1 child elements section:has(h1) {border-color: blue; } // Will match the section element with h2 child elements section:has(h2) {border-color: #09f; } // Will match the section element with p child elements section:has(p) {border-color: red; } section:not(:has(p)) {margin-bottom: 30px; }Copy the code

In a browser that supports :has(), you should see something like this:


The example also demonstrates that :not() and :has() are used together, but the two combine to achieve completely different meanings. Where :not(:has(selector)) matches a parent element that does not contain a selector element, and :has(:not(selector)) matches an element that does not contain a selector child element. The main difference between the two is that :has(:not(selector)) must have a child element, whereas :not(:has()) can be matched without an element.

Interestingly, there is an Issue on Github discussing ** **:has-child()** selector **, and maybe someday we could use it in components like this:


: the empty and blank

In the development of the modern Web, it is impossible to avoid the situation of emptying data, which often causes extra trouble for our UI. For example, if a certain style rule is written in the same kind of UI element, but the data is empty, a scenario like this might appear on the page:


  • Add styles to empty elements
  • Create an empty state

Since you can style empty elements, what are empty elements and what are the differences between them? To answer the first question, what is an empty element? An empty element is an element that does not have any children, such as:

<! -- Empty elements -->
<div class="error"></div>
<div class="error"><! -- annotation --></div>

<! -- Non-empty elements -->
<div class="error"> </div><! -- There is a space in the middle -->
<div class="error">
</div><! -- Line breaks -->
<div class="error">
	<! -- annotation -->
</div><! -- Comments are arranged in line breaks -->
<div class="error"><span></span></div>
Copy the code

:empty compares to :blank, which selects only elements that have no children. Child elements can only be element nodes or text (including Spaces). Comments or processing instructions do not matter.


Demo: codepen. IO/airen/full /…

Note that :empty can recognize content created on empty elements even with pseudo-elements ::before or ::after.

:blank is much more flexible than :empty and is recognized as long as there are no children in the element. However, the W3C specification defines this pseudo-class selector more specifically for form controls, such as submission forms that are recognized when the user has not entered anything in an input or textarea. It’s kind of like form validation.

Back in 2018, Zell Liew tweeted about: Empty and: Blank and published a blog post about it: Empty and: Blank:


So far: Empty has been supported by major browsers and can be used in production, but the: Blank pseudo-class selector is somewhat controversial and can be discussed here.

: focus – visible and focus – the within

CSSer should be familiar with focus. Early on, we used focus for focatable elements to set the UI style of the element in the focus state, i.e. the focus ring style:


Starting with Chrome 86, we also introduced two pseudo-class selectors: focus-Visible and :focus-within to help us better control the style of the UI focus ring:


Although :focus, :focus-within, and :focus-visible can all be used to set the focus ring style, there are some differences between them:

  • :focus: When the user uses the mouse to click on the focus element or uses the keyboardTabThe key (or shortcut key) triggers the focus ring style of the focus element
  • :focus-visible: Only those using the keyboardTabThe key (or shortcut key) triggers the focus ring style of the focus element. If you only use:focus-visibleThe focus ring style will not be triggered when the user clicks on the focus element with the mouse
  • :focus-within: indicates that an element gets focus, or that its descendants get focus. This also means that it or its descendants can be triggered if they gain focus:focus-within

Let’s look at a simple example:

button:focus { 
  outline: 2px dotted #09f; 
  outline-offset: 2px; 
} 

button:focus-visible { 
  outline: 2px solid #f36; 
  outline-offset: 2px; 
}
Copy the code

You’ll notice that the focus ring looks different when you click on the button with the mouse and when you press Tab to bring the button into focus:


Note that :focus and :focus-visible also involve selector weights. For the example above, if we place the :focus selector after :focus-visible:

button:focus-visible { 
  outline: 2px solid #f36; 
  outline-offset: 2px; 
}

button:focus { 
  outline: 2px dotted #09f; 
  outline-offset: 2px; 
} 
Copy the code

When the user uses the keyboard Tab key or mouse to make


If we want :focus and :focus-visible to have separate styles, we can use :not() in the CSS selector to do this:

button:focus:not(:focus-visible) {
  outline: 2px dotted #416dea;
  outline-offset: 2px;
  box-shadow: 0px 1px 1px #416dea;
}

button:focus-visible {
  outline: 2px solid #416dea;
  outline-offset: 2px;
  box-shadow: 0px 1px 1px #416dea;
}
Copy the code

When you press the Tab keyboard and click the mouse, there is a corresponding difference in the focus ring style:


Demo: codepen. IO/airen/full /…

As you might imagine, if you are optimizing for A11Y and want to set different focus ring styles for focus elements on mobile and PC, the above scheme is very flexible.

For: focal-within, this is actually a bit like the :has() pseudo-class selector, which fires the parent element when the child element gets focus:


The figure above depicts the difference between :focus-within and :focus. To put it more simply, it’s a bit like JavaScript event bubbling, bubbling all the way from the focal element available to the root element of the HTML < HTML >, which can receive the focal-within event. Such as:

.box:focus-within,
.container:focus-within {
    box-shadow: 0 0 5px 3px rgb(255 255 255 / 0.65);
}

body:focus-within {
    background-color: #2a0f5bde;
}

html:focus-within {
    border: 5px solid #09f;
}
Copy the code


Demo: codepen. IO/airen/full /…

With the help of the :focus-within pseudo-class selector, we can make some interactions easier, such as the following example, which is implemented using :focus-within without any JavaScript code:

Demo: codepen. IO/airen/full /…

Having :focus, : focus-Visible, and :focus-within allows us to better manage the UI effect of the focus ring when the focus element is in focus state.

In the CSS world, in addition to the pseudo-class selectors mentioned here, there are many other pseudo-class selectors (or pseudo-elements), such as ::marker, :in-range, :out-of-range.


If you’re interested, try using :in-range, :out-of-range to give some input elements (e.g. input elements of type number, range, etc.) They both have min and Max attributes set) to provide different UI effects when the user enters range and non-range values.

CSS color

Since Level 4, CSS color modules have added new function values such as HWB (), LCH (), lab(), color-mix(), color-contrast(), and color(). Changes have also been made to the original RGB (), HSL (), and # RRGGBB color definition syntax.

For example, the familiar way of describing color values is:

#09f 
#90eafe
rgb(123.123.123)
rgba(123.123.123.5)
hsl(220.50%.50%)
hsla(220.50%.50%.5)
Copy the code

New syntax rules, especially for RGB (), RGBA (), HSL (), and HSLA (), which used to use commas (,) as separators, can now use Spaces as separators. And the RGB () and HSL () functions add/between the third and fourth values to substitute rgba() and hSLA () :


In addition, hexadecimal to describe colors, you can also add a new bit value to the last two bits of the original syntax rule to describe transparency. Such as #rrggbbaa or #rgba. You can already see this syntax in the Chrome code inspector:


That is, we can now describe color values as follows:

#hex-with-alpha {
  color: #0f08;
  color: #00ff0088;
}

#functional-notation {
  color: hsl(0deg 0% 0%);
  color: hsl(2rad 50% 50% / 80%);
  color: rgb(0 0 0);
  color: rgb(255 255 255/.25);
}

#lab-and-lch {
  --ux-gray: lch(50% 0 0);
  --rad-pink: lch(50% 200 230);
  --rad-pink: lab(150% 160 0);
  --pale-purple: lab(75% 50 -50);
}
Copy the code

In addition, we now describe colors in the sRBG color value space, which is a complex system. Besides sRGB, there are other color value Spaces, such as LCH:


As shown in the figure above, the LCH color space has more colors than the sRGB color space, and the colors described are more detailed.


The color space can be specified in the color() function, as shown above:

#color-function {
  --rad-pink: color(display-p3 1 0 1);
  --rad-pink: color(lab 50% 150 -50);
  --rad-pink: color(srgb 100% 0% 50%);
}
Copy the code

Note that the use of the color() function to specify color space is not only a matter of function support (browser compatibility) but also of hardware support for color space.

In the CSS, you can use the media query @media to make the corresponding judgment. For example, if the terminal device supports the following example, it will use the specified color space color value:

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

Even more powerful is the CSS color module Level 5 version, the color function capabilities to do a further extension. For example, you can add the from keyword to RGB (), HSL (), HWB (), lab(), and LCH () functions to adjust only one parameter based on a color:

rgb() = rgb([from <color>]? <percentage>{3} [ / <alpha-value> ]? ) | rgb([from <color>]? <number>{3} [ / <alpha-value> ]? ) 
hsl() = hsl([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) 
hwb() = hwb([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) 
lab() = lab([from <color>]? <percentage> <number> <number> [ / <alpha-value> ]? ) 
lch() = lch([from <color>]? <percentage> <number> <hue> [ / <alpha-value> ]?).Copy the code

Consider an example of HSL () :


The figure above shows a new color, HSL (274, 61%, 30%), based on the –theme-primary (primary, i.e. HSL (274, 61%, 50%)), by adjusting only the L parameter from 50% to 30%. In this way, we can easily obtain the color palette based on a color parameter change:


In addition, the Color module Level 5 has added some new functions to describe colors, such as color-mix(), color-contrast(), color-adjust() and so on:

.color-mix {
  --pink: color-mix(red, white);

  --brand: #0af;
  --text1: color-mix(var(--brand) 25%, black);
  --text2: color-mix(var(--brand) 40%, black);
}

.color-contrast {
  color: color-contrast(
    var(--bg)
    vs
    black, white
  );
}

--text-on-bg: color-contrast(
  var(--bg-blue-1)
  vs
  var(--text-subdued),
  var(--text-light),
  var(--text-lightest)
);

.color-adjust {
  --brand: #0af;
  --darker: color-adjust(var(--brand) lightness -50%);
  --lighter: color-adjust(var(--brand) lightness +50%);
}
Copy the code


Just a quick introduction to these color functions. The color-mix() function takes two
values and returns a specified number of their mixed results in a given color space. Blend in the LCH () color space unless otherwise specified. For example, in the LCH () color space (the default), mix red and yellow with 65% red and 35% yellow for each LCH channel, i.e. Color-mix (red yellow 65%) :


The color-contrast() function is an interesting way to improve Web accessibility. Its main function is to take a color value and compare it to a list of other color values, and select the one with the highest contrast from the list.


For example, color-contrast(white vs red, white, green) is used to compare red, white, and green with white, respectively. Green and white have the highest contrast, and the final color is green:


The color-adjust() function is used to adjust the color in a given color space by the specified transformation function, or in the LCH () color space unless otherwise specified. For example, color-adjust(#0af lightness +50%) increases the height of color #0af by 50% in the LCH color space.

That said, in the near future, these color functions will help us control color values on the Web. These color functions have become experimental properties in major browsers, especially Safari, which supports them more than Chrome and Firefox.

The CSS background

CSS background property is a shorthand property, which is familiar to everyone. However, here I would like to highlight background-position and background-repeat separately and have a chat with you. These two attributes are not new, you may ask, so what is there to talk about? Not necessarily, these two attributes are old, but there are a few small details that will be unfamiliar to many developers. So let’s start with background dD-position.

background-position

Background-position is used to specify the position of the background image in the container. It is familiar that the top left corner of the background image (vertex) is computed relative to the top left corner of the container, as shown below:


Sometimes, however, you want the background image to be computed relative to the right or bottom edge of the container, as shown below:


Keep the background image 50px from both the right and bottom edges of the container. For this scenario, you might first want to calculate the distance from the top and left edge using the container size and the background image size, and then apply the calculated value to background-position. Of course, those familiar with CSS might want to use the calc() function:

:root {
	--xPosition: 50px;
  --yPosition: 50px;
}

.container {
	background-position: calc(100% - var(--xPosition)  calc(100% - var(--yPosition)))
}
Copy the code

Using calc() to calculate dynamically is much more convenient than calculating by container and background image size. Actually? Background-position provides another feature where we can specify direction by the keywords top, right, bottom, and left. For example, we are familiar with the usage:

background-position: 50px 50px; / / equivalent tobackground-position: top 50px  left 50px;
Copy the code

So, to achieve the effect shown above, we can use the right and bottom keywords to make things very simple:

:root {
	--xPosition: 50px;
  --yPosition: 50px;
}

.container {
	background-position: right var(--xPosition)  bottom var(--yPosition);
}
Copy the code

The final effect is as follows:

Demo: codepen. IO/airen/full /…

Note that the left side of the example is the effect calculated using calc() and the right side is the effect implemented using keywords.

There is a small detail to note about the use of background-position, that is, background-position uses percentage values. Because background-position calculations using percentage values are complicated relative to other unit values:


As you can see in the image above, the original size of the background image is 100px x 100px, and the size of the container (the elements that use the background image) is 410px x 210px. If you use background-position: 75% 50%, it will calculate as follows:

// background-positionX = (container width - background image width) xbackground-positionThe percentage of the x coordinate of410 - 100) x 75% = 232.5 px.

// background-positionY = (container height - background image height) xbackground-positionThe percentage value of y coordinate of210 - 100) x 50% = 55px
Copy the code

For this example, background-position: 75% 50% is equivalent to bacckground-position: 232.5px 55px.

Note that background-size and background-position contain percentage values can make things even more complicated, especially if background-size is cover or contain. Why is that? This is beyond the topic of this article, if you are interested in this topic, you can explore it in depth.

background-repeat

Round and space can be used on background-repeat attributes in addition to the familiar no-repeat and repeat (or repeat-x and repeat-y).


As we all know, using repeat may cause the background image to be cropped when tiled. If you want the background image to not be cut when tiling, you can use round instead. The main feature of round is that the background image will be sized accordingly according to the width and height of the container when tiling. Space will leave corresponding space, that is, the extra space will be left blank between background pictures on the condition that the background pictures are not clipped.


I recorded a simple video with these values in mind, and the difference between them can be better seen in the presentation of the video:


Demo: codepen. IO/airen/full /…

CSS mask and clipping

If you are familiar with design or design software, you will be familiar with masking and clipping. The ability to mask and cut is often used by designers when they are working on some designs. As CSS evolves, the world of CSS also has these two features, which are defined in the W3C’s CSS Masking Module Level 1 specification. Their main functions are shown in the following figure:


Flexible control of the display area of content.

The CSS attributes corresponding to mask and clipping are mask and clip-path. Mask is a shorthand attribute, and its usage rules are very similar to background. I’ll show you what they can do with a simple example.

Look at the mask:

Demo: codepen. IO/airen/full /…

The emoji and text in the video are incomplete (they look chewed up). Before mask’s ability, it would have been almost impossible to achieve such an effect, but now that we have it, it’s very simple to implement. All we need is an image like the one below (for the image on mask-image), the masked image:


h1 {
	mask-image: url(mask.png);
}
Copy the code

Moreover, we can also make use of mask’s ability to synthesize multiple layers:


Using the ability of mask correlation can quickly help us achieve some effects required by business scenarios:


In addition, it can also achieve the effect of skin changing:


Demo: codepen. IO/airen/full /…

Let’s look at shearing. The ability to use clip-path in CSS, in addition to controlling the area to be displayed, can also combine different paths and function values to achieve irregular graphical display. For example, Clippy shows this effect:


For example, if you need to implement some irregular and multi-state UI effects, clip-path is very convenient:


Besides, using clip-path combined with Transition or animation can make some better dynamic effects on interaction:


On the left is the original effect, on the right is the effect under the mouse hover state:

Demo: codepen. IO/airen/full /…

In particular, more can be done when clip-path supports path(), such as when we want to draw a heart-shaped UI effect:


The clip-path, float and CSS Shapes are used to create irregular Shapes and Shapes:


CSS Mixed Mode

CSS blend mode is an interesting feature. There are two main properties: mix-blending-mode and background-blending-mode. The former is used for combining multiple elements and the latter is used for combining multiple backgrounds. They can be used to achieve special effects, such as filters similar to those in Photoshop:


Using the mixed mode feature, we can easily achieve the effect of color change of product drawings:

Demo: codepen. IO/kylewetton /…

Here is a brief description of how this effect is implemented.

First, we have a product drawing like the following:


With the ability of SVG, we draw a pure black SVG shape that matches the sofa shown above:


The SVG code drawn is not complicated:

<svg id="js-couch" class="couch__overlay" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" width="1000" height="394"> 
  <defs> 
    <path d="M996.35 77.55 q - 1.85-1.95-8.65-3.75 l - 62.4-17.1 q - 9.3-2.75-12.15-2.5-1.8.15-2.85.45. L - 75.3 q2.25-16.3 3.75 22.05 1.15 4.4 - 10.8.2-6.6-1.4. 7-10.85-1.25-5.65-3.1-7.8-2.95-3.35-9.65-2.7-5.95.6-39.3 1.7 38.3 1.25 39.45 1.3-10.25.5-126.75.5-5.05 0-54.2-1.3-45.8-54.05.95-19.45-1.25. 45-30.4-7-20.2 -. 55-23.1-1.3-22.3-5.85-26.5 1.25 2.65 4.55 3.85 7.9-6-1.7. 7 2.5-65-2.2-2.05-4.55-2.75-4.65-6.45-5.2-3.85 -. 55-13.65-4-7.4.1-12. 4-4.05-18.7.9 16.55.8-19.15-1.1-3.4.4-14.6 - 11.3.75-13.05.65 h - 1.1-8.65-9.8 q.. 05-11.45-4-2.85-35-9.25-6-6.7 -. 15-8.5 -.. 25-2.7-1-27.75-1-0-29.6.1 25.1-92.35 1.15-99-1.65-5.15.4-20 0-15.3 -. 4-24.4-1.25-6.75 -. 6-21-1. 55-12.95-9-14.85-1.1-6.45-1.05-11.05-1.5-8.7-85-12.85.5-5.45 1.75-8.1 4.65-3.2 3.4-2.9 8.6.25 4.65 2.1 11.8 1 3.8 2.55 9.1 1 3.85 2.35 10.1-.1 1-1.5 1-1.75 0-7.7.85-7.1 1-9.8 2.05-2.4.9-23 4.75-21.2 3.9-22.05 4.15-8.2 1.85-15.05 3.35Q7.4 69.1 5.65 70.3 2.5 72.45 2 73.1.6 75.75 79.2q.15 4.15 1.3 12.75.9 6.85 1.45 10.5 2.75 8.55 54 6.65 42.15 7.35 46.85 1.15 7.65 4.9 28.55 4.55 25.2 6.35 31.2 2.45 8.15 3.8 11.75 1.85 4.9 3.2 5.75 1.25.8 6.85.65 2.75-.05 5.3-.25l23.85.35 Q.10 1.95T2.95Q.90 3.4-1.4 L23.1 -.25 43.65.4q135.05 1 72.9.5 72.45.65 76.85.45 8.1-.35 64.4 143.35.95 146 1.1.55.05 75.3.3 74.7.3 79.8.6 8.65.5 68.25. 35 l51. 75.5 1.6.4 q1.95.35 3.8.05 1.45-3.5-25. 2 0-3.35. 1.9 3-2.1. 45. 8.25-6.25-8, 3-8.75. 05 5.75.3 1.7.2 8 1 7.4 1.75 1.75 2.2 4.95 10.85 2.8 7.55 4.05 12.4.65 2.5 3.6 17.2 2.75 13.75 3.15-14.8.45-1.25 4.45 22.85 4.05 22.4 4.4 24.4.3-1.45-4-26.3 1.15 3.75 25.2 3.35 23.2 5.5 2.35 18.8 1.4-15.7.8-23.7-6-8.35-3.35-11.15 - z" id="a" /> 
	</defs> 
  <use xlink:href="#a"/> 
</svg>
Copy the code

Use CSS custom properties with a little JavaScript code to complete the product image color change effect:

:root {
	--fill: #f36;
}

svg {
	fill: var(--fill);
  mix-blend-mode: multiply;
}
Copy the code

We use the multiply effect of mix-blending-mode. Additionally, JavaScript changes dynamically — the code for the value of fill is also simple:

const inputEle = document.querySelector('input') 

inputEle.addEventListener('change'.(e) = >{ 
  document.documentElement.style.setProperty('--fill', e.target.value); 
})
Copy the code

That’s easy. And you can use your imagination to achieve a lot of creative effects.

CSS Custom attributes

CSS custom properties are standard with CSS. As described in the W3C specification, CSS custom properties are also called CSS variables:


But what I’m talking about here is not how CSS customization works. I want to talk to you about invalid variables in CSS custom properties. Invalid variables in CSS custom properties are a useful feature for toggling 1 (true) and 0 (false). In other words, you can achieve UI effects in different states very well.

Similarly, invalid variables are described in detail in the W3C specification, but this feature can be ignored if you are not careful. Let’s start with how the specification describes invalid variables:


It means:

When the value of a custom attribute is **initial**, the **var()**** function cannot use it for substitution. Unless a valid fallback value is specified, the declaration is invalidated when the value is evaluated. 支那

When you register custom properties in CSS, you use — to register them. You can assign values to registered custom properties, including empty strings, but there is one interesting detail:

:root{ --invalid:; // Note that there is no space between the colon and semicolon, nor any character --valid:; // Note that there is only one space between the colon and semicolon}Copy the code

The –invalid custom attribute is called an invalid variable, while the –valid custom attribute is a valid variable. Using the above method to distinguish valid and invalid variables is very unreadable for developers, and some text editors may format the code according to their own specifications, possibly resulting in –invalid:; Become invalid:; (With Spaces). To do this, it is common to explicitly declare a custom property as an invalid variable using the keyword initial:

:root{ --invalid: initial; // Invalid variable --valid:; // Valid variable}Copy the code

If you don’t use the var() function to call the registered custom property, it doesn’t do anything for the registered custom property. The var() function takes two arguments, the first being the custom property and the second being the alternate value. When the first argument is an invalid value, the second argument is taken. Because of this, for registered invalid custom attributes (that is, invalid variables), such as –invalid in the code above. Var () does not provide an alternate value (the second argument), which invalidates the CSS style rule (declaration) when calculating the value:

:root{ --invalid: initial; // Invalid variable --valid:; // Valid variable} foo {background-color: var(--invalid); // If no alternate value is providedbackground-colorInvalid calculated valuecolor: var(--invalid, red) // Alternate values are provided, --invalid is an invalid variable, alternate values red}Copy the code

Let’s start with a simple example:

<div class="element">
  <i>Element</i>
</div>

<i>Element</i>

<style>
  .element{-color: red; // only applies to.elementElement and its descendants}i{ --foo: initial; // Invalid variable --color: var(--foo); // Invalid variablebackground-color: var(--color, orange); // --colorIs an invalid variable, use the alternate value orange}</style>
Copy the code

Run the code above and you should see something like this:


Demo: codepen. IO/airen/full /…

Here’s another example:

<section>Element</section>

<style>
  :root{-initial: initial; // Invalid variable --valid:; // Valid variable}section {
  	background-color: var(--initial, red); // Invalid variable, alternate value red will be usedcolor: var(--valid, red); // Valid variable, alternate value ignored}</style>  
Copy the code

So, what you see is this:


Demo: codepen. IO/airen/full /…

The color property in this example is equivalent to setting color:; Value, which the client ignores when evaluating and therefore inherits the color value of its ancestor element. This example inherits the color value black of the body element.

You probably have some familiarity with invalid variables in CSS custom properties. Let’s keep going.

In order to make The use of invalid variables in CSS custom properties easier for developers to understand, @Lea Verou wrote in The –var: Hack to Toggle Multiple values with one Custom Property introduces a concept similar to Switch:

:root{ --ON: initial; // Invalid variable, equivalent to onvarAlternate value of () --OFF:; // A valid variable, equivalent to closingvarAlternate value of ()}Copy the code

This way, when we switch between UI states, we only need to switch between –ON and –OFF. Take the example provided by @Lea Verou in the article:

:root {
  --ON: initial;
  --OFF: ;
}

button {
  --is-raised: var(--OFF);
  
  border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
  background: var(
      --is-raised,
      linear-gradient(hsl(0 0% 100% / 0.3), transparent)
    )
    hsl(200 100% 50%);
  box-shadow: var(
    --is-raised,
    0 1px hsl(0 0% 100% / 0.8) inset,
    0 0.1 em 0.1 em -0.1 em rgb(0 0 0 / 0.2));text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:hover {
  --is-raised: var(--ON);
}

button:active {
  box-shadow: var(--is-raised, 0 1px 0.2 em black inset);
}
Copy the code

The final effect is as follows:

Demo: codepen. IO/airen/full /…

The above example is fairly simple. However, we can use this feature to achieve more complex UI effects. For example, here is a variety of skin changes:

label{-box-shadow: var(--ON);
  --box-shadow-active: var(--OFF);
  box-shadow: 0 0 0 3px var(--box-shadow, rgba(0.0.0.0.05))
    var(--box-shadow-active, #2196f3);
  cursor: pointer;
}

label.dark {
  background-color: var(--dark-bgcolor);
}

label.light {
  background-color: var(--light-bgcolor);
}

label.blue {
  background-color: var(--blue-bgcolor);
}

#dark:checked ~ div .dark.#light:checked ~ div .light.#blue:checked ~ div .blue{-box-shadow: var(--OFF);
  --box-shadow-active: var(--ON);
}

.nav {
  color: var(--light, var(--light-color)) var(--dark, var(--dark-color))
    var(--blue, var(--blue-color));
  background-color: var(--light, var(--light-bgcolor))
    var(--dark, var(--dark-bgcolor)) var(--blue, var(--blue-bgcolor));
}

a.active.a:hover {
  background-color: var(--light, var(--light-active-bgcolor))
    var(--dark, var(--dark-active-bgcolor))
    var(--blue, var(--blue-active-bgcolor));
}

/* Set toggle switch */
:root {
  --ON: initial;
  --OFF: ;

  /* Dark */
  --dark-color: rgba(156.163.175.1);
  --dark-bgcolor: rgba(17.24.39.1);
  --dark-active-bgcolor: rgba(55.65.81.1);

  /* Light */
  --light-color: rgba(55.65.81.1);
  --light-bgcolor: rgba(243.244.246.1);
  --light-active-bgcolor: rgba(209.213.219.1);

  /* Blue */
  --blue-color: rgba(165.180.252.1);
  --blue-bgcolor: rgba(49.46.129.1);
  --blue-active-bgcolor: rgba(67.56.202.1);
}

#dark:checked ~ .nav {
  --light: var(--OFF);
  --dark: var(--ON);
  --blue: var(--OFF);
}

/* The default is Light */
#light:checked ~ .nav {
  --light: var(--ON);
  --dark: var(--OFF);
  --blue: var(--OFF);
}

#blue:checked ~ .nav {
  --light: var(--OFF);
  --dark: var(--OFF);
  --blue: var(--ON);
}
Copy the code

The effect is as follows:

Demo: codepen. IO/airen/full /…

No JavaScript code is used in the example. Isn’t that interesting? If you are interested, you can also try the effect of masturbation.



Tao department front – F-X-team opened a weibo! (Visible after microblog recording)
In addition to the article there is more team content to unlock 🔓