SCSS を lit-html でも使う

毎日変化しているフロントエンドライブラリ向けの共通ライブラリを Web Components で書くことを夢見て、 lit-html を便利に使うべく検証しています。

今回は、 SCSS を lit-html と共に使ってみます。
おそらく PostCSS Stylus LESS など、 Webpack の loader がある言語であればなんでも動かせると思います。

サンプルプロジェクトはこちら

背景

考えられる想定として、あるプロジェクト内で使われる Web Components 内には、プロジェクトで共通のスタイル用( reset, normalize、font-family, icon etc…)とページ用、コンポーネント用の3つの種類のスタイル定義があります。
Web Component では <style> タグを HTML 文字列や DOM に含めるのが良いとされているパターンですが、全部で共通のスタイルを別々に書き込むのは効率が良いとも思いませんでした。
(サンプルプロジェクトを Chrome で確認すると、 common.css をリクエストしているのは 1 度だけでした。)

サンプルの構成

サンプルプロジェクトでは、以下の SCSS ファイルが存在します。

'src/variables.scss'
せっかく SCSS を使っているので、変数の定義ファイルとしての使用例として使っています。実際にもこういう使用例は多いと思いますので。
'src/common.scss'
名前の通り、プロジェクト共通の(ベース)スタイルです。
'src/page/index.scss'
こちらは HTML ページ全体に適応されるスタイルです。
'src/components/scss-button.scss'
ある Web Components 用のスタイルです。

ページに対しては、 ‘common.scss’ と ‘index.scss’ を、 <scss-button> コンポーネントに対しては ‘common.scss’ と ‘scss-button.scss’ を使うこととします。(どちらも ‘variables.scss’ は SCSS コンパイル時に参照される)

Webpack の設定

Webpack の設定を見ていきます。
サンプルでは、 ‘common.scss’ は <link> タグによる外部ファイル読み込み、コンポーネント用である ‘scss-button.scss’ は内部に埋め込むよう設定しています。

まず、 entry としてページ用 SCSS ファイルと <scss-button> コンポーネント用の JS ファイルを指定します。
続いて、 SCSS のコンパイル処理用のラインを、共通用 SCSS ファイルと コンポーネント用 SCSS ファイルのそれぞれに用意します。webpack.config.dev.js の一部では、それぞれ 3 ルート分の設定を行っています。
共通用 SCSS ファイルは ‘src/*.scss’ 、ページ用 SCSS ファイルは ‘src/page/*.scss’ にあると仮定して、 sass-loader -> file-loader というラインを、コンポーネント用 SCSS ファイルは ‘src/components/*.scss’ あると仮定して、 sass-loader -> raw-loader という設定にしました。
コンポーネント用では sass-loader -> css-loader -> to-string-loader という構成にすれば、 minify も可能です。
sass - Difference: Webpack css-loader and raw-loader - Stack Overflow より、 sass-loader の直後に raw-loader を使うのは問題ないようです。)

コンポーネント用 JS ファイルの中では、 scss ファイルを import して、以下のようにスタイル定義用に加えます。

scss-button.js
3
4
5
6
7
8
9
10
import commonStyleHref from '../common.scss';
import style from './scss-button.scss'

class ScssButton extends HTMLElement {
static template() {
return html`<link rel="stylesheet" type="text/css" href="${commonStyleHref}" /><style>${style}</style>
<a href="http://example.com/"><slot></slot></a>`;
}

同じ SCSS ファイルインポートでもパイプラインが異なるので、気持ち悪いですがこう指定することになります。(それぞれ内容をタグに入れてしまうような loader を書くのが一番楽かもしれない。)