Author: The tapir

Introduction to the

If you’ve been following the CSS Status reports for the past two years (2019 and 2020), it’s not hard to find that there’s a special section on the new CSS features in the reports:

Above is an interception of the 2020 CSS Status report!

Is there a kind of feeling like acquaintance, or some are so strange. None of this is new to me. But WHAT I want to say is that what is mentioned above (or in the report) is just the tip of the iceberg, and there is a lot that doesn’t show up in the surveys and reports. Examples include pseudo-class selectors, content visibility, container queries, and so on.

Well! Start with the CSS selector! (^_^)

CSS pseudo-class selector

CSS selection is now a huge system:

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

The current specification for CSS selectors is mainly CSS (referring to CSS 2.1), followed by selectors Level 3 and Level 4. Pseudo-class selectors appear in all three parts of the specification. They are a progressive process, especially in Level 3 and Level 4, which add many excellent selectors to the original. A few of the modern pseudo-class selectors mentioned today are in Levele 4:

: is () and the where ()

First look at :is() and :where(). @Elad Shechter tweeted a quiz about the :is() and: Where () selectors:

If this is the first time you have seen this question, please test yourself. Which answer would you choose? If you chose Purple, congratulations, you’re right.

Codepen. IO /airen/full/…

There are two pseudo-class selectors in the example :is() and: WHERE () that you probably haven’t touched before:

:is(.header, .main) .p { color: purple } :where(.header, .main) .p { color: red; } copy codeCopy the code

In fact, these two selectors are equivalent to:

.header .p, .main .p { color: purple; } .header .p, .main .p { color: red; } copy codeCopy the code

The only difference between them is the weight of the selector. The priority of :where() is always 0, but the priority of :is() is determined by the highest priority selector in its list of selectors. In the above example, we can use the following picture to clearly describe the relationship between them:

The above address is from: twitter.com/eladsc/stat…

That is, the following three selectors select the same element:

