CSS Modules と CSS の Custom Properties の組み合わせで色々解決できそうな気がしてる

3 min read読了の目安(約3000字

はじめに

Webフロント開発で CSS の取り扱いに頭を悩ませている人は多いと思うので、最近良さそうだなと思っているアイデアを投稿したいと思います。

現状の問題と予想

現在開発で SCSS と CSS Modules の組み合わせを利用しているのですが、読み込み順序が不定の問題などでプロパティの上書きが発生するパターンで事故ることがあり(デザイナーさんのほうで上書きが発生しないように管理するのが難しいらしい)、将来的にどうしていこうか考えていく中で、IE11のサポートさえしなくてよい状況になれば、 CSS の Custom Properties との組み合わせで色々スマートに解決できるのではないかという予想にたどり着きました。

サンプル

すごく雑なサンプルですが

https://codesandbox.io/s/still-grass-rtcdy?file=/index.html:830-1109

CSS でこう定義しておいて

      .section-theme1 {
        --section-color: red;
        --section-heading-font-size: 14px;
        --section-message-text-align: center;
      }
      .section-theme2 {
        --section-color: blue;
        --section-heading-font-size: 20px;
        --section-message-text-align: right;
      }

      .section {
        color: var(--section-color);
      }
      .heading {
        font-size: var(--section-heading-font-size);
      }
      .message {
        text-align: var(--section-message-text-align);
      }

HTML はこんな感じにすると、いい感じにスコープ単位でスタイルセットを切り替えることができます。

    <section class="section-theme1 section">
      <h1 class="heading">section A</h1>
      <p class="message">message A</p>
    </section>
    <section class="section-theme2 section">
      <h1 class="heading">section B</h1>
      <p class="message">message B</p>
    </section>

表示結果

スコープ単位であれば、読み込み順序の問題なども影響しないので安心ですし、何よりスタイル違いにするとしても、構造化された内部のクラス名が短い固定の名前で良いのは魅力的。

応用

上の例はただの CSS と HTML の組み合わせですが、 CSS Modules と組み合わせると、好きなテーマを適用した上で、ハッシュ付きのスコープで任意の変数を上書きするなど、変えたいところだけ変えるみたいなことがわりと簡単且つ安全に行えそう。 (適宜ハッシュ付けたくないところに :global() とか必要かもしれない)

// この .section はハッシュ付けるやつ
.section {
  composes: section-theme1 from "section-theme.scss";
  --section-color: lime;
}

発想としては、全ての要素に短い固定のクラス名が付いていて、スタイルセットを適用する要素にだけハッシュ付きのクラス名を適用しつつ、 composes を使って持ってきたスタイルセットのクラス名でベーススコープを作り、ハッシュ付きのクラス名で作られたスコープを上書き可能なスコープとして利用するイメージ。

また、さらに応用すればスタイルを指定するプロパティのセット自体も提供して任意の場所で composes するような形にできそうな気がしています。

      // このファイルはハッシュ付かなくていい
      .section-template1 {
        &.section-theme1 {
          --section-background-color: red;
          --section-heading-font-weight: bold;
          --section-message-padding: 4px;
        }
        &.section-theme2 {
          --section-background-color: blue;
          --section-heading-font-weight: bold;
          --section-message-padding: 4px;
        }
        &.section {
          background-color: var(--section-background-color);
        }
        .heading {
          font-weight: var(--section-heading-font-weight);
        }
        .message {
          padding: var(--section-message-padding);
        }
     }

使うときにはこれだけで良いはず。

// この .section はハッシュ付けるやつ
.section {
  // セクションに対して、どのテンプレートの、どのテーマを使うか
  composes: section-template1 section-theme1 from "section-theme.scss";
}

終わり

あまり実験はできてないのですが、所感としては行けそうな気がしているので、何か問題がありそうだったりしたら教えていただけると助かります。

誰か同じようなこと考えてる人がいそうな気がしなくもない。