2
头图

foreword

CSS variables (officially called custom properties) are user-defined values that can be set once and used multiple times in your codebase. They make it easier to manage color, font, size, and animation values and ensure consistency across a web application.

For example, you can set the brand color as a CSS property ( --primarycolor: #7232FA ) and use this value in any component or style that uses the brand color ( background: var(--primarycolor); ).

In addition to cleaner and less repetitive code, CSS variables can be used to build color palettes, improve responsiveness, and create dynamic type systems.

Define CSS variables

To define a custom property, select a name and prefix it with two hyphens. Any alphanumeric character can be part of the name. Hyphens ( - ) and underscores ( _ ) are also allowed. A wide range of Unicode characters can be part of custom attribute names. This also includes emoji, but for clarity and readability, stick to alphanumeric names.

Here is an example:

 --primarycolor: #0ad0f9ff; /* RGB alpha 16进制颜色 */

-- to the CSS interpreter that this is a custom property. When used as a variable, the interpreter engine replaces the property with its value.

Custom property names are case-sensitive. This means that --primaryColor and --primarycolor are considered two different attribute names. This is different from traditional CSS, where the case of properties and values doesn't matter. However, it is consistent with the variable name rules in ECMAScript.

Like other properties, say display or font , CSS custom properties must be defined inside a declaration block. A common pattern is to use the :root pseudo-element as a selector to define custom attributes.

 :root {
  --primarycolor: #0ad0f9ff;
}

:root is a pseudo-element that points to the root element in the document. For HTML documents, it refers to the <html> element. For an SVG document, it refers to the <svg> element. Using :root makes the attribute immediately available throughout the document.

Use CSS variables

In order to use the custom property as a variable, we need to use the var() function. For example, if we want to use the --primarycolor custom attribute as the background color, we need to do this:

 body {
    background-color: var(--primarycolor);
}

The value of our custom property will become the computed value of the background-color property.

So far, custom properties can only be used as variables to set values for standard CSS properties. For example, you can't store a property name as a variable and then reuse it. The following CSS will not work:

 :root {
    --top-border: border-top; /* Can't set a property as custom property's value */
    var(--top-border): 10px solid #bc84d8; /* Can't use a variable as a property */
}

You also can't store a property-value pair as a variable and reuse it. The following example is also invalid:

 :root {
    --text-color: 'color: orange'; /* Invalid property value */
}
body {
    var(--text-color); /* Invalid use of a property */
}

Finally, you can't concatenate a variable as part of a value:

 :root {
    --base-font-size: 10;
}
body {
    font: var(--base-font-size)px / 1.25 sans-serif; /* Invalid CSS syntax */
}

CSS custom properties vs CSS variables

"Custom Properties" is a future-proof name that says this feature might one day be used. However, this could change if browser vendors implement the CSS extension specification. This specification defines ways to extend CSS with custom selector combinations, functions, and at-rules .

We usually call custom properties "variables", and so far this is the only way we can use them. In theory, they are not exactly interchangeable terms. But so far in practice the terms are interchangeable. In this article, I mainly use custom properties , because that's their correct name. If the sentence is clearer, I will use the variable name.

set alternate value

var() function accepts two parameters. The first parameter is the custom property name. The second parameter is optional, but must be the declared value. The function of this declared value is to be used as an alternate or default value when the custom property value is not defined.

Let's take a look at the following CSS:

 .btn__call-to-action {
    background: var(--accent-color, deepskyblue);
}

If --accent-color is defined, and we assume its value is #f30 , then any background color with the class .btn__call-to-action will be orange. If not defined, the background color will be dark sky blue.

Declared values can also be nested. In other words, variables can be used as alternate values for var functions.

 body {
    background-color: var(--books-bg, var(--arts-bg));
}

In the above CSS, if --books-bg is defined, the background color will be set to the value of the --books-bg property. If not defined, the background color will be whatever value is assigned to --arts-bg . If neither property is defined, the background color will be the property's initial value. In this example, the initial value is transparent .

A similar situation occurs when a custom property's value is invalid for the property it uses. Consider the following CSS:

 :root {
    --text-primary: #600;
    --footer-link-hover: #0cg; /* Not a valid color value */
}
body {
    color: var(--text-primary);
}
a:link {
    color: blue;
}
a:hover {
    color: red;
}
footer a:hover {
    color: var(--footer-link-hover);
}

In this example, the --footer-link-hover attribute's value is not a valid color. Instead, footer a:hover inherits the color value from the <body> element.

Custom properties are parsed in the same way as other CSS values are parsed. If the value is invalid or undefined, the CSS parser will use the inherited value if the property is inheritable (eg color or font ). If the property is not inheritable, the CSS parser will use the initial value (eg background-color ).

Cascading values

Custom properties also follow cascading rules. Their values are overridden by subsequent rules:

 :root {
    --text-color: #190736; /* navy */
}
body {
    --text-color: #333;  /* dark gray */
}
body {
    color: var(--text-color);
}

In the above example, body the font color will be dark gray. We can also reset the value on a per selector basis. Let's add more rules to this CSS:

 :root {
    --text-color: #190736; /* navy */
}
body {
    --text-color: #333;   /* dark gray */
}
p {
    --text-color: #f60;  /* orange */
}
body {
    color: var(--text-color);
}
p {
    color: var(--text-color)
}

In this example, any text wrapped in the <p> tag will be orange. But the text inside <div> or the text inside other elements is still dark gray.

You can also use the style property to set the value of a custom property. For example, style="--brand-color: #9a09af" .

palette

Custom properties are especially useful for managing HSL palettes. HSL stands for hue, saturation, lightness . This is a luminance-based color model, similar to RGB. We can use HSL in CSS thanks to the hsl() and hsla() color functions. hsl() function receives three parameters, namely hue, saturation, and lightness, that is, hue, saturation, and brightness. hsla() function also receives the fourth parameter, which represents the color's alpha transparency (value range is between 0-1).

The RGB system expresses color in terms of red, green, and blue ratios, while HSL uses a color circle, with hue being a degree position on the circle, and hue or shading defined by saturation and lightness values. Saturation ranges from 0% to 100%, where 0% is gray and 100% is full color. Brightness also ranges from 0% to 100%, where 0% is black, 100% is white, and 50% is normal color.

调色板.png

In the HSL color system, the primary colors red, green, and blue are 120 degrees apart at 0 degrees/360 degrees, 120 degrees, and 240 degrees. The secondary colors -- cyan, magenta, and yellow -- are also 120 degrees apart, but on the opposite side of the primary colors, at 180 degrees, 300 degrees, and 60 degrees/420 degrees, respectively. Levels 3, 4, and other colors fall in between in about 10-degree increments. If blue was written in HSL, it would be hsl(240, 100%, 50%) .

HSL parameter units

When you use a unitless value in the first argument of the hsl() and hsla() functions, the browser assumes it is an angle in degrees. However, you can use any supported CSS angle unit . Blue can also be represented as hsl(240deg, 100%, 50%) , hsl(4.188rad, 100%, 50%) or hsla(0.66turn, 100%, 50%) .

This is where it gets interesting. We can set the hue value using custom properties, as well as brighter and darker shadows by adjusting saturation and brightness:

 :root {
    --brand-hue:      270deg;  /* purple */
    --brand-hue-alt:  .25turn; /* green */

  /*
    hsl()和hsla()可以接受逗号分隔的参数或空格分隔的参数。
    但旧的浏览器(如Internet Explorer 11)只支持逗号分隔的参数。
  */

    --brand-primary:   hsl( var( --brand-hue ) 100% 50% );
    --brand-highlight: hsl( var( --brand-hue ) 100% 75% );
    --brand-lowlight:  hsl( var( --brand-hue ) 100% 25% );
    --brand-inactive:  hsl( var( --brand-hue )  50% 50% );

    --brand-secondary:     hsl( var( --brand-hue-alt ) 100% 50% );
    --brand-2nd-highlight: hsl( var( --brand-hue-alt ) 100% 75% );
    --brand-2nd-lowlight:  hsl( var( --brand-hue-alt ) 100% 25% );
    --brand-2nd-inactive:  hsl( var( --brand-hue-alt )  50% 50% );
}

The CSS above provides the palette shown below.

image.png

This is a simplified version, but you can also use custom properties to adjust saturation and brightness values.

Robust palette generation

Another idea is to combine a custom property with the calc() function to generate a square color scheme from a base hue. Let's create a square color scheme in the example below. A square color scheme consists of four colors that are equidistant from each other on the color wheel, that is, 90 degrees apart:

 :root {
    --base-hue: 310deg; /* Hot pink */
    --distance: 90deg;

    --color-a: hsl( var(--base-hue), 100%, 50% );
    --color-b: hsl( calc( var(--base-hue) + var( --distance ) ), 100%, 50% );
    --color-c: hsl( calc( var(--base-hue) + ( var( --distance ) * 2 ) ), 100%, 50% );
    --color-d: hsl( calc( var(--base-hue) + ( var( --distance ) * 3 ) ), 100%, 50% );
}

CSS gives us a rather tropical color scheme like the one below.

image.png

Custom properties also work well with media queries, as we'll see in a later chapter.

dark theme palette

You can use CSS custom variables to define a range of variables related to the dark and light themes for your website.

Taking the following page style as an example, we can replace all HSL colors in different selectors with variables after defining custom attributes for the corresponding colors in :root :

 :root {
  /*...*/
  --nav-bg-color: hsl(var(--primarycolor) , 50%, 50%);
  --nav-text-color: hsl(var(--primarycolor), 50%, 10%);
  --container-bg-color: hsl(var(--primarycolor) , 50%, 95%);
  --content-text-color: hsl(var(--primarycolor) , 50%, 50%);
  --title-color: hsl(var(--primarycolor) , 50%, 20%);
  --footer-bg-color: hsl(var(--primarycolor) , 93%, 88%);
  --button-text-color: hsl(var(--primarycolor), 50%, 20%);
}

Appropriate names have been used for custom properties. For example, --nav-bg-color refers to the navigation bar background color, --nav-text-color refers to the navigation bar foreground color or text color.

Then, copy the :root selector and its contents, but add a theme attribute to it with the value dark :

 :root[theme='dark']{
  /*...*/
}

If a theme attribute with the value dark is added to the <html> element, then the theme will be activated.

Now we can manually manipulate these variable values to provide a dark theme by reducing the lightness value of the HSL color. Or we can use other techniques like CSS filters like invert() and brightness() which are usually used to adjust the rendering of images but can also be used for any other element.

Add the following code to :root[theme='dark'] :

 :root[theme='dark'] {
  --dark-hue: 240;
  --light-hue: 250;
  --primarycolor: var(--dark-hue);
  --nav-bg-color: hsl(var(--primarycolor), 50%, 90%);
  --nav-text-color: hsl(var(--primarycolor), 50%, 10%);
  --container-bg-color: hsl(var(--primarycolor), 50%, 95%);
  --content-text-color: hsl(var(--primarycolor), 50%, 50%);
  --title-color: hsl(--primarycolor, 50%, 20%);
  --footer-bg-color: hsl(var(--primarycolor), 93%, 88%);
  --button-text-color: hsl(var(--primarycolor), 50%, 20%);
  filter: invert(1) brightness(0.6);
}

invert() The filter inverts all colors of the selected elements (in this case, each element). The reversed value can be specified as a percentage or a number. A parameter value of 100% or 1 will completely invert the element's hue, saturation, and lightness values.

brightness() filter makes the element brighter or darker. If the value is 0, a completely dark element appears.

invert() The filter makes some elements very bright. The brightness needs to be reduced by setting brightness(0.6) .

Dark themes with varying degrees of dark:

dark-theme.png

Switch themes with JavaScript

Now, when the user clicks the dark/light button, we use JavaScript to toggle the dark and light themes. Add the inline <script> tag before the HTML </body> tag is closed, with the following code in the tag:

 const toggleBtn = document.querySelector("#toggle-theme");
toggleBtn.addEventListener('click', e => {
  console.log("Switching theme");
  if(document.documentElement.hasAttribute('theme')){
    document.documentElement.removeAttribute('theme');
  }
  else{
    document.documentElement.setAttribute('theme', 'dark');
  }
});

Document.documentElement refers to the root DOM element in the document, which is <html> . This code uses the .hasAttribute() method to check if the theme property exists. If it doesn't exist, add dark to this property, causing a switch to the dark theme. Otherwise, it will remove the property, causing a switch to the light theme.

Note: You should also use this in conjunction with the prefers-color-scheme feature in CSS, which can be used to automatically change the light/dark theme from the user's OS or user agent (browser) settings. This will be shown in the next section.

media inquiries

We can also use custom attributes in media queries. For example, you can use custom properties to define light and dark color schemes:

 :root {
    --background-primary: hsl(34, 78%, 91%);
    --text-primary: hsl(25, 76%, 10%);
    --button-primary-bg: hsl(214, 77%, 10%);
    --button-primary-fg: hsl(214, 77%, 98%);
}
@media screen and ( prefers-color-scheme: dark ) {
    :root {
      --background-primary: hsl(25, 76%, 10%);
      --text-primary: hsl(34, 78%, 91%);
      --button-primary-bg: hsl(214, 77%, 98%);
      --button-primary-fg: hsl(214, 77%, 10%);
  }
}

Likewise, we can use custom properties to change the base font size for screen and print.

 :root {
    --base-font-size: 10px;
}
@media print {
    :root {
        --base-font-size: 10pt;
    }
}
html {
    font: var(--base-font-size) / 1.5 sans-serif;
}
body {
    font-size: 1.6rem;
}

In this example, we are using media units suitable for print and screen. For both media, we'll use a base font size of 10 units, pixels (px) for screen and points (pt) for print. We'll also use the value of --base-font-size: 3926620405cba8af807c0a0fa1ed44dd--- to set a starting size for our root element ( html ). We can then adjust the typography relative to the base font size in rem units.

Using custom properties in JavaScript

Remember: custom properties are CSS properties, and we can interact with them. For example, we can use the CSS.supports() API to detect whether the browser supports custom attributes:

 const supportsCustomProps = CSS.supports('--primary-text: #000');

// 如果浏览器支持自定义属性,则打印true
console.log(supportsCustomProps);

We can also use the setProperty() method to set custom property values:

 document.body.style.setProperty('--bg-home', 'whitesmoke');

Using removeProperty() is similar. Just pass the custom property name as a parameter:

 document.body.style.removeProperty('--bg-home');

To use a custom property as a value in JavaScript, you can use the var() function with the property name as its parameter.

 document.body.style.backgroundColor = 'var(--bg-home)';

Also, you cannot use the style object's square bracket syntax or camelCase attributes to set custom attributes. In other words, document.body.style.--bg-home or document.body.style['--bg-home'] will work.

Use custom properties in components

JavaScript frameworks like React, Angular, and Vue let developers use JavaScript to create reusable, shareable chunks of HTML, often defining CSS at the component level.

Here is an example of a React component, written using JSX syntax:

 import React from 'react';

/* Importing the associated CSS into this component */
import '../css/field-button.css';

class FieldButtonGroup extends React.Component {
    render() {
        return (
            <div className="field__button__group">
                <label htmlFor={this.props.id}>{this.props.labelText}</label>
                <div>
                    <input type={this.props.type}
                      name={this.props.name}
                      id={this.props.id}
                      onChange={this.props.onChangeHandler} />
                    <button type="submit">{this.props.buttonText}</button>
                 </div>
            </div>
        );
    }
}

export default FieldButtonGroup;

Our React component imports CSS into a JavaScript file. When compiling, the contents of field-button.css are loaded inline. Here's a possible way to use custom properties:

 .field__button__group label {
    display: block;
}
.field__button__group button {
    flex: 0 1 10rem;
    background-color: var(--button-bg-color, rgb(103, 58, 183)); /* include a default */
    color: #fff;
    border: none;
}

In this example, we use a custom attribute --button-bg-color as the button's background color, along with the default color, in case --button-bg-color is not defined. Here, we can set the value of --button-bg-color in the global stylesheet or via the style property.

Let's set the value to a React property. React props (properties for short) mimic element properties. They are a way to pass data into React components. In this example, we will add a property named buttonBgColor :

 import FieldButtonGroup from '../FieldButtonGroup';

class NewsletterSignup extends React.Component {
    render() {
        // For brevity, we've left out the onChangeHandler prop.
        return (
            <FieldButtonGroup type="email" name="newsletter" id="newsletter"
              labelText="E-mail address" buttonText="Subscribe"
              buttonBgColor="rgb(75, 97, 108)" />
        );
    }
}

export default NewsletterSignup;

Now, we need to update the FieldButtonGroup component to support this change:

 class FieldButtonGroup extends React.Component {
    render() {
        /*
        In React, the style attribute value must be set using a JavaScript
        object in which the object keys are CSS properties. Properties
        should either be camelCased (e.g. backgroundColor) or enclosed in
        quotes.
        */

        const buttonStyle = {
            '--button-bg-color': this.props.buttonBgColor
        };

        return (
            <div className="field__button__group">
                <label htmlFor={this.props.id}>{this.props.labelText}</label>
                <div>
                    <input type={this.props.type} 
                      name={this.props.name} id={this.props.id}
                      onChange={this.props.onChangeHandler} />
                    <button type="submit" style={buttonStyle}>
                      {this.props.buttonText}
                    </button>
                </div>
            </div>
        );
    }
}

In the above code, we add a buttonStyle object that contains the name of the custom property and set its value to the value of the buttonBgColor property and add style for the button style property.

Using the style attribute may go against what you've learned about writing CSS. One of the selling points of CSS is that we can define a set of styles for use in multiple HTML and XML documents. On the other hand, the style attribute limits the scope of CSS to the element it is applied to, preventing us from reusing it. And also can't take advantage of stacking.

But in a component-based front-end architecture, a component may be used by multiple teams in multiple situations, and may even be shared across client projects. In these cases, you may want to combine the cascading global scope with the local scope provided by the style property.

Setting a custom attribute value with the style attribute limits the effect to this specific instance of the FieldButtonGroup component. However, since we're using a custom property instead of a standard CSS property, we still have the option to define --button-bg-color in a stylesheet instead of as a component property.

Summarize

Custom properties take one of the best features of the preprocessor, variables, and make it a native feature of CSS. Using custom properties, we can:

  • Create reusable, themed components
  • Easily adjust padding, margins, and typography to fit various viewport sizes and media
  • Improve consistency of CSS color values

Variables have a range of applications and are particularly useful in component-based design systems. I hope you now have a better understanding of how to use variables or custom properties in CSS.

The above is all the content. If it is helpful to you, please like, collect and forward~


chuck
303 声望41 粉丝