Written by Idorenyin Obong✏️
Editor's note: This article was updated by Emmanuel John in March 2025 to include instructions on setting CSS variables dynamically with JavaScript, differentiate between CSS and SASS variables, and troubleshoot common developer issues with CSS variables.
CSS offers many predefined standard key-value-based properties for styling semantic HTML elements. However, while designing webpages, developers often need to repetitively use the same values for properties in several segments of stylesheets — for example, while using a primary accent color for various webpage elements.
CSS now supports using custom properties, also known as CSS variables, to avoid repetitive CSS property values. Like any popular programming language, CSS also implements variables for writing clean code with a productive assignment and retrieval syntax, scoping support, and fallback values.
In this tutorial, we’ll first demystify CSS variables and then build four simple projects that utilize them. Some basic CSS knowledge is required to follow along with this tutorial. Let’s dive in!
What are CSS variables?
CSS variables are user-defined values that can be reused throughout a stylesheet. They are also known as custom properties*.* The --
prefix and var()
function is used to define and access CSS variables respectively:
:root {
--primary-color: #3498db;
}
button {
background-color: var(--primary-color);
}
Unlike traditional CSS properties, CSS variables can be modified dynamically with JavaScript using (element.style.setProperty
). CSS variables can be changed in one place and all elements using it update automatically. They can be defined within selectors or globally using (:root
).
One of the most common use cases for CSS variables is managing websites in which numerous values are similar to those in the document. This helps to reduce the friction associated with refactoring or updating your code.
What we’ll build in this tutorial
To solidify our knowledge about CSS variables, we’ll build four very simple projects:
- Button variations — This concept is popular in Bootstrap, where certain elements share CSS rules that give them a default design but are differentiated by colors or other properties
- Theme-based design — Specifically, a light-and-dark theme manipulated by JavaScript
- A responsive login form — We’ll display different layouts on desktop, tablet, and mobile screens
- JavaScript-free dynamic elements — This project generates a colorful native checkbox list
Each project should provide insights into how we can use CSS variables to take care of a wide variety of use cases.
Also referred to as custom properties or cascading variables, CSS variables have myriad use cases.
How to declare and use CSS variables
CSS variables can be declared in two ways (--
prefix and @property
at-rule).
--
prefix
The --
prefix declares variables in two ways (globally and locally). The former uses the :root
selector to define global variables:
:root {
--primary-color: blue;
--font-size: 16px;
}
While the latter defines a variable inside specific elements:
.card {
--card-bg: lightgray;
background-color: var(--card-bg);
}
Here, --card-bg
is only accessible inside .card
. Global variables are accessible everywhere in the stylesheet.
@property
at-rule
The @property
at-rule allows you to be more expressive with the definition of CSS variables by allowing you to define their type, control inheritance, and set default values which act as fallback. Using the @property
at-rule ensures more predictable behavior.
@property --card-color {
syntax: "<color>";
inherits: false;
initial-value: #FFFFFF;
}
Here, --card-color
is declared as a CSS variable that expects <color>
value. The inherits:false;
property prevents it from being inherited by child elements, and initial-value:#FFFFFF;
sets a default color when no <color>
value is assigned.
How to use variables in CSS**
CSS variables can be applied to elements using the var()
function:
button {
background-color: var(--primary-color);
font-size: var(--font-size);
}
If the value of --primary-color
is updated, all the elements using it will automatically change.
CSS variables inheritance**
Like traditional CSS properties, CSS variables follow standard property rules — i.e., they inherit, can be overridden, and adhere to the CSS specificity algorithm. The value of an element is inherited from its parent elements if no custom property is defined in a specific child element, as shown in the following example.
The HTML:
<div class="container">
<article class="post">
<h1 class="post-title">Heading text</h1>
<p class="post-content">Paragraph text</p>
</article>
</div>
The CSS:
.container {
--padding: 1rem;
}
.post {
--padding: 1.5rem;
}
.post-content {
padding: var(--padding);
}
In this case, the .post-content
selector inherits padding value from its direct parent, .post
, with the value of 1.5rem
rather than 1rem
. You can use Chrome DevTools to see from where the specific CSS variable value gets inherited, as shown in the following preview: You can use CSS variable inheritance to pass variable values from parent elements to child elements without re-declaring them in selectors. Also, overriding variable values is possible as traditional CSS properties.
CSS variables cascading
CSS cascade rules handle the precedence of CSS definitions that come from various sources. CSS variables also follow the standard cascade rules as any other standard properties. For example, if you use two selectors with the same specificity score, the variable assignment order will decide the value of a specific CSS variable.
A variable assignment in a new CSS block typically overrides the existing precedence and re-assigns values to variables. Let’s understand variable cascading rules with a simple example.
The HTML:
<span class="lbl lbl-ok">OK</span>
The CSS:
.lbl {
--lbl-color: #ddd;
background-color: var(--lbl-color);
padding: 6px;
}
.lbl-ok { --lbl-color: green }
/* --- more CSS code ---- */
/* ---- */
.lbl-ok { --lbl-color: lightgreen }
The above CSS selectors have the same specificity, so CSS uses cascading precedence to select the right lbl-color
value for elements. Here, we’ll get the lightgreen
color for the span
element since lightgreen
is in the last variable assignment. The color of the label may change based on the order of the above selectors.
CSS variables also work with developer-defined cascade layers that use the @layer
at-rule. To demonstrate this, we can add some cascade layers to the above CSS snippet:
@layer base, mods;
@layer base {
.lbl {
--lbl-color: #ddd;
background-color: var(--lbl-color);
padding: 6px;
}
}
@layer mods {
.lbl-ok { --lbl-color: lightgreen }
}
You can check how cascading rules overwrite variable values with Chrome DevTools:
Fallback and invalid values**
When using custom properties, you might reference a custom property that isn’t defined in the document. You can specify a fallback value to be used in place of that value.
The syntax for providing a fallback value is still the var()
function. Send the fallback value as the second parameter of the var()
function:
:root {
--light-gray: #ccc;
}
p {
color: var(--light-grey, #f0f0f0); /* No --light-grey, so #f0f0f0 is
used as a fallback value */
}
Did you notice that I misspelled the value --light-gray
? This should cause the value to be undefined, so the browser loads the fallback value, #f0f0f0
for the color
property.
A comma-separated list is also accepted as a valid fallback value. For example, the following CSS definition loads red, blue
as the fallback value for the gradient function:
background-image: linear-gradient(90deg, var(--colors, red, blue));
You can also use variables as fallback values with nested var()
functions. For example, the following definition loads #ccc
from --light-gray
if it’s defined:
color: var(--light-grey, var(--light-gray, #f0f0f0));
Note that it’s generally not recommended to nest so many CSS functions due to performance issues caused by nested function parsing. Instead, try to use one fallback value with a readable variable name. If your web app should work on older web browsers that don’t support custom properties, you can define fallback values outside of the var()
function as follows:
p {
color: #f0f0f0; /* fallback value for older browsers */
color: var(--light-grey);
}
If the browser doesn’t support CSS variables, the first color
property sets a fallback value. We’ll discuss browser compatibility in the upcoming section about browser support for the CSS variables feature.
Meanwhile, custom properties can get invalid values due to developer mistakes. Let’s learn how the browser handles invalid variable assignments and how to override the default invalid assignment handling behavior:
:root {
--text-danger: #ff9500;
}
body {
--text-danger: 16px;
color: var(--text-danger);
}
In this snippet, the --text-danger
custom property was defined with a value of #ff9500
. Later, it was overridden with 16px
, which isn’t technically wrong. But when the browser substitutes the value of --text-danger
in place of var(--text-danger)
, it tries to use a value of 16px
, which is not a valid property value for color in CSS.
The browser treats it as an invalid value and checks whether the color property is inheritable by a parent element. If it is, it uses it. Otherwise, it falls back to a default color (black in most browsers).
This process doesn’t bring the correct initial value defined in the :root
selector block, so we have to define custom properties with the accepted type and initial value using the @property
at-rule, as shown in the following code snippet:
@property --text-danger {
syntax: "<color>";
inherits: true;
initial-value: #ff9500;
}
body {
--text-danger: 16px;
color: var(--text-danger);
}
Now, the browser renders the expected text color even if we assign an invalid value within the body
selector.
Creating scoped CSS variables
As discussed in previous examples, it’s possible to create global CSS variables using either :root
or @property
at-rule. Also, creating local variables is possible by defining variables inside child element selectors. For example, a variable defined inside header
won’t be exposed to body
.
However, if you define a variable inside a specific <style>
tag, it gets exposed to all elements that match the particular selector. What if you need to create a scoped variable that is only available for a targeted HTML segment?
By default, browsers won’t scope style tags even if we wrap them with elements like <div>
for creating scoped variables. The @scope
at-rule helps us implement scoped CSS variables with scoped style tags, as shown in the following HTML snippet:
<style>
button {
padding: 6px 18px;
border: none;
border-radius: 4px;
margin: 12px;
background-color: var(--accent-color, #4cc2e6);
}
</style>
<div>
<style>
@scope {
button {
--accent-color: #f2ba2c;
}
}
</style>
<button>Sample button #1</button>
</div>
<button>Sample button #2</button>
Here, the second style tag becomes scoped for the wrapped <div>
element because of the @scope
at-rule. So, the button
selector in the second style tag selects only buttons inside the parent <div>
element. As a result, --accent-color
is only available for the first button.
The first button gets the #f2ba2c
color for the background since the scoped style tag’s button
selector sets the --accent-color
variable. The second button gets the #4cc2e6
fallback background color since the --accent-color
scoped variable is not available in the global scope: Learn more about the
@scope
at rule from the official MDN documentation. @scope
is still an experimental feature, so you can use the minimal css-scope-inline
library to create scoped CSS variables in production.
Best practices for structuring CSS variables in projects
Variables should be grouped logically as follows:
:root {
/* Colors */
--primary-color: #3498db;
--secondary-color: #2ecc71;
/* Typography */
--font-size-base: 16px;
--font-weight-bold: 700;
}
Fallback values should be used to ensure compatibility:
color: var(--text-color, black);```
<p>If <code>--text-color</code> is not defined, <code>black</code> will be used as a default. <strong>Use meaningful names (avoid <code>--color1</code>, <code>--sizeA</code>)</strong></p>
<h2>
<a name="project-1-building-button-variations" href="#project-1-building-button-variations">
</a>
Project 1: Building button variations
</h2>
<p>In CSS frameworks such as <a href="https://blog.logrocket.com/bootstrap-adoption-guide/">Bootstrap</a>, variables make sharing a base design across elements much easier. Take the <code>.bg-danger</code> class, which turns an element’s background color to red and its own color to white. In this first project, you’ll build something similar. </p>
<p>Get started with the first project by adding the following HTML document to a new <code>.html</code> file:<br>
</p>
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span> <span class="nt">/></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span> <span class="nt">/></span>
<span class="nt"><meta</span> <span class="na">http-equiv=</span><span class="s">"X-UA-Compatible"</span> <span class="na">content=</span><span class="s">"ie=edge"</span> <span class="nt">/></span>
<span class="nt"><title></span>CSS Variables - Button Variations<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><section></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="nt"><h1</span> <span class="na">class=</span><span class="s">"title"</span><span class="nt">></span>CSS Color Variations<span class="nt"></h1></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"btn-group"</span><span class="nt">></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-primary"</span><span class="nt">></span>Primary<span class="nt"></button></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-secondary"</span><span class="nt">></span>Secondary<span class="nt"></button></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-link"</span><span class="nt">></span>Link<span class="nt"></button></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-success"</span><span class="nt">></span>Success<span class="nt"></button></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-error"</span><span class="nt">></span>Error<span class="nt"></button></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></section></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<p></p>
<p>The structure of this markup is pretty standard. Notice how each button element has two classes: the <code>btn</code> class and a second class. We’ll refer to the <code>btn</code> class, in this case, as the base class and the second class as the modifier class that consists of the <code>btn-</code> prefix. </p>
<p>Next, add the following style tag content to the above:<br>
</p>
<pre class="highlight css"><code><span class="o"><</span><span class="nt">style</span><span class="o">></span>
<span class="o">*</span> <span class="p">{</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nd">:root</span> <span class="p">{</span>
<span class="py">--primary</span><span class="p">:</span> <span class="m">#0076c6</span><span class="p">;</span>
<span class="py">--secondary</span><span class="p">:</span> <span class="m">#333333</span><span class="p">;</span>
<span class="py">--error</span><span class="p">:</span> <span class="m">#ce0606</span><span class="p">;</span>
<span class="py">--success</span><span class="p">:</span> <span class="m">#009070</span><span class="p">;</span>
<span class="py">--white</span><span class="p">:</span> <span class="m">#ffffff</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* base style for all buttons */</span>
<span class="nc">.btn</span> <span class="p">{</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">1rem</span> <span class="m">1.5rem</span><span class="p">;</span>
<span class="nl">background</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="m">700</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="m">0.5rem</span><span class="p">;</span>
<span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* variations */</span>
<span class="nc">.btn-primary</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--primary</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--white</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.btn-secondary</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--secondary</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--white</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.btn-success</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--success</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--white</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.btn-error</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--error</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--white</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.btn-link</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--primary</span><span class="p">);</span>
<span class="p">}</span>
<span class="o"></</span><span class="nt">style</span><span class="o">></span>
</code></pre>
<p></p>
<p>The <code>btn</code> class contains the base styles for all the buttons and the variations come in where the individual modifier classes get access to their colors, which are defined at the <code>:root</code> level of the document. This is extremely helpful not just for buttons, but for other elements in your HTML that can inherit the custom properties. </p>
<p>For example, if tomorrow you decide the value for the <code>--error</code> custom property is too dull for a red color, you can easily switch it up to <code>#f00000</code>. Once you do so, voila — all elements using this custom property are updated with a single change! </p>
<p>Here’s what your first project should look like: <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8u2ugfq6exeln38vbedd.png" alt="Preview Of Project Using Css To Build Button Color Variations"> You can access the complete source code and see a live preview of this project from <a href="https://codepen.io/shalithasuranga/pen/dyrKEyd">this CodePen</a>.</p>
<h2>
<a name="project-2-setting-css-variables-dynamically-with-javascript" href="#project-2-setting-css-variables-dynamically-with-javascript">
</a>
Project 2: Setting CSS variables dynamically with JavaScript
</h2>
<p>The <code>document.documentElement.style.setProperty</code> method is used to set CSS variables dynamically with JavaScript, updating CSS variables in real-time without modifying the style sheet:<br>
</p>
<pre class="highlight css"><code><span class="nt">document</span><span class="nc">.documentElement.style.setProperty</span><span class="o">(</span><span class="s2">'--primary-color'</span><span class="o">,</span> <span class="s2">'green'</span><span class="o">)</span>
</code></pre>
<p></p>
<p>This will update the <code>--primary-color</code> variable, affecting all the elements that use it. </p>
<p>To see the practical use case for this, we’ll build the second project “a light-and-dark theme”. The light theme will take effect by default unless the user already has their system set to a dark theme. On the page, we’ll create a toggle button that allows the user to <a href="https://blog.logrocket.com/create-better-themes-with-css-variables">switch between themes</a>. </p>
<p>First, add the following HTML structure into a new <code>.html</code> file:<br>
</p>
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span> <span class="nt">/></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span> <span class="nt">/></span>
<span class="nt"><meta</span> <span class="na">http-equiv=</span><span class="s">"X-UA-Compatible"</span> <span class="na">content=</span><span class="s">"ie=edge"</span> <span class="nt">/></span>
<span class="nt"><title></span>CSS Variables - Theming<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><header></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container-inner"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"#"</span> <span class="na">class=</span><span class="s">"logo"</span><span class="nt">></span>My Blog<span class="nt"></a></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"toggle-button-container"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"toggle-button-label"</span> <span class="na">for=</span><span class="s">"checkbox"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">class=</span><span class="s">"toggle-button"</span> <span class="na">id=</span><span class="s">"checkbox"</span> <span class="nt">/></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"toggle-rounded"</span><span class="nt">></div></span>
<span class="nt"></label></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></header></span>
<span class="nt"><article></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="nt"><h1</span> <span class="na">class=</span><span class="s">"title"</span><span class="nt">></span>Title of article<span class="nt"></h1></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"info"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"tags"</span><span class="nt">></span>
<span class="nt"><span></span>#html<span class="nt"></span></span>
<span class="nt"><span></span>#css<span class="nt"></span></span>
<span class="nt"><span></span>#js<span class="nt"></span></span>
<span class="nt"></div></span>
<span class="nt"><span></span>1st February, 2024<span class="nt"></span></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"content"</span><span class="nt">></span>
<span class="nt"><p></span>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">></span>Link to another url<span class="nt"></a></span> Eius, saepe optio! Quas
repellendus consequuntur fuga at. Consequatur sit deleniti, ullam
qui facere iure, earum corrupti vitae laboriosam iusto eius magni,
adipisci culpa recusandae quis tenetur accusantium eum quae harum
autem inventore architecto perspiciatis maiores? Culpa, officiis
totam! Rerum alias corporis cupiditate praesentium magni illo, optio
nobis fugit.
<span class="nt"></p></span>
<span class="nt"><p></span>
Eveniet veniam ipsa similique atque placeat dignissimos
quos reiciendis. Odit, eveniet provident fugiat voluptatibus esse
culpa ullam beatae hic maxime suscipit, eum reprehenderit ipsam.
Illo facilis doloremque ducimus reprehenderit consequuntur
cupiditate atque harum quaerat autem amet, et rerum sequi eum cumque
maiores dolores.
<span class="nt"></p></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></article></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<p></p>
<p>This snippet represents a simple blog page with a header, a theme toggle button, and a dummy article. </p>
<p>Next, add the following style tag to add CSS definitions for the above HTML structure:<br>
</p>
<pre class="highlight css"><code><span class="o"><</span><span class="nt">style</span><span class="o">></span>
<span class="nd">:root</span> <span class="p">{</span>
<span class="py">--primary-color</span><span class="p">:</span> <span class="m">#0d0b52</span><span class="p">;</span>
<span class="py">--secondary-color</span><span class="p">:</span> <span class="m">#3458b9</span><span class="p">;</span>
<span class="py">--font-color</span><span class="p">:</span> <span class="m">#424242</span><span class="p">;</span>
<span class="py">--bg-color</span><span class="p">:</span> <span class="m">#ffffff</span><span class="p">;</span>
<span class="py">--heading-color</span><span class="p">:</span> <span class="m">#292922</span><span class="p">;</span>
<span class="py">--white-color</span><span class="p">:</span> <span class="m">#ffffff</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* Layout */</span>
<span class="o">*</span> <span class="p">{</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">html</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="n">-apple-system</span><span class="p">,</span> <span class="n">BlinkMacSystemFont</span><span class="p">,</span> <span class="s2">'Segoe UI'</span><span class="p">,</span> <span class="n">Roboto</span><span class="p">,</span> <span class="n">Oxygen</span><span class="p">,</span>
<span class="n">Ubuntu</span><span class="p">,</span> <span class="n">Cantarell</span><span class="p">,</span> <span class="s2">'Open Sans'</span><span class="p">,</span> <span class="s2">'Helvetica Neue'</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--bg-color</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--font-color</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.container</span> <span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="nl">max-width</span><span class="p">:</span> <span class="m">768px</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span> <span class="m">1rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.container-inner</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">justify-content</span><span class="p">:</span> <span class="n">space-between</span><span class="p">;</span>
<span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* Using custom properties */</span>
<span class="nt">a</span> <span class="p">{</span>
<span class="nl">text-decoration</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--primary-color</span><span class="p">);</span>
<span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1.2rem</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">1rem</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">1.5</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">header</span> <span class="p">{</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">1rem</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">border-bottom</span><span class="p">:</span> <span class="m">0.5px</span> <span class="nb">solid</span> <span class="n">var</span><span class="p">(</span><span class="n">--primary-color</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.logo</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--font-color</span><span class="p">);</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">2rem</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="m">800</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toggle-button-container</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toggle-button-container</span> <span class="nt">em</span> <span class="p">{</span>
<span class="nl">margin-left</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toggle-button-label</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">34px</span><span class="p">;</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">60px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toggle-button-label</span> <span class="nc">.toggle-button</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toggle-rounded</span> <span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="m">#ccc</span><span class="p">;</span>
<span class="nl">bottom</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
<span class="nl">right</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">transition</span><span class="p">:</span> <span class="m">0.4s</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toggle-rounded</span><span class="nd">:before</span> <span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="m">#fff</span><span class="p">;</span>
<span class="nl">bottom</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
<span class="nl">content</span><span class="p">:</span> <span class="s2">''</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">26px</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
<span class="nl">transition</span><span class="p">:</span> <span class="m">0.4s</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">26px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">input</span><span class="nd">:checked</span><span class="o">+</span><span class="nc">.toggle-rounded</span> <span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="m">#9cafeb</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">input</span><span class="nd">:checked</span><span class="o">+</span><span class="nc">.toggle-rounded</span><span class="nd">:before</span> <span class="p">{</span>
<span class="nl">transform</span><span class="p">:</span> <span class="n">translateX</span><span class="p">(</span><span class="m">26px</span><span class="p">);</span>
<span class="p">}</span>
<span class="nt">article</span> <span class="p">{</span>
<span class="nl">margin-top</span><span class="p">:</span> <span class="m">2rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.title</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">3rem</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--font-color</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.info</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">1rem</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.tags</span> <span class="p">{</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">1rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.tags</span> <span class="nt">span</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--primary-color</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--white-color</span><span class="p">);</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0.2rem</span> <span class="m">0.5rem</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="m">0.2rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="o"></</span><span class="nt">style</span><span class="o">></span>
</code></pre>
<p></p>
<p>This snippet can be divided into two main sections: the layout section and the custom properties section. The latter is what you should focus on. As you can see, the variables are applied above in the link, paragraph, heading, and article elements. </p>
<p>The idea behind this approach is that, by default, the website uses a light theme, and when the box is checked, the values for the light theme get inverted to a dark variant. </p>
<p>Since you can’t trigger these sitewide changes via CSS, JavaScript is critical here. In the next section, we’ll hook up the JavaScript code necessary to toggle between the light and dark themes. </p>
<p>Alternatively, you could trigger a change automatically via CSS using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">the <code>prefers-color-scheme</code> media query</a> to detect whether the user requested a light or dark theme. In other words, you can directly update the website to use the dark variants of the light theme. </p>
<p>Add the following snippet to all the CSS code you just wrote:<br>
</p>
<pre class="highlight css"><code><span class="k">@media</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span>
<span class="nd">:root</span> <span class="p">{</span>
<span class="py">--primary-color</span><span class="p">:</span> <span class="m">#325b97</span><span class="p">;</span>
<span class="py">--secondary-color</span><span class="p">:</span> <span class="m">#9cafeb</span><span class="p">;</span>
<span class="py">--font-color</span><span class="p">:</span> <span class="m">#e1e1ff</span><span class="p">;</span>
<span class="py">--bg-color</span><span class="p">:</span> <span class="m">#000013</span><span class="p">;</span>
<span class="py">--heading-color</span><span class="p">:</span> <span class="m">#818cab</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p></p>
<p>We’re listening to the user’s device settings and adjusting the theme to dark if they’re already using a dark theme. Finally, add the following script segment to the above HTML document:<br>
</p>
<pre class="highlight css"><code><span class="o"><</span><span class="nt">script</span><span class="o">></span>
<span class="nt">const</span> <span class="nt">toggleButton</span> <span class="o">=</span> <span class="nt">document</span><span class="nc">.querySelector</span><span class="o">(</span><span class="s2">'.toggle-button'</span><span class="o">);</span>
<span class="nt">toggleButton</span><span class="nc">.addEventListener</span><span class="o">(</span><span class="s2">'change'</span><span class="o">,</span> <span class="nt">toggleTheme</span><span class="o">,</span> <span class="nt">false</span><span class="o">);</span>
<span class="nt">const</span> <span class="nt">theme</span> <span class="o">=</span> <span class="p">{</span>
<span class="py">dark</span><span class="p">:</span> <span class="err">{</span>
<span class="s2">'--primary-color'</span><span class="p">:</span> <span class="s2">'#325b97'</span><span class="p">,</span>
<span class="s2">'--secondary-color'</span><span class="p">:</span> <span class="s2">'#9cafeb'</span><span class="p">,</span>
<span class="s2">'--font-color'</span><span class="p">:</span> <span class="s2">'#e1e1ff'</span><span class="p">,</span>
<span class="s2">'--bg-color'</span><span class="p">:</span> <span class="s2">'#000013'</span><span class="p">,</span>
<span class="s2">'--heading-color'</span><span class="p">:</span> <span class="s2">'#818cab'</span>
<span class="p">}</span><span class="o">,</span>
<span class="nt">light</span><span class="o">:</span> <span class="p">{</span>
<span class="err">'--primary-color':</span> <span class="err">'#0d0b52',</span>
<span class="err">'--secondary-color':</span> <span class="err">'#3458b9',</span>
<span class="err">'--font-color':</span> <span class="err">'#424242',</span>
<span class="err">'--bg-color':</span> <span class="err">'#ffffff',</span>
<span class="err">'--heading-color':</span> <span class="err">'#292922'</span>
<span class="p">}</span>
<span class="err">}</span><span class="o">;</span>
<span class="nt">function</span> <span class="nt">toggleTheme</span><span class="o">(</span><span class="nt">e</span><span class="o">)</span> <span class="p">{</span>
<span class="err">if</span> <span class="err">(e.target.checked)</span> <span class="err">{</span>
<span class="err">useTheme('dark');</span>
<span class="err">localStorage.setItem('theme',</span> <span class="err">'dark');</span>
<span class="p">}</span> <span class="nt">else</span> <span class="p">{</span>
<span class="err">useTheme('light');</span>
<span class="err">localStorage.setItem('theme',</span> <span class="err">'light');</span>
<span class="p">}</span>
<span class="err">}</span>
<span class="nt">function</span> <span class="nt">useTheme</span><span class="o">(</span><span class="nt">themeChoice</span><span class="o">)</span> <span class="p">{</span>
<span class="err">document.documentElement.style.setProperty(</span>
<span class="err">'--primary-color',</span>
<span class="err">theme\[themeChoice\]['--primary-color']</span>
<span class="err">);</span>
<span class="err">document.documentElement.style.setProperty(</span>
<span class="err">'--secondary-color',</span>
<span class="err">theme\[themeChoice\]['--secondary-color']</span>
<span class="err">);</span>
<span class="err">document.documentElement.style.setProperty(</span>
<span class="err">'--font-color',</span>
<span class="err">theme\[themeChoice\]['--font-color']</span>
<span class="err">);</span>
<span class="err">document.documentElement.style.setProperty(</span>
<span class="err">'--bg-color',</span>
<span class="err">theme\[themeChoice\]['--bg-color']</span>
<span class="err">);</span>
<span class="err">document.documentElement.style.setProperty(</span>
<span class="err">'--heading-color',</span>
<span class="err">theme\[themeChoice\]['--heading-color']</span>
<span class="err">);</span>
<span class="p">}</span>
<span class="nt">const</span> <span class="nt">preferredTheme</span> <span class="o">=</span> <span class="nt">localStorage</span><span class="nc">.getItem</span><span class="o">(</span><span class="s2">'theme'</span><span class="o">);</span>
<span class="nt">if</span> <span class="o">(</span><span class="nt">preferredTheme</span> <span class="o">===</span> <span class="s2">'dark'</span><span class="o">)</span> <span class="p">{</span>
<span class="err">useTheme('dark');</span>
<span class="err">toggleButton.checked</span> <span class="err">=</span> <span class="err">true;</span>
<span class="p">}</span> <span class="nt">else</span> <span class="p">{</span>
<span class="err">useTheme('light');</span>
<span class="err">toggleButton.checked</span> <span class="err">=</span> <span class="err">false;</span>
<span class="p">}</span>
<span class="o"></</span><span class="nt">script</span><span class="o">></span>
</code></pre>
<p></p>
<p>Now let’s break down the current state of the website. </p>
<p>A user visits the page. The media query <code>prefers-color-scheme</code> determines whether the user is using a light or dark theme. If it’s a dark theme, the website updates to use the dark variants of the custom properties. </p>
<p>Let’s say a user isn’t using a dark theme or their OS doesn’t support a dark theme. The browser would default to the light theme, allowing the user to control that behavior by checking or unchecking the box. </p>
<p>Depending on whether the box is checked or unchecked, the <code>useTheme()</code> function is called to pass in the theme variant and save the user’s current selection to local storage. You’ll see why it’s saved in a minute. </p>
<p>The <code>useTheme()</code> function is where all the magic happens. Based on the theme variant passed, a lookup is performed on the <code>theme</code> constant and used to switch between light and dark modes. </p>
<p>The last piece of the puzzle is persisting the current theme, which is achieved by reading the last preferred theme from local storage and setting it automatically when the user revisits the website. </p>
<p>Here’s what your second project should look like: <img src="https://blog.logrocket.com/wp-content/uploads/2020/02/img5-CSS-light-dark-theme-switcher-project.gif" alt="Demo Css Project Shown Toggling Between Light And Dark Mode"> You may be thinking of a million other ways to achieve this. Feel free to go through the code and make as many changes as you see fit. You can access the complete source code and see a live preview of this project from <a href="https://codepen.io/shalithasuranga/pen/vYPaYWN">this CodePen</a>.</p>
<h2>
<a name="project-3-handling-responsive-design-features" href="#project-3-handling-responsive-design-features">
</a>
Project 3: Handling responsive design features
</h2>
<p>In our third project, we’ll build a responsive login form that loads some adjustment values from CSS variables. Like the media query feature dynamically switches standard CSS properties, it also switches custom properties, so we can assign different values for variables within different responsive breakpoints. </p>
<p>First, add the following content into a new HTML document:<br>
</p>
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span> <span class="nt">/></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span> <span class="nt">/></span>
<span class="nt"><meta</span> <span class="na">http-equiv=</span><span class="s">"X-UA-Compatible"</span> <span class="na">content=</span><span class="s">"ie=edge"</span> <span class="nt">/></span>
<span class="nt"><title></span>Responsive design with CSS variables<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-box"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">value=</span><span class="s">"Username"</span> <span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"password"</span> <span class="na">value=</span><span class="s">"Password"</span> <span class="nt">/></span>
<span class="nt"><button></span>Login<span class="nt"></button></span>
<span class="nt"></div></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<p></p>
<p>Here we created a simple login form that consists of two input elements and a button. Add the following style tag into this HTML document to style it properly:<br>
</p>
<pre class="highlight css"><code><span class="o"><</span><span class="nt">style</span><span class="o">></span>
<span class="c">/* --- desktops and common --- */</span>
<span class="nd">:root</span> <span class="p">{</span>
<span class="py">--form-box-padding</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
<span class="py">--form-box-flex-gap</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
<span class="py">--form-input-font-size</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span> <span class="p">{</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">justify-content</span><span class="p">:</span> <span class="n">flex-end</span><span class="p">;</span>
<span class="py">gap</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--form-box-flex-gap</span><span class="p">);</span>
<span class="nl">padding</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--form-box-padding</span><span class="p">);</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="m">#333</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="nt">input</span><span class="o">,</span>
<span class="nc">.form-box</span> <span class="nt">button</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--form-input-font-size</span><span class="p">);</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="nt">input</span> <span class="p">{</span>
<span class="nl">outline</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="nt">button</span> <span class="p">{</span>
<span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="m">#edae39</span><span class="p">;</span>
<span class="p">}</span>
<span class="o"></</span><span class="nt">style</span><span class="o">></span>
</code></pre>
<p></p>
<p>The above CSS snippet styles the login form only for desktop devices, so it won’t adjust content responsively when you resize the browser, as shown in the following preview: <img src="https://blog.logrocket.com/wp-content/uploads/2020/02/img6-Non-responsive-webpage-desktop-only-styles.gif" alt="Example Of Webpage Without Responsive Elements Displaying Desktop Only Styles Even As Viewport Shrinks"> We can simply make this page responsive by writing some styling adjustments — i.e., changing <code>flex-direction</code> — inside media query breakpoints. For <code>padding</code> or <code>font-size</code>-like values-based properties, we can use CSS variables instead of writing CSS properties repetitively to improve the readability and maintainability of CSS definitions. </p>
<p>Look at the previous CSS snippet: you will notice three CSS variables. Change those variables with media query blocks and complete the responsive screen handling code using the following code snippet:<br>
</p>
<pre class="highlight css"><code><span class="c">/* --- tablets --- */</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">601px</span><span class="p">)</span> <span class="n">and</span> <span class="p">(</span><span class="n">max-width</span><span class="p">:</span> <span class="m">900px</span><span class="p">)</span> <span class="p">{</span>
<span class="nd">:root</span> <span class="p">{</span>
<span class="py">--form-box-padding</span><span class="p">:</span> <span class="m">20px</span> <span class="m">12px</span> <span class="m">20px</span> <span class="m">12px</span><span class="p">;</span>
<span class="py">--form-box-flex-gap</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span>
<span class="py">--form-input-font-size</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="nt">input</span><span class="o">,</span>
<span class="nc">.form-box</span> <span class="nt">button</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="nb">block</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">/* --- mobiles --- */</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">max-width</span><span class="p">:</span> <span class="m">600px</span><span class="p">)</span> <span class="p">{</span>
<span class="nd">:root</span> <span class="p">{</span>
<span class="py">--form-box-padding</span><span class="p">:</span> <span class="m">24px</span><span class="p">;</span>
<span class="py">--form-box-flex-gap</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span>
<span class="py">--form-input-font-size</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="p">{</span>
<span class="nl">flex-direction</span><span class="p">:</span> <span class="n">column</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.form-box</span> <span class="nt">input</span><span class="o">,</span>
<span class="nc">.form-box</span> <span class="nt">button</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="nb">block</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p></p>
<p>The above code snippet adjusts the layout for mobile and tablet screens with some standard CSS properties and custom properties. For example, it uses a different <code>flex-direction</code> mode, <code>display</code> mode for several elements, and the following custom property values for mobile screens:<br>
</p>
<pre class="highlight css"><code><span class="nt">--form-box-padding</span><span class="o">:</span> <span class="err">24</span><span class="nt">px</span><span class="o">;</span>
<span class="nt">--form-box-flex-gap</span><span class="o">:</span> <span class="err">12</span><span class="nt">px</span><span class="o">;</span>
<span class="nt">--form-input-font-size</span><span class="o">:</span> <span class="err">20</span><span class="nt">px</span><span class="o">;</span>
</code></pre>
<p></p>
<p>Test this project by resizing the browser window: <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pa2pgh7ea70a82oc21j.gif" alt="Example Of Project With Responsive Webpage Styles Adjusting As Viewport Shrinks"> Try adjusting these CSS variables and creating new ones to improve this login screen further. You can use the same strategy to use CSS variables with <a href="https://blog.logrocket.com/css-container-queries-guide/">container queries</a>. Check the complete source code and see a live preview from <a href="https://codepen.io/shalithasuranga/pen/QWoBaWr">this CodePen</a>.</p>
<h2>
<a name="project-4-generating-javascriptfree-dynamic-elements" href="#project-4-generating-javascriptfree-dynamic-elements">
</a>
Project 4: Generating JavaScript-free dynamic elements
</h2>
<p>Imagine that you need to create a colorful native checkbox list with multiple accent colors. Using different values for <code>accent-color</code> via the inline style attribute is undoubtedly time-consuming since you have to define colors yourself. Hence, you may create this checkbox list dynamically with JavaScript. </p>
<p>However, what if this list gets rendered in a JavaScript-disabled environment, like inside a Markdown document? We can use CSS variables to generate JavaScript-free dynamic elements. </p>
<p>Let’s create a colorful native checkbox list with CSS variables. Create a new HTML document and add the following style tag:<br>
</p>
<pre class="highlight html"><code>input[type="checkbox"] {
width: 80px;
height: 80px;
--hue: calc(var(--i) * 50 + 100);
accent-color: hsl(var(--hue), 50%, 50%);
}
</code></pre>
<p></p>
<p>Here, we calculate a dynamic color for the <code>accent-color</code> property using the <code>hsl</code> color function. </p>
<p>For the hue input parameter, we use the <code>--hue</code> variable which gets a dynamically calculated value using the <code>--i</code> variable. This implementation lets us generate multiple colors by using different numbers for <code>--i</code>. </p>
<p>Use the following HTML snippet to get multiple colorful native checkboxes:<br>
</p>
<pre class="highlight html"><code><span class="nt"><div</span> <span class="na">style=</span><span class="s">"text-align: center"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">checked</span> <span class="na">style=</span><span class="s">"--i: 0"</span><span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">checked</span> <span class="na">style=</span><span class="s">"--i: 1"</span><span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">checked</span> <span class="na">style=</span><span class="s">"--i: 2"</span><span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">checked</span> <span class="na">style=</span><span class="s">"--i: 3"</span><span class="nt">/></span>
<span class="nt"></div></span>
</code></pre>
<p></p>
<p>Here we set an index manually for the <code>--i</code> variable via inline style attributes to generate a dynamic accent color. This approach is more productive than setting colors yourself for each checkbox element. Look at the following preview of the fourth project: <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c8x0ri4cn86xpvrud8f9.png" alt="Colorful Checkbox Styles Using Css Variables"> You can browse the complete source code and see a live preview from <a href="https://codepen.io/shalithasuranga/pen/YzgjegB">this CodePen</a>. It’s possible to use the same strategy to generate JavaScript-free dynamic elements by adjusting any standard CSS property value, i.e., using <code>--i</code> to set dynamic image filter configurations.</p>
<h2>
<a name="css-variables-vs-sass-variables" href="#css-variables-vs-sass-variables">
</a>
CSS variables vs. SASS variables
</h2>
<p>The following table will help you know when to use CSS variables and preprocessor variables:</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>CSS variables</th>
<th>Preprocessor variables</th>
</tr>
</thead>
<tbody>
<tr>
<td>Scope</td>
<td>Can be dynamically modified at runtime</td>
<td>Compiles to static values before rendering</td>
</tr>
<tr>
<td>Use cases</td>
<td>Great for dynamic theming, user-controlled styles, or runtime updates</td>
<td>Great for working with a large-scale project that benefits from functions, mixins, and nested styles</td>
</tr>
<tr>
<td>Usage</td>
<td>Works directly in browsers</td>
<td>Requires a pre-processor like Less or SASS</td>
</tr>
<tr>
<td>Performance</td>
<td>Results in slightly higher runtime cost due to look up but mostly negligible in most cases</td>
<td>No runtime cost, but may impact load time due to larger stylesheets.</td>
</tr>
<tr>
<td>Runtime update</td>
<td>Can be modified easily with JavaScript</td>
<td>Impossible to update with JavaScript because it requires recompilation</td>
</tr>
</tbody>
</table>
<h2>
<a name="advanced-techniques-using-css-variables-in-animations" href="#advanced-techniques-using-css-variables-in-animations">
</a>
Advanced techniques: Using CSS variables in animations
</h2>
<p>CSS variables can be used with <code>@keyframes</code> to make animations more dynamic and reusable without direct changes to the styles. However, they must be applied outside the<code>@keyframes</code> since variables are not recognized in keyframes.</p>
<h3>
<a name="animating-button-color-dynamically" href="#animating-button-color-dynamically">
</a>
Animating button color dynamically
</h3>
<p></p>
<pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
<span class="py">--btn-bg</span><span class="p">:</span> <span class="m">#3498db</span><span class="p">;</span> <span class="c">/* Default background color */</span>
<span class="p">}</span>
<span class="nt">button</span> <span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--btn-bg</span><span class="p">);</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">12px</span> <span class="m">24px</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
<span class="nl">animation</span><span class="p">:</span> <span class="n">pulse</span> <span class="m">1.5s</span> <span class="n">infinite</span> <span class="n">alternate</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">@keyframes</span> <span class="n">pulse</span> <span class="p">{</span>
<span class="nt">from</span> <span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--btn-bg</span><span class="p">);</span>
<span class="p">}</span>
<span class="nt">to</span> <span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="n">lighten</span><span class="p">(</span><span class="n">var</span><span class="p">(</span><span class="n">--btn-bg</span><span class="p">),</span> <span class="m">20%</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p></p>
<p>The background color changes dynamically based on <code>--btn-bg</code>. Adjusting <code>--btn-bg</code> in <code>:root</code> instantly updates the animation color! </p>
<p>Now, we can use JavaScript to update the CSS variable in real-time to animate the button’s color on hover or user interaction<br>
</p>
<pre class="highlight javascript"><code><span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">button</span><span class="dl">"</span><span class="p">).</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseover</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nf">setProperty</span><span class="p">(</span><span class="dl">"</span><span class="s2">--btn-bg</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">#e74c3c</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
<span class="nb">document</span><span class="p">.</span><span class="nf">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">button</span><span class="dl">"</span><span class="p">).</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseout</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nf">setProperty</span><span class="p">(</span><span class="dl">"</span><span class="s2">--btn-bg</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">#3498db</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre>
<p></p>
<p>The button smoothly transitions between colors when hovered!</p>
<h2>
<a name="browser-support-for-the-css-variables-feature" href="#browser-support-for-the-css-variables-feature">
</a>
Browser support for the CSS variables feature
</h2>
<p>According to the browser compatibility table of the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*#browser_compatibility">official MDN documentation</a>, the CSS variables feature is widely available in all popular browser versions released after April 2017. More specifically, browsers released this feature with the following versions:</p>
<ul>
<li> Google Chrome 49 (Released 2 March 2016)</li>
<li> Microsoft Edge 15 (Released 5 April 2017)</li>
<li> Mozilla Firefox 31 (Released 22 July 2014)</li>
<li> Apple Safari 9.1 (Released 21 March 2016)</li>
</ul>
<p>According to these statistics, using custom properties in production apps is possible since most users nowadays use up-to-date web browsers. However, it would be prudent to analyze your audience’s browser versions before using any new native CSS feature.</p>
<h2>
<a name="common-mistakes-amp-troubleshooting-css-variables" href="#common-mistakes-amp-troubleshooting-css-variables">
</a>
Common mistakes & troubleshooting CSS variables
</h2>
<p>Here are some common mistakes with CSS variables and how to fix them:</p>
<h3>
<a name="missing-fallbacks-for-older-browsers" href="#missing-fallbacks-for-older-browsers">
</a>
Missing fallbacks for older browsers
</h3>
<p>Some older browsers, like IE11, do not support CSS variables, which can cause styling issues if a fallback is not provided. A common mistake is using <code>var(--color-primary)</code> without specifying an alternative. To prevent this, always include a fallback value inside <code>var()</code>, such as <code>var(--color-primary, #3498db)</code>, ensuring that a default color is applied if the variable is unavailable.</p>
<h3>
<a name="variables-with-media-queries" href="#variables-with-media-queries">
</a>
Variables with media queries**
</h3>
<p>CSS variables cannot be used directly in media queries, as they are not evaluated in the same way as standard values. For example, defining a variable like <code>--breakpoint-mobile: 600px</code> in <code>:root</code> and attempting to use it inside <code>@media (max-width: var(--breakpoint-mobile))</code> will not work. Instead, media queries require fixed values, so it's best to use predefined breakpoints directly, such as <code>@media (max-width: 600px)</code>, to ensure proper functionality.</p>
<h3>
<a name="variables-dont-work-in-certain-properties-eg-raw-display-endraw-raw-zindex-endraw-" href="#variables-dont-work-in-certain-properties-eg-raw-display-endraw-raw-zindex-endraw-">
</a>
Variables** <strong>d</strong>on't <strong>w</strong>ork in <strong>c</strong>ertain <strong>p</strong>roperties (e.g.,<code>display</code><strong>,</strong> <code>z-index</code><strong>)</strong>
</h3>
<p>CSS variables don’t work in all properties, especially those that require integer values like <code>z-index</code> or <code>display</code>.<br>
</p>
<pre class="highlight css"><code><span class="nd">:root</span> <span class="p">{</span>
<span class="py">--display-mode</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.container</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--display-mode</span><span class="p">);</span> <span class="c">/* Won't work */</span>
<span class="p">}</span>
</code></pre>
<p></p>
<h2>
<a name="conclusion" href="#conclusion">
</a>
Conclusion
</h2>
<p>By building these simple projects, you can learn how to use CSS variables like a pro. You can use the <code>style</code> attribute to apply CSS variables directly to an HTML element like this <code><p style="color: var(--primary-color);">Hello, world!</p></code>. Also, you can debug CSS variable issues using the browser DevTools. There’s certainly more to them than I explained, so feel free to mess around with the code to explore further. </p>
<p>CSS variables help simplify the way you build websites and complex animations while still allowing you to write reusable and elegant code. Using CSS variables is also possible with <a href="https://blog.logrocket.com/using-css-variables-react-native/">React Native projects</a> that run on the React Native Web renderer.</p>
<h2>
<a name="frequently-asked-questions" href="#frequently-asked-questions">
</a>
Frequently asked questions
</h2>
<h3>
<a name="how-do-i-use-variables-in-css" href="#how-do-i-use-variables-in-css">
</a>
How do I use variables in CSS?**
</h3>
<p>You can use variables in your CSS using the <code>var()</code> function to apply declared variables in your styles.</p>
<h3>
<a name="when-should-i-use-css-variables" href="#when-should-i-use-css-variables">
</a>
When should I use CSS variables?**
</h3>
<span>Use CSS variables when you need global or reusable values like colors, font sizes, spacings, or themes.</span> <span>They are also useful when you need to avoid hard-coded repetition. In large projects, changing a value like a primary color in multiple places can be tedious. Using CSS variables makes the codebase maintainable.</span>
<h3>
<a name="how-do-you-initialize-a-variable-in-css" href="#how-do-you-initialize-a-variable-in-css">
</a>
How do you initialize a variable in CSS?
</h3>
<p>CSS variables can be declared using either the <code>--</code> prefix or the <code>@property</code> at-rule.</p>
<hr>
<h2>
<a name="is-your-frontend-hogging-your-users-cpu" href="#is-your-frontend-hogging-your-users-cpu">
</a>
Is your frontend hogging your users' CPU?
</h2>
<p>As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, <a href="https://lp.logrocket.com/blg/css-signup?utm_source=devto&utm_medium=organic&utm_campaign=25Q1&utm_content=how-to-use-css-variables">try LogRocket</a>.</p>
<p><a href="https://lp.logrocket.com/blg/css-signup?utm_source=devto&utm_medium=organic&utm_campaign=25Q1&utm_content=how-to-use-css-variables"><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j90obgd2v7r7pue3vr0n.png" alt="LogRocket CSS Demo"></a></p>
<p><a href="https://lp.logrocket.com/blg/css-signup?utm_source=devto&utm_medium=organic&utm_campaign=25Q1&utm_content=how-to-use-css-variables">LogRocket</a> is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.</p>
<p>Modernize how you debug web and mobile apps — <a href="https://lp.logrocket.com/blg/css-signup?utm_source=devto&utm_medium=organic&utm_campaign=25Q1&utm_content=how-to-use-css-variables">start monitoring for free</a>.</p>
Top comments (0)