Using Fontsource With 11ty

Murtuzaali Surti
Murtuzaali Surti

• 4 min read

🏷️ 11ty

🏷️ fontsource

Dealing with fonts can get quite overwhelming. For quite some time, I was searching for a way to self host google fonts because the google fonts API's network request increased the render blocking time more than I expected.

I stumbled upon fontsource.org the other day and I found the idea of installing fonts from npm packages appealing.

At first, I used it in my personal site built with Astro. The process was incredibly simple. You just have to import fonts in your base layout file and that's it. You are ready to use it in your CSS!

npm i @fontsource/poppins @fontsource/nunito
import "@fontsource/poppins"
import "@fontsource/nunito/400.css"

I thought what if I could use this in my 11ty blog too. The next immediate thing which came to mind was to use vite because it can bundle those fonts for me just like Astro.

So, I decided to use slinkity. You can call slinkity as an extension for 11ty projects. It adds more features on the table by post processing your 11ty build output.

Slinkity uses vite for bundling which is great, but it became a problem for me. I have a relatively old and large project with some custom configurations which came in the way of how vite does things, so migrating to vite resulted in a mess. The migration needs more time. So, I decided to take another approach which might be not so good.

Using Rollup #

Rollup is a javascript bundler you can use to bundle your modules.

npm install rollup --save-dev

I created a rollup config file, named rollup.config.mjs at the root of the project.

export default [
    {
        input: "src/js/combine.js",
        output: {
            file: "src/js/minified/index.bundle.js",
            sourcemap: false,
        }
    }
];

The idea is to import the fonts just like I did in astro, in a javascript file and then extract css from it in order to copy it in the styles folder.

So, inside the combine.js file, I imported some fonts.

import "@fontsource/nunito";
import "@fontsource/poppins";

Now, to extract css, I used the postcss rollup plugin and the rollup copy plugin to copy the generated css file to the desired directory. I had to use rollup-plugin-copy because of this issue.

Installed the following dependencies:

npm i rollup-plugin-postcss rollup-plugin-copy @rollup/plugin-node-resolve --save-dev

The postcss plugin will extract the styles from the javascript file into a new CSS file.

import { nodeResolve } from "@rollup/plugin-node-resolve";
import postcss from "rollup-plugin-postcss";

export default [
    {
        input: "src/js/combine.js",
        output: {
            file: "src/js/minified/index.bundle.js",
            sourcemap: false,
        }
        plugins: [
            nodeResolve(),
            postcss({
                extract: true, // no way to move output to another folder https://github.com/egoist/rollup-plugin-postcss/issues/250
                minimize: true,
            })
        ],
    }
];

The next step is to move the CSS file to the desired location because the extracted CSS file will be in the same directory as of the javascript file and you can't specify a location outside the parent folder due to a bug in the postcss plugin.

import { nodeResolve } from "@rollup/plugin-node-resolve";
import postcss from "rollup-plugin-postcss";
import copy from "rollup-plugin-copy";

export default [
    {
        input: "src/js/combine.js",
        output: {
            file: "src/js/minified/index.bundle.js",
            sourcemap: false,
        }
        plugins: [
            nodeResolve(),
            postcss({
                extract: true, // no way to move output to another folder https://github.com/egoist/rollup-plugin-postcss/issues/250
                minimize: true,
            }),
            copy({
                targets: [
                    {
                        src: "src/js/minified/index.bundle.css",
                        dest: "src/styles/minified",
                        rename: "fonts.bundle.css",
                    },
                ],
                verbose: true,
                hook: "writeBundle",
            })
        ],
    }
];

I am running the copy plugin after all the bundles are written and that's why you see the hook: "writeBundle" build hook specified for the copy plugin.

Run rollup using this command:

npx rollup --bundleConfigAsCjs -c

Now, the font CSS is ready to use, right? No. There is still one problem.

This is how the generated CSS looks:

@font-face{font-display:swap;font-family:Nunito;font-style:normal;font-weight:400;src:url(files/nunito-cyrillic-ext-400-normal.woff2) format("woff2"),url(files/nunito-all-400-normal.woff) format("woff");unicode-range:u+0460-052f,u+1c80-1c88,u+20b4,u+2de0-2dff,u+a640-a69f,u+fe2e-fe2f}@font-face{font-display:swap;font-family:Nunito;font-style:normal;font-weight:400;src:url(files/nunito-cyrillic-400-normal.woff2) format("woff2"),url(files/nunito-all-400-normal.woff) format("woff");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}

If you look closely at the src url, you will find that the files which the url is referring to are not present in our directory. Yes, the actual font files.

I used addPassthroughCopy() in 11ty to copy the required font files from node_modules to the output directory as per the url specified in the font stylesheet.

  eleventyConfig.addPassthroughCopy({'./node_modules/@fontsource/poppins/files/*.woff2': 'styles/files'})

Copy the required files to the output folder and the fonts are ready to be used.

font-family: "Poppins", "Nunito", sans-serif;

Conclusion

Is this a good solution? I don't know. For me it's more of a hack than a solution and I think there's a better way. Maybe, all we need is a 11ty plugin :)


Creating Git Hooks Using Husky

Previous

What is DOM diffing?

Next