We eagerly need variables in CSS by long ages. Now, with build systems, we can use variables with Sass and Less. What CSS Variables will bring to us? Something Sass/Less never give us.

Dynamic

   The places of variables in Sass/Less, will token by constants after build. Therefore, you can not change them. They are not real variables. On the other hand, CSS Variables is real variables, you can change them by @media or JS.

Better Scope Role

   CSS Variables, also named Custom Properties, they don’t like variables in Sass/Less, but like normal CSS Properties. The scope of variables in Sass/Less is between a pair of {}. CSS Variables, however, like other CSS Properties, defined in CSSOM and bing to DOM with selector. and available in the DOM element and its children (and children of children, and children of children of children…). So the scope is depend on DOM tree. That’s impossible in Sass/Less.

Componentization and Custom Properties

button
{
	min-width: calc(var(--button-size, 1)*1rem);
	height: calc(var(--button-size, 1)*1rem);
}

body>main
{
	--button-size: 1.5;
}

   In this example, we set size of buttons with --button-size. If it’s undefined, use the default value 1. This rule apply on all <button>s in the page. And we set --button-size: 1.5 on selector body>main. So the variable will apply on <main> directly belongs to <body> and it’s children. As we see, height of the <button>s in this <main> will be 1.5rem, and the height of the <button>s outside will be 1rem.

   You may say, what? use a variable before declaring?

   Um, let’s think out side the box, the button {} as a function, and --button-size as the parameter. The function called on rendering of <button>, and the value defined on body>main pass in.

   This little example show the basic componentization. The <button> is a component with styles inside, and with some parameters. We keep not changing styles inside the box, but only change the parameters when use it. The CSS Variables becomes the properties of the component.

   It’s hard to make world ordered by this primitive componentization. Fortunately, we have Web Components.

Work with Web Components

   In Custom Elements, styles are stand alone with outside. But the CSS Variables can penetrate the boundary.

   Here is a example.

:host
{
	--button-size: 1.5;
}

button
{
	font-size: var(--font-size, 1rem);
	min-width: calc(var(--button-size)*1rem);
	height: calc(var(--button-size)*1rem);
}

   Here, we set size of buttons just like previous. And, we declare --button-size on the :host, but not --font-size. The difference is: the variable --font-size can be defined on any layer outside, but the --button-size must be defined on the custom element itself. We can use two ways on different scene. The way like --button-size, is the common way to defined a parameter of element. And the way like --font-size, can use to access global configurations.

Interaction with JS

   Here are the API of CSS properties (include Custom Properties) for JS.

style instanceof CSSStyleDeclaration;
/*
 * The style can be a style object of a DOM element:
 *   document.body.style
 *
 * And can be a CSS rule:
 *   document.styleSheets[0].cssRules[0].style
 *
 * Also can be a computed result (read only):
 *   getComputedStyle( element, )
 */

// get value of a CSS property, (always got a string, even the style is undefined)
const value= style.getPropertyValue( '--foo', );

// set a property (the value always be converted to a string)
style.setProperty( '--foo', value, );

// remove a property
style.removeProperty( '--foo', );

   This is the better way to access CSS properties then read or assign the properties of the style object.

// good
style.setProperty( 'z-index', '1', );
style.setProperty( 'float', 'left', );

// bad
style.zIndex= '1';
style.cssFloat= 'left';

   Here is a situation that we get the position of mouse by JS, and read the layout. Then make some calculations with JS. And set some value to styles. That’s dreadful, JS has a hand in the layout, break the separation of concerns. And when JS read the layout, unnecessary reflow will happen.

   Using CSS Variables, the problems solved perfectly. You just need to pass the position from JS to CSS Variables. And do nothing else in JS. In css, using calc() to take layout calculations. The separation of concerns is obeyed. Unnecessary reflows are prevented too.

Data Types

   In CSS, the basic data types are number(with unit), string, keyword, color and so on, and list, besides. All of them can be the type of CSS Variables.

.foo
{
	--an-int: 3;
	--a-float: 5.2;
	--with-unit: 80%;
	--a-color: white;
	--a-string: 'foobar';
	--a-keyword: left;
	--a-list: 1px 1px;
	--another-list: green , 1px;
	--more-another-list: 20% / 1em;
}

   You can combine them and calculate. You must pay attention to the unit when calculating. To the lists, you need assume , and / as elements of list just like 1px.

.foo
{
	/* basic using */
	z-index: var(--an-int);
	font-size: var(--a-float);
	width: var(--with-unit);
	color: var(--a-color);
	font-family: var(--a-string);
	float: var(--a-keyword);
	margin: var(--a-list);
	border-radius: var(--more-another-list);
	
	/* complex using */
	background-color: hsla(var(--an-int),var(--with-unit),calc(var(--an-int)*20%));
	box-shadow: var(--a-list) 5px var(--another-list) 2px 2px red;
	border-radius: 4px 4px 4px var(--more-another-list) 4px 4px 4px;
}

Work with Animation and Transition

   Unfortunately, there is no browser support transition of CSS Variables. We can only use animation and transition on normal CSS properties.

.foo
{
	--lightness: 80%;
	color: hsla(120,100%,var(--lightness));
	background-color: hsla(0,100%,var(--lightness));
	
	/* Not supported */
	transition:
		--lightness 500ms
	;
	
	/* Supported */
	transition:
		color 500ms
		,
		background-color 500ms
	;
}

.foo:hover
{
	--lightness: 50%;
}

   Therefore, some exciting idea cannot achieve. Such as change hue and lightness by different speed, change color of gradient background… Let’s just await for future.