Eleventy での Sass の使用と CSS の軽量化
やること
静的サイトジェネレーター 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.exports
で addTransform
を使い 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 に追加するように強く推奨されています。