{"id":29547241,"url":"https://github.com/chrisnajman/github-pages-optimised-build","last_synced_at":"2025-10-11T17:45:44.014Z","repository":{"id":303192977,"uuid":"1014679993","full_name":"chrisnajman/github-pages-optimised-build","owner":"chrisnajman","description":"GitHub Pages site template built from the '/docs' folder using an automated front-end build process with minified HTML, CSS and (transpiled) JavaScript.","archived":false,"fork":false,"pushed_at":"2025-07-06T08:50:37.000Z","size":308,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-06T09:21:21.089Z","etag":null,"topics":["css-nesting","cssnano","es6-modules","esbuild","html-css-javascript","html-minifier-terser","postcss","postcss-cli","postcss-import","postcss-nesting","shx"],"latest_commit_sha":null,"homepage":"https://chrisnajman.github.io/github-pages-optimised-build/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisnajman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-06T07:38:53.000Z","updated_at":"2025-07-06T08:50:40.000Z","dependencies_parsed_at":"2025-07-06T09:21:34.853Z","dependency_job_id":null,"html_url":"https://github.com/chrisnajman/github-pages-optimised-build","commit_stats":null,"previous_names":["chrisnajman/github-pages-optimised-build"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chrisnajman/github-pages-optimised-build","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fgithub-pages-optimised-build","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fgithub-pages-optimised-build/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fgithub-pages-optimised-build/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fgithub-pages-optimised-build/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisnajman","download_url":"https://codeload.github.com/chrisnajman/github-pages-optimised-build/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fgithub-pages-optimised-build/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265654551,"owners_count":23805932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css-nesting","cssnano","es6-modules","esbuild","html-css-javascript","html-minifier-terser","postcss","postcss-cli","postcss-import","postcss-nesting","shx"],"created_at":"2025-07-17T20:00:54.256Z","updated_at":"2025-10-11T17:45:43.996Z","avatar_url":"https://github.com/chrisnajman.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Pages Optimised Build\n\n\u003e [!IMPORTANT]  \n\u003e The following assumes you have Node.js installed on your machine.\n\n**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:\n\n- Automatically bundle and minify JavaScript, process and minify CSS (including nesting), and minify HTML\n- Save all production-ready assets into a `/docs` folder\n- Configure GitHub Pages to serve the site directly from `/docs`\n\nThis 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`.\n\nThe build process is fully scriptable using NPM scripts, with no reliance on tools like Webpack or Gulp.\n\n[View on GitPage](https://chrisnajman.github.io/github-pages-optimised-build)\n\n---\n\n### Why `/docs`?\n\n- The `/docs` folder contains optimized production-ready assets, including:\n  - `bundle.js`: the JavaScript bundled into a single file (no modules)\n  - `style.min.css`: the CSS processed with nesting flattened and minified\n- The original source files (`index.js`, files in `js-modules/`, `style.css` with nesting, etc.) remain in the project root for easier development and editing.\n- GitHub Pages is configured to serve the site from the `/docs` folder instead of the root, so your published site uses these optimized files.\n\n\u003e [!IMPORTANT]\n\u003e The build process only supports a **flat** HTML structure, i.e. all HTML pages must sit in the root of the project (no folders).\n\n\u003e [!IMPORTANT]\n\u003e When publishing to GitHub Pages, make sure the Pages setting is configured to serve from the `/docs` folder on the `main` branch.\n\n---\n\n## Build \u0026 Deployment Setup for `/docs` Folder\n\n### 1. Install Required Packages\n\nRun this once in your project root to install dev dependencies:\n\n```bash\nnpm install --save-dev esbuild html-minifier-terser postcss postcss-cli postcss-import postcss-nesting cssnano shx\n```\n\n- **esbuild** — bundles, transpiles (to ES2015), and minifies JS\n- **html-minifier-terser** — minifies HTML files in the `/docs` folder after copying and post-processing\n- **postcss, postcss-cli** — for CSS processing\n- **postcss-import** — enables @import rules in CSS for modular stylesheets\n- **postcss-nesting** — enables CSS nesting support\n- **postcss-url** — processes and rewrites URLs in CSS (e.g., copying assets and adjusting paths) for correct referencing in the build output\n- **cssnano** — minifies CSS\n- **shx** — cross-platform CLI utility (copy, rm, etc.)\n\n\u003e [!NOTE]\n\u003e 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).\n\n---\n\n### 2. Create `postcss.config.js` in project root\n\n```js\nmodule.exports = {\n  plugins: [\n    require(\"postcss-import\"),\n    require(\"postcss-nesting\"),\n    require(\"postcss-url\")({\n      url: \"copy\",\n      assetsPath: \"img\",\n      useHash: true,\n    }),\n    require(\"cssnano\")({ preset: \"default\" }),\n  ],\n}}\n```\n\n\u003e [!NOTE]\n\u003e For the current project, `postcss.config.js` already exists in the project root.\n\n---\n\n### 3. Add build scripts to `package.json`\n\n```json\n{\n  \"scripts\": {\n    \"build:js\": \"esbuild index.js --bundle --minify --target=es2015 --outfile=docs/bundle.js\", // bundles, transpiles to ES2015, and minifies\n    \"build:css\": \"postcss style.css --output docs/style.min.css\",\n    \"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/\n    \"copy:html\": \"shx cp *.html docs/\",\n    \"postprocess:html\": \"node scripts/postprocess-html.js\",\n    \"build\": \"npm run build:js \u0026\u0026 npm run build:css \u0026\u0026 npm run copy:assets \u0026\u0026 npm run copy:html \u0026\u0026 npm run postprocess:html\"\n  }\n}\n```\n\n\u003e [!NOTE]\n\u003e For the current project, build scripts have already been added to `package.json`.\n\n\u003e [!NOTE]\n\u003e The `copy:assets` script includes the required files for the current project. You can edit it to include additional folders or assets as needed.\n\n\u003e [!WARNING]\n\u003e Do **not** include `node_modules`, `scripts/postprocess-html.js`, `.gitignore`, `README.md` or `LICENSE` in the `copy:assets` script.\n\n---\n\n### 4. Create the HTML post-processing script\n\nCreate a folder called `scripts` in your project root, and inside it, create a file named `postprocess-html.js` with this content:\n\n```js\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst { minify } = require(\"html-minifier-terser\")\n\nconst docsDir = \"docs\"\n\nconst minifyOptions = {\n  collapseWhitespace: true,\n  removeComments: true,\n  removeRedundantAttributes: true,\n  removeEmptyAttributes: true,\n  minifyCSS: true,\n  minifyJS: true,\n}\n\n;(async () =\u003e {\n  const files = fs.readdirSync(docsDir)\n\n  for (const file of files) {\n    if (file.endsWith(\".html\")) {\n      const filePath = path.join(docsDir, file)\n      const rawContent = fs.readFileSync(filePath, \"utf-8\")\n\n      try {\n        let content = await minify(rawContent, minifyOptions)\n\n        // Replace JS and CSS references\n        content = content\n          .replace(\n            /\u003cscript[^\u003e]*\\s(src=[\"']\\.?\\/index\\.js[\"'])[^\u003e]*type=[\"']module[\"'][^\u003e]*\u003e\u003c\\/script\u003e/i,\n            '\u003cscript src=\"./bundle.js\" defer\u003e\u003c/script\u003e'\n          )\n\n          .replace(\n            /\u003clink[^\u003e]+href=[\"']\\.?\\/style\\.css[\"'][^\u003e]*\u003e/i,\n            '\u003clink rel=\"stylesheet\" href=\"./style.min.css\"\u003e'\n          )\n\n        fs.writeFileSync(filePath, content, \"utf-8\")\n      } catch (err) {\n        console.error(`Failed to process ${file}:`, err)\n      }\n    }\n  }\n})()\n```\n\n\u003e [!NOTE]\n\u003e For the current project, `scripts/postprocess-html.js` already exists in the project root.\n\n---\n\n### 5. Run the full build process\n\nIn the terminal, run:\n\n```bash\nnpm run build\n\n```\n\nThis will:\n\n- Bundle and minify JS into `/docs/bundle.js`\n- Process and minify CSS into `/docs/style.min.css`\n- Copy assets (folders like `/css`, `/img`, `/fonts`, and files like `favicon.ico`) into `/docs`\n- Copy all root HTML files into `/docs`\n- Modify the copied HTML files in `/docs` to reference the optimized JS and CSS files\n\n\u003e [!NOTE]\n\u003e 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.\n\n---\n\n## CSS\n\nBuilt with modern CSS features such as nesting, custom properties, and the `:has()` pseudo-class, this project emphasizes modular, accessible, and maintainable styling.\n\nThe 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.\n\nThe CSS has been split into separate modules, improving organization and reusability:\n\n- `root.css`: Global CSS custom properties (`--variables`) for themes, layout, and design tokens.\n- `base.css`: Global reset and base element styles.\n- `navigation.css`: Styles the primary navigation and hamburger menu.\n- `theme-toggler.css`: Styles the dark/light mode toggle control.\n- `loader.css`: Styles the full-screen loading overlay that appears while the page is initializing.\n- `project-specific.css`: Contains small, per-project overrides or additions.\n\n\u003e [!NOTE]\n\u003e 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.\n\n---\n\n## JavaScript\n\nBuilt with **vanilla ES6 JavaScript**, focusing on modern syntax and browser APIs, , then bundled, transpiled to ES2015, and minified for broad browser compatibility.\n\nThe JavaScript has been split into separate modules, improving code modularity:\n\n- `module-placeholder.js`: Empty module, imported into `index.js`.\n- `primary-navigation.js` and `hamburger-button.js`: See [Accessible Mobile Menu Git repository](https://github.com/chrisnajman/accessible-mobile-menu)\n- `loader.js`: Displays a loader animation until the page is fully rendered, then removes the loader and announces readiness for screen readers.\n- `theme.js`: Handles theme toggling (light/dark mode) and local storage management.\n\n---\n\n## Accessibility\n\nThe site is fully navigable using tab keys and up/down arrows.\n\n---\n\n## Theme Toggling\n\nThe application includes a dark mode and light mode toggle:\n\n- The current theme state is stored in **local storage** and applied automatically on page reload.\n- Accessible buttons with appropriate ARIA attributes are used to improve usability.\n\n\u003e [!IMPORTANT]\n\u003e Remember to change `const LOCAL_STORAGE_PREFIX` in `js-modules/theme.js` to a unique identifier.\n\n---\n\n## Testing and Compatibility\n\nThe application has been tested on the following platforms and browsers:\n\n- **Operating System**: Windows 10\n- **Browsers**:\n  - Google Chrome\n  - Mozilla Firefox\n  - Microsoft Edge\n\n### Device View Testing\n\nThe layout and functionality have been verified in both browser and device simulation views to ensure responsiveness and usability.\n\n---\n\n## How to Run\n\n1. Clone or download the repository to your local machine.\n2. Open the project folder and start a simple HTTP server (e.g., using `Live Server` in VS Code or Python's `http.server` module).\n3. Open the project in a modern browser (e.g., Chrome, Firefox, or Edge).\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fgithub-pages-optimised-build","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisnajman%2Fgithub-pages-optimised-build","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fgithub-pages-optimised-build/lists"}