feat: add postcss filter

All CSS processing is handled by the filter, and is now inlined into the
HTML
This commit is contained in:
Devin Haska 2025-03-16 12:07:31 -07:00
parent 6a84af0c37
commit 2aa912399c
14 changed files with 53 additions and 60 deletions

View file

@ -1,35 +0,0 @@
import colorSchemes from "../../config/design-tokens/colors.js";
import { helperClassesToCss } from "./helper-classes.js";
const lightScheme = colorSchemes.light;
const darkScheme = colorSchemes.dark;
const colorToCss = (key, value) =>
`--${key}: ${value}; --color-${key}: hsl(${value});`;
const colorSchemeToCss = (scheme) =>
Object.entries(scheme).reduce(
(css, [key, value]) => css + colorToCss(key, value),
``,
);
const lightCss = colorSchemeToCss(lightScheme);
const darkCss = colorSchemeToCss(darkScheme);
const colorSchemeToHelperClassesCss = (scheme, helperClasses) => {
return Object.entries(scheme).reduce((css, [key]) => {
return css + helperClassesToCss(helperClasses, key, `var(--color-${key})`);
}, ``);
};
const helperClasses = [
["text", ["color"]],
["bg", ["background-color"]],
];
const helperClassesCss = colorSchemeToHelperClassesCss(
lightScheme,
helperClasses,
);
export default `:root{${lightCss}}${helperClassesCss}@media (prefers-color-scheme: dark) {:root{${darkCss}}}`;

View file

@ -1,51 +0,0 @@
import { getFontUrl } from "../utils/fonts.js";
import fonts from "../../config/design-tokens/fonts.js";
const fontsToCss = (fonts) => {
return Object.entries(fonts).reduce((css, [, fontProperties]) => {
const family = fontProperties.family;
const format = fontProperties.format;
const weights = fontProperties.weights;
return (
css +
Object.entries(weights).reduce((css, [variant, fontFamily]) => {
const url = getFontUrl(fontFamily.path);
const style = fontFamily["font-style"];
const weight = fontFamily.weight;
const postScriptName = [family, variant].join("-").replaceAll(" ", "");
return (
css +
fontFamilyToCss(
family,
style,
weight,
url,
family,
postScriptName,
format,
)
);
}, ``)
);
}, ``);
};
const fontFamilyToCss = (
family,
style,
weight,
url,
localName,
postScriptName,
format,
) => `@font-face {
font-family: ${family};
font-style: ${style};
font-weight: ${weight};
font-display: swap;
src: local("${localName}"), local("${postScriptName}"), url("${url}") format("${format}")
}\n`;
export default fontsToCss(fonts);

View file

@ -1,45 +0,0 @@
import fonts from "../../config/design-tokens/fonts.js";
const fallbacks = [
"-apple-system",
"BlinkMacSystemFont",
"Segoe UI",
"Roboto",
"Ubuntu",
"Open Sans",
"Helvetica Neue",
"sans-serif",
];
const fontsToCss = (fonts) => {
return Object.entries(fonts).reduce((css, [fontType, fontProperties]) => {
const family = fontProperties.family;
const weights = fontProperties.weights;
const fontTypeCss = fontFamilyToCss(fontType, family);
const fontWeightsCss = fontWeightsToCss(fontType, weights);
return css + fontTypeCss + fontWeightsCss;
}, ``);
};
const validVariants = ["regular", "bold", "extrabold"];
const fontWeightsToCss = (type, weights) =>
Object.entries(weights)
.filter(([variant]) => validVariants.includes(variant.toLowerCase()))
.reduce((css, [variant, fontFamily]) => {
const weight = fontFamily.weight;
return css + fontWeightToCss(type, variant.toLowerCase(), weight);
}, ``);
const fontWeightToCss = (type, variant, value) =>
`--font-weight-${type}-${variant}: ${value};`;
const fontFamilyToCss = (type, value) =>
`--font-family-${type}: ${value},${fallbacks.join(",")};`;
const css = `:root{${fontsToCss(fonts)}}`;
export default css;

View file

@ -1,43 +0,0 @@
/**
* Given an array of CSS properties, output css properties
* with each property equal to `value`
*/
export const cssPropertiesToCss = (cssProperties, value) => {
return cssProperties.reduce((css, cssProp) => {
return css + `${cssProp}:${value};`;
}, ``);
};
/**
* Given a helperClass (string) and array of cssProperties,
* will generate a css class named helperClass that has
* all cssProperties mapped to value.
*/
export const helperClassToCss = (helperClass, cssProperties, value) => {
const cssProps = cssPropertiesToCss(cssProperties, value);
return `.${helperClass}{${cssProps}}`;
};
/**
* Given an array of helperClasses that map to cssProperties,
* output a string of CSS that maps the helperClass (with variant modifier)
* to the array of css properties with each css property equal to
* value
*
* e.g.
* helperClasses = [["text", ["color"]]],
* variant = "primary",
* value = "#000"
*
* Will output the following:
* .text-primary {
* color: #000;
* }
*/
export const helperClassesToCss = (helperClasses, variant, value) => {
return helperClasses.reduce((css, [helperClass, cssProperties]) => {
return (
css + helperClassToCss(`${helperClass}-${variant}`, cssProperties, value)
);
}, ``);
};

