From 79e2598fc80d5ea7dbfeae6081d734f9ca85df62 Mon Sep 17 00:00:00 2001 From: Fynn Becker Date: Thu, 27 Jun 2024 21:21:50 +0200 Subject: [PATCH] feat(css): Reduce layout shift with fonts fallback --- README.md | 8 +++++ generate-fonts-fallback.js | 32 ++++++++++++++++++ package-lock.json | 26 +++++++++++++++ package.json | 2 ++ public/css/global/fonts-fallback.css | 49 ++++++++++++++++++++++++++++ public/css/main.css | 1 + public/css/tokens/typo.css | 6 ++-- 7 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 generate-fonts-fallback.js create mode 100644 public/css/global/fonts-fallback.css diff --git a/README.md b/README.md index d826165..14e36fe 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,14 @@ node --run dev node --run build ``` +## Fonts + +Generate fonts fallback CSS file: + +```sh +node generate-fonts-fallback.js +``` + ## Formatting ```sh diff --git a/generate-fonts-fallback.js b/generate-fonts-fallback.js new file mode 100644 index 0000000..85f9c94 --- /dev/null +++ b/generate-fonts-fallback.js @@ -0,0 +1,32 @@ +import fs from "node:fs/promises"; + +import { createFontStack } from "@capsizecss/core"; +import Arial from "@capsizecss/metrics/arial"; +import NotoSans from "@capsizecss/metrics/notoSans"; +import Roboto from "@capsizecss/metrics/roboto"; +import Teko from "@capsizecss/metrics/teko"; +import WorkSans from "@capsizecss/metrics/workSans"; +import prettier from "prettier"; +import stylelint from "stylelint"; + +const filepath = "./public/css/global/fonts-fallback.css"; + +const heading = createFontStack([Teko, NotoSans, Roboto, Arial]); +const text = createFontStack([WorkSans, NotoSans, Roboto, Arial]); + +let code = ` +${heading.fontFaces} +${text.fontFaces} +`; + +// Run Stylelint autofixer +const linterResult = await stylelint.lint({ code, fix: true }); +code = linterResult.code ?? ""; + +// Run Prettier formatter +const prettierOptions = await prettier.resolveConfig(filepath, { + editorconfig: true, +}); +code = await prettier.format(code, { filepath, ...prettierOptions }); + +await fs.writeFile(filepath, code); diff --git a/package-lock.json b/package-lock.json index f3569fb..559e574 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "@11ty/eleventy-plugin-rss": "^2.0.1", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0", "@11ty/eleventy-plugin-webc": "^0.11.2", + "@capsizecss/core": "^4.1.2", + "@capsizecss/metrics": "^3.2.0", "@eslint/js": "^9.5.0", "eslint": "^9.5.0", "eslint-plugin-simple-import-sort": "^12.1.0", @@ -481,6 +483,23 @@ "node": ">=4" } }, + "node_modules/@capsizecss/core": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@capsizecss/core/-/core-4.1.2.tgz", + "integrity": "sha512-5tMjLsVsaEEwJ816y3eTfhhTIyUWNFt58x6YcHni0eV5tta8MGDOAIe+CV5ICb5pguXgDpNGLprqhPqBWtkFSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.1.1" + } + }, + "node_modules/@capsizecss/metrics": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@capsizecss/metrics/-/metrics-3.2.0.tgz", + "integrity": "sha512-EVWRXJaakH6NTq+7cZawgFiqA+UyESMszN/c1oDHbC/b0SAzQJ/QLS0xpaa3Y+YMfXOkEEjkuChYIV5pSkgmcg==", + "dev": true, + "license": "MIT" + }, "node_modules/@csstools/css-parser-algorithms": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz", @@ -1781,6 +1800,13 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", diff --git a/package.json b/package.json index 9d4b596..7fc869c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,8 @@ "@11ty/eleventy-plugin-rss": "^2.0.1", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0", "@11ty/eleventy-plugin-webc": "^0.11.2", + "@capsizecss/core": "^4.1.2", + "@capsizecss/metrics": "^3.2.0", "@eslint/js": "^9.5.0", "eslint": "^9.5.0", "eslint-plugin-simple-import-sort": "^12.1.0", diff --git a/public/css/global/fonts-fallback.css b/public/css/global/fonts-fallback.css new file mode 100644 index 0000000..7b1c0f2 --- /dev/null +++ b/public/css/global/fonts-fallback.css @@ -0,0 +1,49 @@ +@font-face { + ascent-override: 155.511%; + descent-override: 77.1062%; + font-family: "Teko Fallback: Noto Sans"; + size-adjust: 61.6034%; + src: local("Noto Sans Regular"), local("NotoSans-Regular"); +} + +@font-face { + ascent-override: 145.9389%; + descent-override: 72.3601%; + font-family: "Teko Fallback: Roboto"; + size-adjust: 65.6439%; + src: local("Roboto"), local("Roboto-Regular"); +} + +@font-face { + ascent-override: 146.2593%; + descent-override: 72.519%; + font-family: "Teko Fallback: Arial"; + line-gap-override: 0%; + size-adjust: 65.5001%; + src: local("Arial"), local("ArialMT"); +} + +@font-face { + ascent-override: 88.3407%; + descent-override: 23.0826%; + font-family: "Work Sans Fallback: Noto Sans"; + size-adjust: 105.2743%; + src: local("Noto Sans Regular"), local("NotoSans-Regular"); +} + +@font-face { + ascent-override: 82.9031%; + descent-override: 21.6618%; + font-family: "Work Sans Fallback: Roboto"; + size-adjust: 112.1791%; + src: local("Roboto"), local("Roboto-Regular"); +} + +@font-face { + ascent-override: 83.0851%; + descent-override: 21.7093%; + font-family: "Work Sans Fallback: Arial"; + line-gap-override: 0%; + size-adjust: 111.9334%; + src: local("Arial"), local("ArialMT"); +} diff --git a/public/css/main.css b/public/css/main.css index bad16d2..6e816fc 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -9,6 +9,7 @@ /* Global */ @import url("global/fonts.css") layer(global); +@import url("global/fonts-fallback.css") layer(global); @import url("global/base.css") layer(global); /* Typography */ diff --git a/public/css/tokens/typo.css b/public/css/tokens/typo.css index a8f7ebb..0301da5 100644 --- a/public/css/tokens/typo.css +++ b/public/css/tokens/typo.css @@ -1,10 +1,12 @@ :root { /* Default */ - --typo-Default-font-family: "Work Sans", sans-serif; + --typo-Default-font-family: "Work Sans", "Work Sans Fallback: Noto Sans", + "Work Sans Fallback: Roboto", "Work Sans Fallback: Arial", sans-serif; --typo-Default-line-height: 1.4; /* Heading */ - --typo-Heading-font-family: "Teko", serif; + --typo-Heading-font-family: "Teko", "Teko Fallback: Noto Sans", + "Teko Fallback: Roboto", "Teko Fallback: Arial", sans-serif; --typo-Heading-font-weight: 600; --typo-Heading-line-height: 1;