􀀂􀀟􀀍􀀅 􀀂􀀝􀀌􀀃 List CSS variables in JavaScript

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);
  /* ... */
}

I wanted to see a list of all the root level variables in one place, no matter where they are defined in the source. 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.

Screenshot of the page at /controls/cssvars

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().

<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>

Responses

Comments are hosted on this site and powered by Remark42 (thanks!).

Webmentions are hosted on remote sites and syndicated via Webmention.io (thanks!).