Simple introduction to CSS variables

Simple introduction to CSS variables

What's a CSS variable?

A CSS variable is an defined name with a valid CSS value associated to it, it's defined by using -- at the start of the variable name and then it can be use as the value of a CSS property by implementing the var() function.

Example:

.css-class{
--text-size: 15px;
font-size: var(--text-size)
}

CSS variables are great way to make our styles easier to be reusable in different scenarios and help to make our style sheets more organized, specially when out styles start to become bigger and we want to keep a global and specified set of styles to use.

Variables and scopes

Our variables have an specific scope depending on where they are defined and where they are applied.

Global scope
If we want our variables to be used globally, we can defined them on the :root identifier and they all can be acceded from other identifiers (id,classes,elements).

:root{
--rounded-border: 8px;
--card-height: 300px;
--card-width: 300px;
}

.card{
border-radius: var(--rounded-border);
height:var(--card-height);
width:var(--card-width);
margin: 10px;
}

img{
height:var(--card-height);
width:var(--card-width);
}

#redirect-button{
border-radius:  var(--rounded-border);
}

Cascade scope
Variables cascade the same way styles does. what does this mean? it means that variables can be acceded according to the order of how the styles are specified to be applied.

Lets look an example:

<div class="card">
<label>A label</label>
<span>
<label>A label inside an span</label>
</span>
</div>
.card{
height:150px;
width:300px;
}

.card label{
font-weight: bold;
}

In the previous example, our style will cascade to all label elements inside the class card applying bold font weight. So if we want to be more specific and say that we only only want to apply the bold font weight to all the labels which parent is the card class.

.card{
height:150px;
width:300px;
}

.card > label{
font-weight: bold;
}

Now the style wont cascade to the label inside the span element, it will only cascade to labels that are direct children of the card class.

This works the same for our variables!

.card{
--label-font-weight: bold;
height:150px;
width:300px;
}

.card label{
font-weight: var(--label-font-weight);
}

In this case the variable --label-font-weight will affect all label elements inside the card class.

.card{
--label-font-weight: bold;
height:150px;
width:300px;
}

.card > label{
font-weight: var(--label-font-weight);
}

Now the variable will affect only the direct children that are label elements.

But remember that our variable exist inside the card class, so it ca be used by all the elements inside it so it can be used on the label inside the span by being more specific.

/*apply to all labels inside span*/
.card > span  label{
font-weight: var(--label-font-weight);
}

/*apply to all labels which parent is a span*/
.card > span > label{
font-weight: var(--label-font-weight);
}

You can use calculated values

That's right! you can keep calculated values inside a variable when using it with the function calc()

:root{
--card-height: 300px;
--card-width: 300px;
--secondary-card-width: calc(var(--card-width) - 100px)
}

.secondary-card{
width: var(--secondary-card-width)
}

We can also make the value dynamic by calculating with changing values like the screen height!

:root{
--card-height: 300px;
--card-width: 300px;
--calculated-card-height: calc(100vh-var(--card-height))
}

.dynamic-card{
width: var(--calculated-card-height)
}

The height of the dynamic-card height will now be calculated, and will be the screen height minus the value of the --card-height variable, if the screen height changes so will change the dynamic-card class height.

Variable Inheritance

Variables can be inherit from the parent container and can be use and changed inside the children elements.

<div class="parent-card">
  <label>PARENT<label>
    <span class="first-child-card">First Child</span>
   <span class="second-child-card">Surprise!</span>
</div>
.parent-card{
--text-color: salmon;
}

.first-child-card{
color: var(--text-color);
}

.second-child-card{
--text-color: powderblue;
color: var(--text-color);
}

we defined out --text-color variable which first-child-card and second-child-card class inherit. The first-child-card apply the variable with no problems and second-child-card changes it value first and then applies it. If we applied the variable on a third-child-card then its value will be salmon since the change of value happens on another class that also inherited it, the parent value is still salmon.

Fallback value

The var() function can receive a second parameter thar works as the default value in case the variable is not defined.

.card{
height:var(--card-height,300px);
width:var(--card-width,300px);
}

If the variables --card-height, --card-width are not defined then the 300px value will be applied instead.

You can also use another variable as a fallback value

.card{
--secondary-card-height: 500px
height:var(--card-height,--secondary-card-height);
width:var(--card-width,300px);
}

Accessing CSS variable with JS

To have access to a CSS variable we use the getComputedStyle function and passing it the element using variables we want to have access or accessing the specific element property.

/*getting the element using card class*/
const card = querySelector(".card")

/*getting the value of the css varaiable --card-height*/

//directly by the element
let height = card.style.getPropertyValue("--card-height")

//using getComputedStyle
let height_b = getComputedStyle(card).getPropertyValue("--card-height")

/*changing the variable value*/
card.style.setProperty("--card-height",height+100)

Code pen example