CSS Variables を使うと CSS で変数のようなカスタムプロパティを使うことができる仕様で Chrome 49 から使えるようになっています。

:root {
  --foreground: black;
  --background: white;
}

:root * {
  color: var(--foreground);
  background-color: var(--background);
}

div {
  --foreground: red;
  --background: green;
}

このように書くとページ全体では白地に黒文字、 <div> のなかでは緑地に赤文字になります。

--foreground--background が CSS Variables なのですが、これらの値を JavaScript からも触ることができます。

取得

DOM の style プロパティからは style="..." で指定した値を取得できます。ただし普通のスタイルのように style.color のようにプロパティからは取得できず、 getPropertyValue() メソッドを使います。

<div id="a" style="--a: xyz"></div>
<script>
  console.log(document.querySelector('#a').style.getPropertyValue('--a')); // "xyz"
</script>

実際に適用されている値はさらに getComputedStyle() メソッドと組み合わせると取得できます。

<style>
  #b { --x: blue !important; }
</style>
<div id="b" style="--x: red"></div>
<script>
  console.log(document.defaultView.getComputedStyle(document.querySelector('#b')).style.getPropertyValue('--a')); // "blue"
</script>

値の設定

値の取得が getPropertyValue なら設定は setPropertyValue だと考えるのが自然ですが、実際は
setProperty です。

<div id="c" style="--x: 1"></div>
<script>
  document.querySelector('#c').style.setProperty('--x', 2); // 書き換え
</script>

応用

JavaScript から CSS に値を渡せるのが確認できたので、それを活用した例を考えてみます。

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    :root {
        width: 100%;
        height: 100%;
        --scroll: 0;
    }

    #a {
        background: gray;
        height: 3000px;
        width: calc(100% * var(--scroll));
    }
</style>
<div id="a">a</div>
<script>
    window.addEventListener('scroll', () => {
        const root = document.documentElement;
        const body = document.body;
        root.style.setProperty('--scroll',
            body.scrollTop / (body.scrollHeight - root.offsetHeight));
    }, false);
</script>

scroll イベントでスクロール率を計算して CSS に渡しています。その値を使って div 要素の width を計算しています。実際に表示するとスクロールに連動して幅がうねうね動くのがわかります。

div 要素の width を直接代入するのと違い、はじめに CSS を書けば scroll イベントのリスナでいろいろ計算する必要がないのがメリットです。 JavaScript よりも CSS の calc() のなかだと calc(100px + 1em) のような計算が簡単にできるので、細かい調整が楽に済みます。

CSS Variables がすべてのブラウザで使える状況ではないので Web サイトで使うのは難しいかもしれませんが、もし使えるなら覚えておいて活用したい機能だと感じました。