TailwindCSS と daisyUI の CSS ファイルを削減する方法・インライン CSS にする方法

CSS のユーティリティクラスを提供する「TailwindCSS」と、それをベースにしたコンポーネント UI ライブラリの「daisyUI」というのを、最近よく使っている。

Vite で開発環境を構築した、React + React Router ベースのプロジェクトに、以下のように導入している。

/* TailwindCSS を使用する */
@import "tailwindcss";

/* daisyUI を使用する */
@plugin "daisyui" {
  themes: light --default;  /* ライトテーマのみにする */
}

TailwindCSS のスキャン範囲を調整する source 指定

ふと、TailwindCSS ってビルドされる CSS ファイルサイズを削減できないのかな?と思って調べたところ、source 指定というのが有効だと分かった。

自分が作るプロジェクトは client/ ディレクトリに React Router を SPA モードで配置し、server/ ディレクトリに Hono によるバックエンド API サーバを実装する、という構成にしていることが多い。TailwindCSS のビルドを担う Vite の設定ファイル vite.config.ts はプロジェクトルートにあり、TailwindCSS は標準ではプロジェクト全体の全てのファイルを走査し、CSS クラス名っぽいモノを拾い上げて CSS ファイルをビルドしているようなのだ。

つまり、SSR などをしない純粋なクライアントサイドオンリーの SPA なのに、サーバサイドのコードまで参照して CSS ファイルをビルドしてしまっており、本来使用箇所はないのに「それっぽい文字列」を見つけただけで CSS ファイルが膨れ上がっている可能性があるというのだ。

これを回避するため、TailwindCSS が提供する @source ディレクティブや source() 関数を使って、スキャン対象を変更できるのである。今回の自分の例では、以下のようにすることで効果があった。

/* TailwindCSS を使用する : `source` 指定で `client/` ディレクトリ配下のファイルのみスキャンするようにする */
@import "tailwindcss" source("./");

@source ディレクティブを使うとより柔軟にスキャン対象とするファイルを指定できたりもする。

daisyUI にも include 指定がある

daisyUI も、基本的にはプロジェクト内で使用されている CSS クラス名を自動的にスキャンして、必要なコードだけをバンドルしてくれるのだが、include を指定することでビルドに含めるコンポーネントを自分で指定できる。

使っているコンポーネントなのに include に書かない、などするとデザインが崩れる恐れがあるので指定は慎重にすべきだが、コレもファイルサイズ削減に効果があった。

/* daisyUI を使用する */
@plugin "daisyui" {
  themes: light --default;  /* ライトテーマのみにする */
  
  /* 共通のベーススタイル、および使用中のコンポーネント類のみ含める (`btn` は `button`・`file-input` は `fileinput` で指定する)  */
  include: properties, rootcolor, scrollbar, rootscrolllock, rootscrollgutter, svg, loading, alert, button, input, fileinput, link, radio, select, textarea, join, table;
}

properties, rootcolor, scrollbar, rootscrolllock, rootscrollgutter, svg までは「ベーススタイル」とされる指示で、合計で 1KB 未満らしいが、まぁ明記が必要。あとは .btn を使っていれば button を書き、.table を使っていれば table を書き、というような感じで、使っているコンポーネント名を書けば良い。

どのくらいファイルサイズが変わったか?

今回、2つのプロジェクトで試してみたので紹介する。

以下は TailwindCSS + daisyUI のプロジェクトの、ビルド後の CSS ファイルのサイズ。前述のとおり、プロジェクトのディレクトリ全体としては Hono によるサーバサイドのコードやら、docs/ ディレクトリに Markdown ファイルなどがある状態。

source 指定の時点で 30KB ほど削減できているのがスゴい。それだけ「プロジェクト全体のテキストを読むと、TailwindCSS のクラス名っぽい文字列があったが、実際には使っていなかった」のだと分かる。

include 指定も行うと、さらに 16KB ほど削減できた。当然、サイト全体でスタイル指定が抜けてしまってデザイン崩れが発生したような場所はないことを確認済み。

サイトの見た目には影響ないのに、合計で 50KB ほど CSS ファイルを削減できた。

次に、コチラは TailwindCSS のみを使っている、先ほどよりは小規模なプロジェクト。

こちらは 4KB の削減に留まったが、とはいえ「4KB 分は使用箇所のない CSS コードだった」というのを source("./") と指定しただけで削減できたのは、お手軽かつ強力だと思う。

React Router SPA において CSS をインライン埋め込みするには

さて、ココからは PageSpeed Insights でのパフォーマンス向上を目的とした話。上述のように、ビルドする CSS ファイルのサイズ自体は削減できたが、これは依然としてビルド後の資材としては assets.css という別ファイルで配信される。そのため PageSpeed Insights では「ページの初回読み込みをブロッキングしている、パフォーマンス低下に繋がっている原因のファイル」と指摘されてしまうことがある。

そこで、CSS を index.html ファイル内にインラインで埋め込むことで、別ファイルのロードを減らそう、という話である。

React Router の SPA モードで実装している場合、元々このように実装していた。

import './styles.css';  // CSS ファイルを直接 `import` しておくと、`assets.css` と別ファイルにビルドされる

/** `index.html` を構築するルートレイアウト */
export function Layout({ children }: { children: ReactNode }): ReactElement {
  return (
    <html lang="ja">
      <head>
        <meta charSet="UTF-8" />
        <title>サンプル</title>
        
        {/* `<Links />` を書いておくことで `assets.css` ファイルを読み込むための `<link>` 要素がビルド時に生成される */}
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

コレを以下のようにすることで、CSS コードを HTML ファイル内にインライン書き込みできる。

import styles from './styles.css?inline';  // ← Vite が提供する `?inline` 指定の仕組みを利用して CSS コードを取得する

export function Layout({ children }: { children: ReactNode }): ReactElement {
  return (
    <html lang="ja">
      <head>
        <meta charSet="UTF-8" />
        <title>サンプル</title>
        
        {/* 取得した CSS をインラインで埋め込む */}
        <style>{styles}</style>
        
        {/* Links は残しておいて OK */}
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

このように、styles という名前で取得した CSS コードを style 要素内にブチ込む形にするだけ。

コレだけでビルドされたファイルの中に assets.css といった CSS ファイルは一切出力されなくなり、index.html 内にビルドされた CSS コードが埋め込まれた状態にできる。

PageSpeed Insights のパフォーマンススコアも向上できてよきよき。