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.
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.
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.
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:
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~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。