Important
The following assumes you have Node.js installed on your machine.
GitHub Pages Optimised Build is a test project designed to demonstrate an automated front-end build pipeline using modern tooling. The key purpose is to:
- Automatically bundle and minify JavaScript, process and minify CSS (including nesting), and minify HTML
- Save all production-ready assets into a
/docs
folder - Configure GitHub Pages to serve the site directly from
/docs
This approach ensures the original development files (with modular JS, nested CSS, and unminified HTML) stay intact in the root of the project, while the optimized output is safely contained and served from /docs
.
The build process is fully scriptable using NPM scripts, with no reliance on tools like Webpack or Gulp.
- The
/docs
folder contains optimized production-ready assets, including:bundle.js
: the JavaScript bundled into a single file (no modules)style.min.css
: the CSS processed with nesting flattened and minified
- The original source files (
index.js
, files injs-modules/
,style.css
with nesting, etc.) remain in the project root for easier development and editing. - GitHub Pages is configured to serve the site from the
/docs
folder instead of the root, so your published site uses these optimized files.
Important
The build process only supports a flat HTML structure, i.e. all HTML pages must sit in the root of the project (no folders).
Important
When publishing to GitHub Pages, make sure the Pages setting is configured to serve from the /docs
folder on the main
branch.
Run this once in your project root to install dev dependencies:
npm install --save-dev esbuild html-minifier-terser postcss postcss-cli postcss-import postcss-nesting cssnano shx
- esbuild — bundles, transpiles (to ES2015), and minifies JS
- html-minifier-terser — minifies HTML files in the
/docs
folder after copying and post-processing - postcss, postcss-cli — for CSS processing
- postcss-import — enables @import rules in CSS for modular stylesheets
- postcss-nesting — enables CSS nesting support
- postcss-url — processes and rewrites URLs in CSS (e.g., copying assets and adjusting paths) for correct referencing in the build output
- cssnano — minifies CSS
- shx — cross-platform CLI utility (copy, rm, etc.)
Note
For the current project, package.json
and package-lock.json
already contain references to all the required npm
packages, so all you have to do is run npm install
in the terminal to install node_modules
(which will contain the required packages).
module.exports = {
plugins: [
require("postcss-import"),
require("postcss-nesting"),
require("cssnano")({ preset: "default" }),
],
}
Note
For the current project, postcss.config.js
already exists in the project root.
{
"scripts": {
"build:js": "esbuild index.js --bundle --minify --target=es2015 --outfile=docs/bundle.js", // bundles, transpiles to ES2015, and minifies
"build:css": "postcss style.css --output docs/style.min.css",
"copy:assets": "shx cp -r img favicon.ico favicon-16x16.png favicon-32x32.png apple-touch-icon.png android-chrome-192x192.png android-chrome-512x512.png docs/", // Add or remove items from here as required - do not touch shx cp -r OR docs/
"copy:html": "shx cp *.html docs/",
"postprocess:html": "node scripts/postprocess-html.js",
"build": "npm run build:js && npm run build:css && npm run copy:assets && npm run copy:html && npm run postprocess:html"
}
}
Note
For the current project, build scripts have already been added to package.json
.
Note
The copy:assets
script includes the required files for the current project. You can edit it to include additional folders or assets as needed.
Warning
Do not include node_modules
, scripts/postprocess-html.js
, .gitignore
, README.md
or LICENSE
in the copy:assets
script.
Create a folder called scripts
in your project root, and inside it, create a file named postprocess-html.js
with this content:
const fs = require("fs")
const path = require("path")
const { minify } = require("html-minifier-terser")
const docsDir = "docs"
const minifyOptions = {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
minifyCSS: true,
minifyJS: true,
}
;(async () => {
const files = fs.readdirSync(docsDir)
for (const file of files) {
if (file.endsWith(".html")) {
const filePath = path.join(docsDir, file)
const rawContent = fs.readFileSync(filePath, "utf-8")
try {
let content = await minify(rawContent, minifyOptions)
// Replace JS and CSS references
content = content
.replace(
/<script[^>]*\s(src=["']\.?\/index\.js["'])[^>]*type=["']module["'][^>]*><\/script>/i,
'<script src="./bundle.js" defer></script>'
)
.replace(
/<link[^>]+href=["']\.?\/style\.css["'][^>]*>/i,
'<link rel="stylesheet" href="./style.min.css">'
)
fs.writeFileSync(filePath, content, "utf-8")
} catch (err) {
console.error(`Failed to process ${file}:`, err)
}
}
}
})()
Note
For the current project, scripts/postprocess-html.js
already exists in the project root.
In the terminal, run:
npm run build
This will:
- Bundle and minify JS into
/docs/bundle.js
- Process and minify CSS into
/docs/style.min.css
- Copy assets (folders like
/css
,/img
,/fonts
, and files likefavicon.ico
) into/docs
- Copy all root HTML files into
/docs
- Modify the copied HTML files in
/docs
to reference the optimized JS and CSS files
Note
In the current project, the initial npm run build
has already been run. However, any subsequent edits made to files and assets in the root of the project will necessitate further builds.
Built with modern CSS features such as nesting, custom properties, and the :has()
pseudo-class, this project emphasizes modular, accessible, and maintainable styling.
The main style.css
file serves as an entry point and imports individual CSS modules using @import
. These are then processed by PostCSS (with postcss-import
, postcss-nesting
, and cssnano
) during the build.
The CSS has been split into separate modules, improving organization and reusability:
root.css
: Global CSS custom properties (--variables
) for themes, layout, and design tokens.base.css
: Global reset and base element styles.navigation.css
: Styles the primary navigation and hamburger menu.theme-toggler.css
: Styles the dark/light mode toggle control.loader.css
: Styles the full-screen loading overlay that appears while the page is initializing.project-specific.css
: Contains small, per-project overrides or additions.
Note
If you later revert to a single stylesheet without imports, the build process will continue to work seamlessly, as postcss-import
gracefully handles the absence of @import
statements.
Built with vanilla ES6 JavaScript, focusing on modern syntax and browser APIs, , then bundled, transpiled to ES2015, and minified for broad browser compatibility.
The JavaScript has been split into separate modules, improving code modularity:
module-placeholder.js
: Empty module, imported intoindex.js
.primary-navigation.js
andhamburger-button.js
: See Accessible Mobile Menu Git repositoryloader.js
: See Loader Git repositorytheme.js
: Handles theme toggling (light/dark mode) and local storage management.
The application includes a dark mode and light mode toggle:
- The current theme state is stored in local storage and applied automatically on page reload.
- Accessible buttons with appropriate ARIA attributes are used to improve usability.
Important
Remember to change const LOCAL_STORAGE_PREFIX
in js-modules/theme.js
to a unique identifier.
The site is fully navigable using tab keys and up/down arrows.
The application has been tested on the following platforms and browsers:
- Operating System: Windows 10
- Browsers:
- Google Chrome
- Mozilla Firefox
- Microsoft Edge
The layout and functionality have been verified in both browser and device simulation views to ensure responsiveness and usability.
- Clone or download the repository to your local machine.
- Open the project folder and start a simple HTTP server (e.g., using
Live Server
in VS Code or Python'shttp.server
module). - Open the project in a modern browser (e.g., Chrome, Firefox, or Edge).