New site part 2 - styling
Posted on 30 September, 2023
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:
- Add
sass,lightningcssandbrowserslistto the project
npm install --save-dev sass lightningcss browserslist
- Add some
requiresineleventy.js
// eleventy.js – before module.exports
const sass = require(“sass”);
const { transform, browserslistToTargets } = require(“lightningcss”);
const browserslist = require(“browserslist”);
- 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:
- Front Matter — set a default
titleanddescriptionplus an emptymetacontenttag.
// src/_includes/layouts/base.njk - pt 1
---
title: Default
description: Default description
metacontent: []
---
- 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>
- Google fonts — to prevent blocking during load, I set
media="print"with anonloadthat will remove themediatag (so the font load will after initial render). This requires the second, basic,linkwrapped 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>
- 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">
- 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>