📑

Lightning CSSをViteに導入する

2022/12/05に公開

最近Lightning CSSというものを知ったので、ViteのプロジェクトにLightning CSSを導入する方法を紹介したいと思います。

Lightning CSSとは

Lightning CSSとは一体どういったものなのでしょうか。

https://lightningcss.dev/

An extremely fast CSS parser, transformer, bundler, and minifier.

簡単にいうと、CSSのParser / Transformer / Bundler / Minifierの役割を担うツールです。
Rust製のツールであり、そのビルド速度はESBuilをも凌ぐ速度となっています。

Lightning CSS lets you use modern CSS features and future syntax today.

と書かれているようにLightning CSSを導入することで、最新のCSSの機能と将来の構文を使うことが可能となります。

実際にどうような構文がサポートされているのかはGitHubに記載がありますので、そちらをご参照ください。現時点(2022.12.5)での一例を挙げるとCSS NestingCustom Media Queriesが使えるようになります。

とりあえず機能だけでも試してみたいという方は公式のPlaygroundが用意されているので、ぜひそちらで試してみてください。

ドキュメントを読む限りだと、もともとParcel向けに開発されている印象を受けますが、ParcelでなくてもJavaScriptやCLIなどから使うことができます。本記事ではViteの自作プラグインとしてプロジェクトに組み込む方法を紹介します。

Lightning CSSをViteに導入する

本記事ではViteの自作プラグインとして、Lightning CSSをViteプロジェクトに導入したいと思います。

プロジェクトの作成

まずはyarn create viteでViteプロジェクトの作成を行いましょう。
本記事ではyarnを使います。未検証ですがnpmでも問題はないと思います。

https://ja.vitejs.dev/guide/#最初の-vite-プロジェクトを生成する

プロジェクトの作成については基本的に上記の手順にしたがいます。

$ yarn create vite
//...略
✔ Project name: … vite-lightningcss
✔ Select a framework: › Vanilla
✔ Select a variant: › TypeScript

プロジェクト名はvite-lightningcss(ここは任意のプロジェクト名にしてください)、frameworkはVanilla、variantはTypeScriptを選択しました。

$ cd vite-lightningcss
$ yarn

プロジェクトルートに移動し、yarnコマンドでパッケージをインストールします。

$ yarn dev

その後yarn devコマンドでローカルサーバーが立ち上がれば、プロジェクトの作成は完了です。

不必要な記述の削除とファイルの整理

この状態では以下のようなフォルダ構成になっていると思います。

.
├── index.html
├── node_modules
│   └── ...略...
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── counter.ts
│   ├── main.ts
│   ├── style.css
│   ├── typescript.svg
│   └── vite-env.d.ts
├── tsconfig.json
└── yarn.lock

本記事において、不必要なフォルダ・ファイルの削除と記述の整理を行います。

publicディレクトリ、src以下にあるcounter.tstypescript.svgは必要ないので削除します。

$ rm -r public src/counter.ts src/typescript.svg

次にsrc/main.tssrc/style.cssを書き換え、最低限の記述だけを行います。

src/main.ts
import './style.css';
src/style.css
* {
  margin: 0;
  padding: 0;
}

最後にsrc/index.htmlも書き換えておきます。

src/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Lightning CSS</title>
  </head>
  <body>
    <h1 class="title">Vite + Lightning CSSのサンプル</h1>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

ここまでの作業で以下のようなフォルダ構成になりました。

.
├── index.html
├── node_modules
│   └── ...略...
├── package.json
├── src
│   ├── main.ts
│   ├── style.css
│   └── vite-env.d.ts
├── tsconfig.json
└── yarn.lock

プラグインを作成する

前準備がおわったところで、プラグインの作成を行なっていきましょう。

https://ja.vitejs.dev/guide/api-plugin.html

詳細は上記にあるので、適宜ご参照してください。

まず必要なモジュールを前もってインストールしておきます。

$ yarn add -D lightningcss browserslist @types/node

lightningcssだけでなく、browserslistもインストールしていますが、これはターゲットのブラウザ指定を簡易にするためです。lightningcss単体でもターゲットブラウザの指定は可能ですが、少し煩雑になるため、browserslistを使用します。

次にプロジェクトのルートにvite.config.tsを作成し、以下のように記述します。

$ touch vite.config.ts
src/vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [],
});

次にプラグインの処理を記述するplugins/vite-plugin-lightningcss.tsを作成します。

$ mkdir plugins
$ touch plugins/vite-plugin-lightningcss.ts

