I wanted to see a list of all the root level variables in one place, no matter where they are defined in the source. You can see it in action in this secret control panel.
Rationale
For all colors in this site’s CSS, I use a variable, like this:
:root {
--body-bg-color: white;
/* ... */
}
body {
background-color: var(--body-bg-color);
/* ... */
}
How can I see them all at once? I was especially interested to show all the colors in use by the current theme (dark/light mode), because my dark mode implementation works by overriding some but not all colors from light mode, so the colors for dark mode are not all listed next to each other in my repo.
To do this, I wrote some JavaScript to find all the CSS variables defined on :root
,
group the variables that have the same value,
show a color swatch if the value is a valid CSS color,
and list them in a table.
I just drop the <script>
element right in the body of my page,
after the table.
This way the table is guaranteed to be available before the JavaScript starts to execute.
In my particular case, the CSS variables are defined in source files
that get inlined directly in the <head>
element of my HTML.
(View source and look for <style id="inlined-styles-root">
and <style id="inlined-dark-theme-styles" media="all and (prefers-color-scheme: dark)">
.)
If you want to use this in a page which loads CSS from separate files,
you may want to wait for the DOMContentLoaded
event before calling listRootCssVariables()
.
Implementation
<table id="cssVariableTable">
<thead>
<tr>
<th>Variable</th>
<th>Color</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<!-- Table rows will be inserted here -->
</tbody>
</table>
<script>
/* Check if a string is a valid CSS color
*/
function isValidColor(colorString) {
const s = new Option().style;
s.color = colorString;
return s.color !== '';
}
/* Enumerate CSS variables on :root and write them as rows in an HTML table.
*
* For each variable with a value of a CSS color, show a color swatch.
* Group variables that have the same value into a single row in the table.
*/
function listRootCssVariables(tableId) {
const rootStyles = getComputedStyle(document.documentElement);
const tableElement = document.getElementById(tableId);
const valueGroupedVariables = {};
// Collect all the variables
for (let i = 0; i < rootStyles.length; i++) {
const propName = rootStyles[i];
if (propName.startsWith('--')) {
const propValue = rootStyles.getPropertyValue(propName).trim();
if (!valueGroupedVariables[propValue]) {
valueGroupedVariables[propValue] = [];
}
valueGroupedVariables[propValue].push(propName);
}
}
// Create and append table rows and cells
for (const [value, names] of Object.entries(valueGroupedVariables)) {
const tableRow = document.createElement("tr");
const nameCell = document.createElement("td");
const nameCode = document.createElement("code");
nameCode.innerHTML = names.join('<br>');
nameCell.appendChild(nameCode);
// Check if the value is a valid color and add a color swatch if so
const colorCell = document.createElement("td");
if (isValidColor(value)) {
const colorSwatch = document.createElement("span");
colorSwatch.style.backgroundColor = value;
colorSwatch.style.display = "inline-block";
colorSwatch.style.width = "20px";
colorSwatch.style.height = "20px";
colorSwatch.style.marginLeft = "10px";
colorSwatch.style.border = "1px solid black";
colorCell.appendChild(colorSwatch);
}
const valueCell = document.createElement("td");
const valueCode = document.createElement("code");
valueCode.textContent = value;
valueCell.appendChild(valueCode);
// Write to the table
tableRow.appendChild(nameCell);
tableRow.appendChild(colorCell);
tableRow.appendChild(valueCell);
tableElement.appendChild(tableRow);
}
}
/*
*
* If your CSS may still be loading when this line is executed,
* consider listening for the DOMContentLoaded event instead, with:
*
* document.addEventListener("DOMContentLoaded", function() { listRootCssVariables("cssVariableTable"); });
*/
listRootCssVariables("cssVariableTable");
</script>