New site part 2 - styling

Part of a series about setting up the new 11ty version of this site

Having got the project structure sorted, we need to get a basic page layout sorted. But to do that we first need some more config and a starter layout that deals with the 'cookie-cutter' stuff.

Style and Sass

I prefer to write my styling in Sass (SCSS) rather than raw CSS. Unfortunately, 11ty doesn’t work with Sass out-of-the-tin (although there’s some example implementations in the docs).

I used to have a simple process, using a Nunjucks file to consolidate all my SCSS and then running it through a conversion filter in the build stage, but recently came across an article by Stephanie Eckles that removes the interim step.

I’ve slightly changed the approach to suit my own use case, but the gist is:

  1. Add sass, lightningcss and browserslist to the project
npm install --save-dev sass lightningcss browserslist
  1. Add some requires in eleventy.js
// eleventy.js – before module.exports
 
const sass = require(“sass”);
const { transform, browserslistToTargets } = require(“lightningcss”);
const browserslist = require(“browserslist”);
 
  1. Add configuration
// eleventy.js – add to the module.exports
 
module.exports = function(eleventyConfig) {

    ...

     // recognise Sass as a “template language”
     eleventyConfig.addTemplateFormats(“scss”);
 
     // compile Sass
     eleventyConfig.addExtension(“scss”, {
        // send the output to a css file
        outputFileExtension: "css",
 
        // when compiling …
        compile: async function (inputContent, inputPath) {
            // … skip files that start with an underscore (by convention,
            // these will be imported somewhere) …
            let parsed = path.parse(inputPath);
            if (parsed.name.startsWith("_")) {
                return;
            }
 
            // … compile through the sass package …
            let result = sass.compileString(inputContent, {
                loadPaths: [parsed.dir || "."],
                sourceMap: false, // or true, your choice!
            });
 
            // … allow included files from @use or @import to
            // trigger rebuilds when using –incremental …
            this.addDependencies(inputPath, result.loadedUrls);
 
            // … and run the result through a lightning css transform to
            // target most browsers and minify it
            let targets = browserslistToTargets(browserslist("> 0.2% and not dead"));
 
            return async () => {
                let { code } = await transform({
                    code: Buffer.from(result.css),
                    minify: true,
                    sourceMap: false,
                    targets,
                });
                return code;
            };
        }
    });

    ...

}

With this setup, we can now include SCSS files in the src/css/ folder and they will be compiled to minified CSS during build.

Layout Basics

I have a generic base layout for 11ty projects, which I hope is worth sharing:

  1. Front Matter — set a default title and description plus an empty metacontent tag.
// src/_includes/layouts/base.njk - pt 1

---
title: Default
description: Default description
metacontent: []
---
  1. Document basics plus use any metacontent that's been passed.
// src/_includes/layouts/base.njk - pt 2

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="{{ description }}">
    
    {% for item in metacontent %}
        <meta name="{{ item[0] }}" content="{{ item[1] }}">
    {% endfor %}

    <meta name="generator" content="Eleventy">
    
    <title>{{ title }} | domwakeling</title>
  1. Google fonts — to prevent blocking during load, I set media="print" with an onload that will remove the media tag (so the font load will after initial render). This requires the second, basic, link wrapped in a <noscript> tag in case javascript is disabled.
// src/_includes/layouts/base.njk - pt 3

    {# font stuff #}
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link
        rel="stylesheet"
        media="print"
        onload="this.onload=null;this.removeAttribute('media');"
        href="link_to_fonts">
    <noscript>
        <link
            rel="stylesheet"
            href="link_to_fonts">
    </noscript>
  1. Import stylesheets and favicons (we previously included a line in the 11ty config that would copy files from src/_includes/favicons/ to the root build folder).
// src/_includes/layouts/base.njk - pt 4

    {# style stuff #}    
    <link rel="stylesheet" href="/css/style.css" type="text/css">

    {# favicon stuff #}
    <link rel="icon" type="image/x-icon" href="/favicon.ico" />
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
    <link rel="manifest" href="/site.webmanifest">
  1. The rest (content is wrapped in a <main> tag, if I want a header and footer they are also included here).
// src/_includes/layouts/base.njk - pt 5
    
</head>
<body>
    <main id="main">{{ content | safe }}</main>
</body>
</html>