ViteプラグインAPIではrollup.jsと同様にいくつかのビルドフックが提供されています。
今回はLightning CSSを用いてCSSを変換するわけですが、このような処理ではtransformフックを使用できます。

https://rollupjs.org/guide/en/#transform

Lightning CSSの使い方については以下に記載があります。

https://github.com/parcel-bundler/lightningcss#from-node

上記を踏まえて、プラグインには次のように処理を記述します。

plugins/vite-plugin-lightningcss.ts
import browserslist from 'browserslist';
import {
  transform as CSSTransform,
  browserslistToTargets as CSSBrowserslistToTargets,
} from 'lightningcss';
import { type Plugin } from 'vite';

const cssRegex = /\.(css)$/;
// browserslistでtargetsを指定
const browserTargets = CSSBrowserslistToTargets(browserslist('>= 0.25%'));

const vitePluginLightningcss = (): Plugin => {
  return {
    // vite-plugin-*で名前をつける
    name: 'vite-plugin-lightningcss',
    // transformフックでCSSの変換処理
    transform(src, id) {
      // CSSファイルの処理
      if (cssRegex.test(id)) {
        // https://github.com/parcel-bundler/lightningcss/blob/master/node/index.d.ts
        // オプションについては上記参照
        const { code, map } = CSSTransform({
          filename: id,
          code: Buffer.from(src),
          targets: browserTargets,
          // デフォルトではnestingとcustomMediaがfalseなのでtrueにする
          drafts: {
            nesting: true,
            customMedia: true,
          },
        });

        return {
          code: code.toString(),
          map: map?.toString(),
        };
      }

      // CSSファイル以外は何も処理せず返す
      return {
        code: src,
      };
    },
  };
};

export default vitePluginLightningcss;

本記事ではCSS NestingCustom Media Queriestrueにしていますので、ご注意ください。
プラグインの記述は完了です。作成したプラグインをvite.config.tsから読み込みます。

src/vite.config.ts
import { defineConfig } from 'vite';
import vitePluginLightningcss from './plugins/vite-plugin-lightningcss';

export default defineConfig({
  // 作成したプラグインを読み込む
  plugins: [vitePluginLightningcss()],
});

スタイルの反映

ここまできたらyarn devでローカルサーバーをたちあげてみましょう。

<!-- ...略... -->
<body>
  <h1 class="title">Vite + Lightning CSSのサンプル</h1>
  <script type="module" src="/src/main.ts"></script>
</body>
<!-- ...略... -->

現状HTMLファイルには上記の記述があるので、「Vite + Lightning CSSのサンプル」の文字が表示されていれば問題ありません。

では実際に将来のCSS構文が使えるのか試してみます。

src/style.css
@custom-media --narrow-window (max-width: 600px);

* {
  margin: 0;
  padding: 0;
}
/* CSS Nesting Module */
/* https://www.w3.org/TR/css-nesting-1/ */
body {
  color: red;
  .title {
    color: green;
  }
}
/* Custom Media Queries */
/* https://www.w3.org/TR/mediaqueries-5/#custom-mq */
@media (--narrow-window) {
  body {
    .title {
      color: blue;
    }
  }
}

上記はCSS NestingCustom Media Queriesを導入した記述になります。上記のスタイルを適用した場合、次のように表示されます。

ビューポート幅590pxのとき、文字色はblueとなる。
ウィンドウ幅590pxで文字色が青である画像

ビューポート幅610pxのとき、文字色はgreenとなる。
ウィンドウ幅610pxで文字色が緑である画像

画像をみてもわかるように、CSS NestingCustom Media Queriesが反映されていますね。

この状態で、一度yarn buildコマンドでビルドしてみましょう。
dist/assets以下に任意のCSSファイルが生成されると思います。その中身を確認してみるとCSSが以下のように変換されていることがわかります。

dist/assets/index.*.css
* {
  margin: 0;
  padding: 0;
}
body {
  color: red;
}
body .title {
  color: green;
}
@media (max-width: 600px) {
  body .title {
    color: #00f;
  }
}

以上で、Lightning CSSをViteに導入することができました。

サンプルコード

本記事で行った作業結果を以下のリポジトリに置いてあります。全体のコードを確認したい方は以下をご参照ください。

https://github.com/yend724/vite-lightningcss-sample

おわりに

Lightning CSSはParcel以外でも簡単に使うことができます。
最新、もしくは将来のCSSの機能や構文をお手軽に試すことが可能なので、興味のある方はぜひ使ってみてください。

参考

https://lightningcss.dev/
https://vitejs.dev/

Discussion