メインコンテンツまで移動する

Eleventy での Sass の使用と CSS の軽量化

  • 公開日

Akira Web デザイナー

やること

静的サイトジェネレーター Eleventy で CSS の環境を構築する方法です。以下の 2 つを行います。

  • Sass を使えるようにする。
  • CSS の軽量化。

CSS の軽量化では、以下のことを行います。

  • ページ内の全ての <style> タグを取得し、1 つの <style> タグにまとめる。
  • ページで未使用の CSS を削除する。
  • CSS を縮小する。

下準備

今回は Eleventy 公式のスタータープロジェクト eleventy-base-blog を使って説明します。尚、必要な Node.js のバージョンは、Getting Started で確認できます。

eleventy-base-blog を使用するために、GitHub リポジトリをクローンします。

git clone https://github.com/11ty/eleventy-base-blog.git my-blog-name

プロジェクトに移動します。

cd my-blog-name

依存関係のパッケージをインストールします。

npm i

あとはローカルでサーバーを立てます。

npm run serve

これで http://localhost:8080 でサイトを開けます。

Sass を使えるようにする

まずは、Sass を使えるようにします。使用するパッケージは以下の 2 つです。

この 2 つをインストールします。

npm i -D sass npm-run-all

そして、package.json の scripts を書き換えます。11ty Sass Skeleton を参考にしました。

"scripts": {
  "watch:sass": "sass --no-source-map --watch sass:_includes",
  "watch:eleventy": "npx @11ty/eleventy --serve",
  "build:sass": "sass --no-source-map sass:_includes",
  "build:eleventy": "npx @11ty/eleventy",
  "start": "npm-run-all build:sass --parallel watch:*",
  "build": "npm-run-all build:eleventy"
}

次に、プロジェクトディレクトリの直下に sass フォルダを作り、フォルダ内に style.scss ファイルを作ります。

そして、ローカルサーバーを立てます。

npm start

これで style.scss に何かを書けば、コンパイルされた style.css が _includes フォルダに出力されます。

最後に、コンパイル後の style.css をサイトに反映させます。_includes/layouts/base.njk にある <link rel="stylesheet"> を全て削除し、代わりに include を使い style.css をインラインで読み込みます。

