One file to create in your project
The module declares where Hugo looks for the PostCSS config. The file itself has to exist in your project: a module cannot provide it.
Bootstrap generates a large stylesheet, so this config is also where PurgeCSS removes the classes your site never renders.
The path
The module sets the directory in its own hugo.yaml, so there is nothing to declare in your project params:
# provided by hugolify-theme-bootstrap, do not repeat it
params:
css:
postcss: "postcss/bootstrap"
The file
Create the file at that path. The module ships it at its root, ready to copy:
postcss.config.js in the module/postcss/bootstrap/postcss.config.js
module.exports = {
plugins: {
autoprefixer: {},
'@fullhuman/postcss-purgecss': {
mode: 'all',
content: ['./hugo_stats.json'],
dynamicAttributes: ['aria-current', 'aria-hidden', 'aria-expanded', 'href', 'role', 'type'],
safelist: {
standard: ['show', 'showing', 'hide', 'fade', /-backdrop$/, /^is-/, /^splide_/],
deep: [/^tobii/]
},
defaultExtractor: (content) => {
// hugo_stats.json holds null tags/classes/ids until Hugo finishes
// collecting stats (first build, or after a crashed build), so guard
// against null to avoid "Cannot read properties of null".
const els = JSON.parse(content).htmlElements;
return [...(els.tags || []), ...(els.classes || []), ...(els.ids || [])];
}
}
}
};
Enable the build stats
PurgeCSS does not read the rendered HTML. It reads hugo_stats.json, the list of tags, classes and ids Hugo actually emitted. That file has to be enabled:
/config/_default/hugo.yaml
build:
buildStats:
disableClasses: false
disableIDs: true
disableTags: false
enable: true
build.writeStats: true is the deprecated form of the same switch. Use build.buildStats.enable: it lets you keep ids out of the file, which keeps it small.
The safelist
A class that only ever appears at runtime (added by JavaScript, or built by string concatenation) is invisible to Hugo, so it never lands in hugo_stats.json and PurgeCSS strips it. The safelist puts it back.
| Entry | Why |
|---|---|
show, showing, hide, fade | Bootstrap toggles them from JS (collapse, modal, offcanvas) |
/-backdrop$/ | Backdrops are injected by Bootstrap at runtime |
/^is-/ | State classes set by the Hugolify vanilla JS |
/^splide_/, /^tobii/ | Class names generated by the carousel and lightbox vendors |
Add your own entries when a component of yours toggles a class from JavaScript.
Install the packages
hugo mod get && hugo mod npm pack && yarn install
Projects started from hugolify-template have it as a script:
yarn install:hugolify
Run it again after every hugo mod get -u: the npm dependencies are declared by the modules, and hugo mod npm pack is what copies them into your package.json.
Troubleshooting
Why the module cannot provide it
Hugo resolves css.postcss from the project root, on the real filesystem, and the file is then loaded by Node (postcss-load-config), which knows nothing about Hugo’s virtual filesystem. A module mount therefore cannot supply it, and Hugo does not fall back to the theme or to the modules:
POSTCSS: failed to transform "sass/main.sass" (text/css):
postcss config "postcss/bootstrap" not found
Styles missing in production only
PurgeCSS runs on every build, but a class rendered on a page you did not build locally is absent from hugo_stats.json. Build the whole site once (yarn build) before judging what was purged.
The stylesheet is not processed, and nothing errors
If the package.json of your project declares "type": "module", a postcss.config.js written in CommonJS loads as an empty config, without an error and therefore without any purge. Name the file postcss.config.cjs instead.