このリポジトリ でやったこと。

やろうとしたこと

tailwind.css は Utility First と銘打った CSS フレームワークで、コンポーネント化を前提としたモダンフレームワークと相性がいいです。今回は next.js の amp-mode で tailwind を使おうとしてみました。

Tailwind CSS - A Utility-First CSS Framework for Rapidly Building Custom Designs

問題

前提として、 amp には inline css の容量制限があり、75kb を超えると AMP と認識されなくなります。

tailwind はビルドして使うのが前提のフレームワークですが、全部入りの tailwind.min.css は 1.3 M あります。これでは到底、75 kb に収まりません。

AMP の CSS サイズ上限が 50000 バイトから 75000 バイトへ 50%増量 ⬆ | 海外 SEO 情報ブログ

tailwind css は reset.css が重いというより、utility の数が膨大で、それらをすべて読み込むのでビルドサイズがかさんでるという感じでした。

なので、 purgecss を使って、使っている CSS だけ使えばよい、という発想で CSS のサイズを削ってみました。

purgecss

purgecss は指定した html,js,tsx, vue のソースコード上に出現する CSS セレクタのみ抽出するライブラリです

FullHuman/purgecss: Remove unused CSS

もちろん、素朴にやるとそれだけでは動かないので、さらに whitelist で許可する CSS を宣言します。今回は postcss を通して使います。

postcss の設定

ついでに、postcss-import を使って、単一ファイルに bundle しつつ、 cssnano で圧縮しています。

準備

yarn add postcss postcss-import taliwindcss @fullhuman/postcss-purgecss postcss-preset-env cssnano postcss-cli -D

postcss.config.js

module.exports = {
  plugins: [
    require("postcss-import"),
    require("tailwindcss"),
    require("@fullhuman/postcss-purgecss")({
      content: [
        "./pages/**/*.{js,jsx,ts,tsx}",
        "./components/**/*.{js,jsx,ts,tsx}",
      ],
      defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
    }),
    require("postcss-preset-env"),
    require("cssnano")({
      preset: "default",
    }),
  ],
};

で、

postcss style/index.css -o css/bundle.css # ビルド

これで css/bundle.css をビルドできました。

next amp で読み込む

普通の next.js 環境なら import "./bundle.css" でいいのですが、amp mode だとちょっと工夫する必要があります。

webpack の raw-loader を通して css を文字列として読み込み、style tag の中に展開します。 (yarn add raw-loader -D)

// @ts-ignore
import bundleCss from "!raw-loader!../style/bundle.css";
import Document, { Html, Head, Main, NextScript } from "next/document";

export default class MyDocument extends Document {
  static async getInitialProps(ctx: any) {
    const page = ctx.renderPage((App) => (props) => <App {...props} />);
    const initialProps: any = await Document.getInitialProps(ctx);
    return {
      ...page,
      styles: [
        ...initialProps.styles,
        <style
          key="custom"
          dangerouslySetInnerHTML={{
            __html: bundleCss,
          }}
        />,
      ],
    };
  }

  render() {
    return (
      <Html>
        <Head></Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

(initialProps.styles は next.js が生成する AMP のためのデフォルト CSS なので、必須です)

おまけ: amdxg-components に適用してみた

動作サンプルはこのブログ自身です。ヘッダやフッタなどが tailwind.css によって書かれています。

このブログは amdx/packages/ssg で書かれているのですが、その内部でさらに分割した amdxg-components パッケージに tailwind + purgecss を適用しました。

bundle.css のサイズは最終的に 1.3M から 6.3K になりました。これなら AMP 環境でも安心。

History
e3cee89 - Update Thu May 14 01:55:14 2020 +0900 
a3673d2 - new:page next tailwind Mon May 11 03:21:08 2020 +0900