{# <link rel="stylesheet" href="{{ '/css/index.css' | url }}"> #}
<style>{% include 'style.css' %}</style>

これで Sass が使えるようになりました。

尚、インラインで CSS を読み込むのは、この後の CSS の軽量化が関係しています。

CSS の軽量化

次は、表示速度の観点から、ページ内で未使用の CSS の削除を行います。また、CSS の縮小も行います。

この削除と縮小を行うにあたって頭に浮かんだのが、Eleventy プラグインの Eleventy AMP Plugin です。この AMP プロジェクト公式プラグインは、ページ内にある全ての <style> タグを取得し、1 つの <style> タグにまとめた後に縮小します。これがコンポーネント化などに便利だったため、同じ動作になるようにしたいと思いました。

未使用の CSS の削除、そして縮小するために使用するパッケージは以下のものです。

4 つのパッケージをインストールします。

npm i -D clean-css jsdom purge-from-html purgecss

そして、プロジェクトディレクトリの直下に minify.js ファイルを作り、以下を追加します。

const { JSDOM } = require('jsdom');
const { PurgeCSS } = require('purgecss');
const PurgeFromHTML = require('purge-from-html').extract;
const CleanCSS = require('clean-css');

module.exports = async (rawContent, outputPath) => {
  if (outputPath && outputPath.endsWith('.html')) {
    const dom = new JSDOM(rawContent);
    const document = dom.window.document;
    const styles = document.querySelectorAll('style');
    let rawCSS = '';

    if (styles.length) {
      for (const style of styles) {
        rawCSS = rawCSS + style.textContent;
        style.remove();
      }
    }

    rawCSS = await new PurgeCSS().purge({
      content: [{ raw: rawContent, extension: 'html' }],
      css: [{ raw: rawCSS }],
      // 未使用の CSS が削除されず残るため purge-from-html を使用する
      extractors: [{ extractor: PurgeFromHTML, extensions: ['html'] }],
      fontFace: true,
      keyframes: true,
    });

    rawCSS = new CleanCSS({
      level: {
        2: {
          all: true,
          // 他のプロジェクトにてマークダウンのコードの挿入でエラーが出たため念の為に false にする
          removeUnusedAtRules: false,
        },
      },
    }).minify(rawCSS[0].css).styles;

    const tag = document.createElement('style');
    rawCSS = document.createTextNode(rawCSS);
    tag.appendChild(rawCSS);
    document.head.appendChild(tag);

    return dom.serialize();
  }

  return rawContent;
};

そして、.eleventy.js の module.exportsaddTransform を使い minify.js を読み込みます。

module.exports = function (eleventyConfig) {
  eleventyConfig.addTransform('minify', require('./minify'));

  // 以下省略
};

これで未使用の CSS の削除と CSS の縮小の完成です。

試してみます。とりあえず、以下の CSS を style.scss に書いてみます。

h1 {
  color: red;
  font-size: 48px;
}

.sample {
  display: flex;
}

<style> タグには、このように出力されます。

<style>
  h1 {
    color: red;
    font-size: 48px;
  }
</style>

eleventy-base-blog には class="sample" の要素はないため、.sample のスタイルは PurgeCSS により全てのページで削除されます。

続いて、posts/firstpost.md<style> タグを追加し、h1 を上書きしてみます。

<style>
  h1 {
    color: green;
  }
</style>

すると、http://localhost:8080/posts/firstpost/ でのみ、以下の <style> タグが出力されます。

<style>h1{font-size:48px;color:green}</style>

firstpost.md に書いた color: green;<head> タグの <style> タグに出力されます。また、本来 h1 は 2 つあるはずですが、clean-css が 1 つにまとめています。さらに、style.scss に書いた color: red; は適用されなくなったため、clean-css により削除されます。尚、他のページでは color: red; のままです。

CDN から読み込む場合

CDN で配信されているスタイルシートを使う場合も CSS の軽量化の対象にできます。

使用するのは、Eleventy 公式プラグインの eleventy-fetch です。

使用するために、まずはインストールします。

npm i -D @11ty/eleventy-fetch

今回はショートコードで eleventy-fetch を使ってみます。.eleventy.js に以下を追加します。

const EleventyFetch = require('@11ty/eleventy-fetch');

module.exports = function (eleventyConfig) {
  eleventyConfig.addAsyncShortcode('stylesheet', async (url) => {
    return `<style>
      ${await EleventyFetch(url, {
        duration: '1w',
        type: 'text',
      })}
      </style>`;
  });

  // 以下省略
};

そして、ショートコードを使います。引数にはスタイルシートの URL を指定します。試しに、.njk ファイルに以下を追加し、CSS リセットの A modern CSS reset を読み込みます。

{% stylesheet 'https://unpkg.com/modern-css-reset/dist/reset.min.css' %}

ショートコードは、どこで使っても構いません。ただ、<style> タグを 1 つにまとめる際に、HTML の上から順にページ内の <style> タグを取得します。CSS の最初にあって欲しい CSS リセットは、<head> タグの上部に書くのがいいかもしれません。読み込み順を気にしたくない場合は、@layer を使うのがいいと思います。

これで A Modern CSS Reset をサイトに読み込みながら、未使用の CSS を削除し、そして縮小もできます。

1 つの <style> タグにまとめられ、PurgeCSS により A Modern CSS Reset の未使用の CSS が削除されます。

Google Fonts を読み込む場合は、Fetch Google Fonts CSS に書かれているようにオプションの fetchOptions を使いユーザーエージェントを伝えられます。

尚、eleventy-fetch を使用すると .cache フォルダが作成されます。この .cache フォルダは、.gitignore に追加するように強く推奨されています。

参考:Fetch — Eleventy

フォローする