View file

@ -1,51 +0,0 @@
import spacing from "../../config/design-tokens/spacing.js";
import { helperClassesToCss } from "./helper-classes.js";
const spacingToCss = (variant, value) =>
`--spacing-${variant.replace(".", "\\.")}: ${value}px;`;
const spacingToHelperClassesCss = (spacingValues, helperClasses) => {
return Object.entries(spacingValues).reduce((css, [spacingVariant]) => {
const variant = spacingVariant.replace(".", "\\.");
return (
css +
helperClassesToCss(helperClasses, variant, `var(--spacing-${variant})`)
);
}, ``);
};
const helperClasses = [
["m", ["margin"]],
["my", ["margin-block-start", "margin-block-end"]],
["mx", ["margin-inline-start", "margin-inline-end"]],
["ml", ["margin-inline-start"]],
["mr", ["margin-inline-start"]],
["mt", ["margin-block-start"]],
["mb", ["margin-block-end"]],
["p", ["padding"]],
["py", ["padding-block-start", "padding-block-end"]],
["px", ["padding-inline-start", "padding-inline-end"]],
["pl", ["padding-inline-start"]],
["pr", ["padding-inline-start"]],
["pt", ["padding-block-start"]],
["pb", ["padding-block-end"]],
["w", ["width"]],
["h", ["height"]],
["size", ["width", "height"]],
["radius", ["border-radius"]],
["gap", ["gap"]],
["row-gap", ["row-gap"]],
["column-gap", ["column-gap"]],
["flow-space", ["--flow-space"]],
];
const spacingVariablesCss = Object.entries(spacing).reduce(
(css, [variant, value]) => css + spacingToCss(variant, value),
``,
);
const helperCss = spacingToHelperClassesCss(spacing, helperClasses);
const css = `:root{${spacingVariablesCss}}${helperCss}`;
export default css;

View file

@ -1,6 +1,10 @@
import { getFontUrl } from "../utils/fonts.js";
import path from "path";
import { ASSETS_FONTS_PATH } from "../../config/constants/paths.js";
import fonts from "../../config/design-tokens/fonts.js";
const getFontUrl = (src) => path.join(ASSETS_FONTS_PATH, src);
const preloads = [
{
as: "font",

View file

@ -1,49 +0,0 @@
/*
* Implementation sourced from eleventyone starter kit
* https://github.com/philhawksworth/eleventyone
* ---
* https://github.com/philhawksworth/eleventyone/blob/master/src/site/css/styles.11ty.js
*/
import fs from "fs";
import postcss from "postcss";
import postcssImport from "postcss-import";
import postcssImportExtGlob from "postcss-import-ext-glob";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";
import { posix as path } from "path";
import { fileURLToPath } from "url";
import colors from "../../css-utils/colors.js";
import fontFamily from "../../css-utils/font-family.js";
import fontVariables from "../../css-utils/font-variables.js";
import spacing from "../../css-utils/spacing.js";
export default class {
async data() {
const dirname = path.dirname(fileURLToPath(import.meta.url));
const rawFilepath = path.join(dirname, "./global.css");
const rawCss = fs.readFileSync(rawFilepath);
const css = `${rawCss}${fontFamily}${fontVariables}${colors}${spacing}`;
return {
permalink: `css/styles.css`,
excludeFromSitemap: true,
eleventyExcludeFromCollections: true,
rawFilepath,
rawCss: css,
};
}
async render({ rawCss, rawFilepath }) {
return await postcss([
postcssImportExtGlob,
postcssImport,
autoprefixer,
cssnano,
])
.process(rawCss, { from: rawFilepath })
.then((result) => result.css);
}
}

View file

@ -8,7 +8,6 @@
{% if title %}{{ title }} •{% endif %}
{{ meta.siteName }}
</title>
<link rel="stylesheet" href="/css/styles.css" />
{% include "partials/meta.html" %}
{% for preload in preloads %}
<link rel="preload"
@ -21,6 +20,10 @@
<script type="module"
src="https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@1.4.0/lite-youtube.min.js"></script>
{% endif %}
{% set css %}
{% include "css/styles.css" %}
{% endset %}
<style>{{ css | postcss | safe }}</style>
</head>
<body class="flex-col">
{% noRobots %}

View file

@ -1,3 +0,0 @@
import path from "path";
export const getFontUrl = (src) => path.join("/assets/fonts", src);