.header .p, .main .p { // ... } :is(.header, .main) .p { // ... } :where(.header, .main) .p { // ... } copy codeCopy the code

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

.header .p, .main .p { // .... } :is(.header, .main) .p { // ... } copy codeCopy the code

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

:where(.header, .main) .p { // ... } .p { // ... } copy codeCopy the code

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

The appearance of :is() and: Where () pseudo-class selectors will make our selectors more concise, as shown in the following example :is() :

// 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 codeCopy the code

Using :is() can be described as follows:

// 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 codeCopy the code

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 codeCopy the code

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

: not () and from the ()

Maybe when you are developing a front-end page, you encounter a demand like the following:

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

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

.card + .card { margin-top: 20px; } // or.card {margin-bottom: 20px; }.child margin-bottom: 0;}.child margin-bottom: 0; } copy codeCopy the code

If you use the :not() selector, you can implement this:

.card:not(:last-child) {margin-bottom: 20pxCopy the code

The effect is as follows:

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

Although CSS selectors have become very powerful, there is no such thing as a parent selector in CSS to select from child elements:

Someone proposed a selector like :parents()!

While there hasn’t been a :parents() selector so far, thankfully, a :has() selector is coming, which can be used to select parent elements. Igalia is currently implementing the selector for Chrome, and team member Brian Kardell describes the :has() selector in detail in the new Can I: Has () article.

<section><! <h1> h1 Level Title</h1> </section> <section><! Margin-bottom: 30px --> <h1>H2 Level Title</h1> </section> <section><! </p> </section> /* CSS */ // / will match section elements with h1 child elements. Section :has(h1) { border-color: blue; } section:has(h2) {border-color: #09f; } section:has(p) {border-color: red; } section:not(:has(p)) {margin-bottom: 30px; } copy codeCopy the code

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

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

Interestingly, there is an Issue on Github discussing ** **:has-child()** selectors **, which we might someday use in our components:

: the empty and blank

In the development of the modern Web, it is always inevitable that data will be spat out as empty, which will bring extra trouble to our UI. For example, if you write a certain style rule in a UI element of the same class, but the data is empty, you might have a scene on the page like this:

CSS’s ‘:empty’ and ‘:blank’ pseudo-class selectors can help avoid this. Both of these selectors are useful:

  • Styles empty elements
  • Creates an empty state

If you can style an empty element, what is an empty element and what is the difference between them? Let’s answer the first question, what is an empty element? An empty element is an element that does not have any child elements or child nodes, such as:

<! - empty elements -- -- > < div class = "error" > < / div > < div class = "error" > <! < div style = "text-align: center; <div class="error"> </div><! <div class="error"> </div><! <div class="error"> <! < div style = "text-align: center; - comments line breaks are arranged - > < div class = "error" > < span > < / span > < / div > duplicate codeCopy the code

:empty compared to :blank, :empty can only select elements that have no children. Child elements can only be element nodes or text (including whitespace). Comments or processing instructions have no effect.

Demo: codepen. IO/airen/full /…

Note that even using the pseudo-elements ::before or ::after to create content on empty elements is recognized by :empty.

: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 pseudo-class selectors that are more likely to apply to form controls, such as when the user has not entered input or textarea, and the form submission is recognized. It’s kind of like form validation.

Back in 2018, Zell Liew tweeted about empty and: Blank, and wrote a blog post about them:

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. In the early days of focusing elements, we used: Focus to set the UI style of the element in the focused state, the focus ring style:

Chrome 86 has also introduced two pseudo-class selectors :focus-visible and focus-within to help us better control the UI focus ring style:

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 clicks on the focus element with the mouse or uses the keyboardTabKey (or shortcut key) triggers the style of the focus element’s focus ring
  • :focus-visible: Keyboard onlyTabKey (or shortcut key) triggers the style of the focus element’s focus ring. If you only use:focus-visibleWith the focus ring style set, the focus ring style will not be triggered when the user clicks on the focus element with the mouse
  • :focus-within: indicates that an element or its descendants gain focus. This also means that either it or its offspring gaining focus can be triggered: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 codeCopy the code

You will notice that the focus ring style is different when clicking the button with the mouse and tab-pressing the button to bring it into focus:

Note, however, that :focus and :focus-visible also involve selector weights. In the example above, if we put the :focus selector style after :focus-visible:

button:focus-visible { outline: 2px solid #f36; outline-offset: 2px; } button:focus { outline: 2px dotted #09f; outline-offset: 2px; } copy codeCopy the code

At this point, you will notice that whether the user uses the keyboard Tab key or the mouse to bring

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

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 codeCopy the code

When you press Tab and click the mouse, the focus ring style will be different:

Demo: codepen. IO/airen/full /…

If you’re doing A11Y optimizations and want to have different focus ring styles for focus elements on mobile and PC, this scenario is very flexible.

For :focus-within, it is similar to the :has() pseudo-class selector that fires the parent element when the child element is in focus:

The figure above illustrates the difference between :focus-within and :focus. More simply, it is somewhat similar to JavaScript’s event bubble, which can be fired from the available focus element all the way to the root HTML element < HTML >. Such as:

. Container :focus-within {box-shadow: 0 0 5px 3px RGB (255 255 255 255/0.65); } body:focus-within { background-color: #2a0f5bde; } html:focus-within { border: 5px solid #09f; } copy codeCopy the code

Demo: codepen. IO/airen/full /…

With the :focus-within pseudo-class selector feature, we can make some interaction effects much simpler, such as the following example, which can be 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 of the focus ring when the focus element is in the focus state.

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

If you’re interested, try using :in-range, :out-of-range for input elements (for example, input elements with type number or range, They both have min and Max properties) to provide different UI effects when the user enters a range value and a non-range value.

CSS color

Since Level 4, the CSS color module has added some new functions, such as HWB (), LCH (), lab(), color-mix(), color-contrast(), and color(). The original syntax rules for defining color values such as RGB (), HSL (), and # RRGGBB were also adjusted.

For example, the familiar way to describe a color value is:

# 09F # 90eAFe RGB (123, 123, 123) rGBA (123, 123, 123,.5) HSL (220, 50%, 50%) hSLA (220, 50%, 50%,.5) copy codeCopy the code

There are new syntax rules, especially for RGB (), rgba(), HSL (), and hSLA (). Instead of using a comma (,) as a separator, you can use Spaces directly as a separator. And the RGB () and HSL () functions add/between the third and fourth values to replace rgba() and hSLA () :

In addition, hexadecimal describes color, and it is possible to add a new bit value to the last two bits of the original syntax rules to describe transparency. Like #rrggbbaa or #rgba. You can already see this syntax in the Chrome code reviewer:

In other words, we can now describe the 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 codeCopy the code

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

As the figure above shows, the LCH color space has a larger number of colors than the sRGB color space, and the colors described are more subtle.

You can specify the color space in the color() function, as shown in the figure 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 codeCopy the code

Note that the use of the color() function to specify the color space requires consideration of how well the function is supported (browser compatibility) as well as how well the hardware supports the color space.

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

@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 codeCopy the code

Even more powerful is the Level 5 version of the CSS color module, which further extends the color function capabilities. For example, we can add the from keyword to the RGB (), HSL (), HWB (), lab(), and LCH () functions to adjust only one parameter based on one 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 codeCopy the code

Consider an example of HSL () :

The figure above shows that the l parameter is only adjusted from 50% to 30% based on the –theme-primary color to get a new color, HSL (274, 61%, 30%). Using this method, we can easily obtain the color palette that is changed based on a certain color parameter:

In addition, the Level 5 version of the color module also adds some new functions to describe colors, such as color-mix(), color-contrast(), and color-adjust() :

.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 codeCopy the code

A quick introduction to these color functions. Where the color-mix() function takes two
values and returns the result of their mixing in a specified amount in a given color space. Blend in the LCH () color space unless otherwise specified. For example, in the LCH () color space (default), red and yellow are mixed. The red value of each LCH channel is 65%, and the yellow value is 35%, which means 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, 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), contrast(red vs red, white, green), contrast(red vs red, white, green), contrast(red vs red, white, green)

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

That said, in the near future, these color functions will be a great way to control color values on the Web. Because these color functions have become experimental properties in major browsers, Safari in particular has more support for them than Chrome and Firefox.

The CSS background

The BACKGROUND property of CSS is a shorthand property that you’ll be familiar with. But here I want to take background-position and background-repeat out and talk with you. These two properties are not new, you may ask, so what’s worth talking about? Not necessarily, these two properties are old, but there are a few small details that will be unfamiliar to many developers. Let’s start with Backgroundd-position.

background-position

Background-position specifies the position of the background image in the container. More familiar is that the top left corner of the background image (vertices) is calculated relative to the top left corner of the container, as shown below:

Sometimes, however, you want the background image to be calculated relative to the right or bottom edge of the container, such as the following:

Make sure the background image is 50px from both the right and bottom edges of the container. For a scenario like this, you might first think of using the container size and the background image size to calculate the distances from the top and left edges, and then apply the calculated values to the background-position. Of course, those of you familiar with CSS might think of using the calc() function:

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

Using calc() is much more convenient than using container and background image sizes. In fact? Background-position provides another feature where we can specify direction by using the keywords top, right, bottom, and left. Like the familiar usage:

background-position: 50px 50px; // Background-position: top 50px left 50px; Copy the codeCopy the code

So, to achieve the image 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 codeCopy the code

The final result 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 achieved using keywords.

One small detail to note when using background-position is that background-position takes a percentage value. Because background-position calculations using percentage values are more complex than other unit values:

As shown in the figure above, the original size of the background image is 100px x 100px, and the size of the container (using the elements of the background image) is 410px x 210px. Using background-position: 75% 50%, it is calculated as follows:

// Background-position x = (container width - background image width) x background-position x % = (410-100) x 75% = 232.5px // X % of background-position y = (210-100) x 50% = 55pxCopy the code

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

Contain only a small amount of background. Contain only a small amount of background. Contain only a small amount of background. Why is that? This is beyond the scope of this post, so if you’re interested, you can explore it further.

background-repeat

For background-repeat attributes, you can use round and space in addition to the familiar no-repeat and repeat (or repeat-x and repeat-y).

As we all know, using repeat can cause the background image to be cropped when tiling. If you want the background image to be tiled without cutting, you can use Round instead. The best feature of round is that the background image will be sized according to the width and height of the container when tiled. Space will set aside the corresponding space, that is, to ensure that the background image is not cropped, the extra space is left between the background images in a blank manner.

For these values, I recorded a simple video, which shows the difference better:


Demo: codepen. IO/airen/full /…

CSS masks and clippings

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. Along with the development of CSS, these two features also emerge in the CSS world. They are defined in THE CSS Masking Module Level 1 specification of the W3C, and their main functions are shown in the figure below:

Flexible control of content display area.

The CSS attributes for mask and clipping are mask and clip-path. Mask is a shorthand attribute and its usage rules are very similar to background. I’m going to show you a simple example of what they can do for us.

Look at the mask:

Demo: codepen. IO/airen/full /…

The emoji and text in the video are incomplete (they look gnawed). Before we had the ability to mask, it was almost impossible to achieve such an effect. Now that we have it, it is very easy to achieve. We only need an image like this (used on mask-image), which is called the mask layer:

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

In addition, we can also use the synthesis ability of mask to make multiple masks do synthesis operation:

The ability to apply masks can quickly help us achieve the desired effects for some business scenarios:

In addition, you can also achieve the effect of skin:

Demo: codepen. IO/airen/full /…

Now let’s cut. The ability to use clip-path in CSS allows us to control the areas we want to display, and to combine different paths and function values to achieve irregular graphics. Like this one from Clippy:

For example, in actual requirements, some irregular and multi-state UI effects need to be implemented, so clip-path is very convenient:

Also, using clip-path in combination with transition or animation can make the interaction even more dynamic:

The left side is the original effect, the right side is the mouse hover state effect:

Demo: codepen. IO/airen/full /…

In particular, when clip-path supports the path() function, there are many more things to do, such as we want to draw a heart-shaped UI effect:

The combination of clip-path, float, and CSS Shapes allows for a variety of irregular typographic layouts:

CSS Hybrid mode

CSS blend mode is an interesting feature. Currently there are mix-blend-mode and background-blend-mode properties. The former is used for multiple elements and the latter for multiple backgrounds. Use them to achieve some special effects, such as filter effects similar to those found in Photoshop:

With the feature of blending mode, we can easily achieve the effect of product drawing color change:

Demo: codepen. IO/kylewetton /…

Here is a brief introduction to how this effect is achieved.

First of all, we have a product picture similar to the one below:

Using the power of SVG, we depict a pure black SVG shape that matches the sofa in the image above:

The drawn SVG code 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.2 Q.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.35q.1 0 1.95T2.95q1.9 0 3.4-1.4 L23.1 -.25 43.65.4q135.05 2.15 137.9 1.9 1.25-.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-4-26.3 3.75 25.2 3.35 23.2 1.45 1.15 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"/> </ SVGCopy the code

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

:root { --fill: #f36; } svg { fill: var(--fill); mix-blend-mode: multiply; } copy codeCopy the code

We use mix-blend-mode multiply effect. In addition, JavaScript dynamically changes the value of –fill code is also simple:

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

Pretty simple. And you can use your imagination to achieve a lot of creative results.

CSS custom properties

As described in the W3C specification, CSS custom properties are also referred to as CSS variables:

But what I’m talking about here is not how to use CSS customizations. I want to talk to you about invalid variables in CSS custom properties. Invalid variables in CSS custom properties are a useful feature to enable a 1 (true) and 0 (false) toggle effect. In other words, different states of the UI can be implemented very well.

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

It means:

When the value of a custom property is **initial**, the **var()**** function cannot replace it. Unless a valid fallback value is specified, this invalidates the declaration when the value is computed. 支那

To register a custom property in CSS, use –. You can assign a value to the registered custom property, including an empty string, but there is an interesting detail:

:root { --invalid:; // Note that there is no space or character between the colon and semicolon --valid:; // Note that there is only one space between the colon and semicolon} copy codeCopy the code

The –invalid custom property is called an invalid variable, while the –valid custom property is a valid variable. Using the above method to distinguish valid and invalid variables is very difficult for developers to read, and some text editors may format code to their own configured specifications, which can result in –invalid:; Become invalid:; (There is space). To do this, we typically use the keyword initial to explicitly declare a custom property as invalid:

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

If a registered custom property is not called using the var() function, then there is no effect on the registered custom property. The var() function takes two arguments. The first argument is the custom property, and the second argument is the alternate value. When the first parameter is an invalid value, the second parameter is taken. Because of this, for registered invalid custom attributes (that is, invalid variables), such as –invalid in the code above. If var() does not provide an alternate value (second argument), then the CSS style rule (declaration) is invalid when evaluating the value:

:root { --invalid: initial; // Invalid variable --valid:; } foo {background-color: var(--invalid); Color: var(--invalid, red) // Alternate value is provided, --invalid is invalid, alternate value is used red} Copy codeCopy the code

Let’s start with a simple example:

<div class="element"> <i>Element</i> </div> <i>Element</i> <style> .element { --color: red; } I {--foo: initial; // Invalid variables --color: var(--foo); Background-color: var(--color, orange); < span style = "max-width: 100%; clear: both; min-height: 1emCopy the code

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

Demo: codepen. IO/airen/full /…

Here’s another example:

<section>Element</section> <style> :root { --initial: initial; // Invalid variable --valid:; } section {background-color: var(--initial, red); Red color: var(--valid, red); < span style = "max-width: 100%; clear: both; min-height: 1emCopy the code

So here’s what you see:

Demo: codepen. IO/airen/full /…

The color property in this example is equivalent to setting color:; Value, which the client will ignore when calculating, so it inherits the color value of its ancestor element. In this case, it inherits the color value of the body element, black.

You’re probably already familiar with invalid variables in CSS custom properties. Let’s move on.

To make it easier for developers to understand The use of invalid variables in CSS custom properties, @Lea Verou writes in The –var: ; Hack to Toggle Multiple Values with one Custom Property Introduces a concept similar to Switch:

:root { --ON: initial; Var () --OFF:; // Copy the code for valid variables, equivalent to turning off the alternate value of var()}Copy the code

So that way, when we switch between different states of the UI, we just have to switch between –ON and –OFF. Take the example provided by @Lea Verou in the post:

:root { --ON: initial; --OFF: ; } button { --is-raised: var(--OFF); Border: 0.0px solid var(--is-raised, RGB (0.00/0.1)); border: 0.0px solid var(--is-raised, RGB (0.00/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.1em 0.1 em-0.1 em RGB (0 0 0/0.2)); Margin-top: 0.0px; margin-top: 0.0px; margin-top: 0.0px; margin-top: 0.0px; margin-top: 0.0px; margin-top: 0.0px; } button:hover { --is-raised: var(--ON); } button:active {box-shadow: var(--is-raised, 0 1px 0.2em black inset); } copy codeCopy the code

The final result 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’s a variety of skin peels:

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 the 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); } /* 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 codeCopy the code

The effect is as follows:

Demo: codepen. IO/airen/full /…

There is no JavaScript code used in the example. Isn’t that interesting. If you are interested, you can also try this effect.