{"id":30796948,"url":"https://github.com/chrisnajman/paginators","last_synced_at":"2026-04-16T11:02:45.991Z","repository":{"id":311443668,"uuid":"1043206226","full_name":"chrisnajman/paginators","owner":"chrisnajman","description":"A modular, lightweight and accessible pagination system for displaying content from various (and varied) JSON data sources. Supports multiple pages with a customisable number of items per page and dynamic pagination buttons.","archived":false,"fork":false,"pushed_at":"2025-09-14T13:21:19.000Z","size":152,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-14T15:10:41.492Z","etag":null,"topics":["accessibility","aria-attributes","chatgpt-free","css-flexbox","css-grid","css-nesting","cssnano","es6-modules","esbuild","history-management","html-css-javascript","html-minifier-terser","json","live-regions","loading-spinner","no-js","pagination","postcss","theme-switcher"],"latest_commit_sha":null,"homepage":"https://chrisnajman.github.io/paginators/","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-23T11:08:49.000Z","updated_at":"2025-09-14T13:21:22.000Z","dependencies_parsed_at":"2025-08-24T18:50:09.567Z","dependency_job_id":"25fccf38-0b55-4b82-933d-6143561aa018","html_url":"https://github.com/chrisnajman/paginators","commit_stats":null,"previous_names":["chrisnajman/paginators"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chrisnajman/paginators","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fpaginators","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fpaginators/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fpaginators/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fpaginators/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisnajman","download_url":"https://codeload.github.com/chrisnajman/paginators/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisnajman%2Fpaginators/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31882886,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T09:23:21.276Z","status":"ssl_error","status_checked_at":"2026-04-16T09:23:15.028Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["accessibility","aria-attributes","chatgpt-free","css-flexbox","css-grid","css-nesting","cssnano","es6-modules","esbuild","history-management","html-css-javascript","html-minifier-terser","json","live-regions","loading-spinner","no-js","pagination","postcss","theme-switcher"],"created_at":"2025-09-05T17:08:22.741Z","updated_at":"2026-04-16T11:02:45.985Z","avatar_url":"https://github.com/chrisnajman.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Paginators\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong id=\"menu\"\u003eMenu\u003c/strong\u003e\u003c/summary\u003e\n\n- [Introduction](#introduction)\n- [Features](#features)\n- [File Structure Overview](#file-structure-overview)\n- [HTML](#html)\n- [Paginator](#paginator)\n- [How to Add a New JSON Type](#how-to-add-a-new-json-type)\n- [Link Handling in Templates](#link-handling-in-templates)\n- [Email Address Handling in Templates](#email-address-handling-in-templates)\n- [Telephone Number Handling in Templates](#telephone-number-handling-in-templates)\n- [CSS](#css)\n- [Use of ChatGPT (Free version)](#use-of-chatgpt-free-version)\n- [Accessibility](#accessibility)\n- [Theme Toggling](#theme-toggling)\n- [Testing and Compatibility](#testing-and-compatibility)\n- [How to Run](#how-to-run)\n- [Build \u0026 Deployment Setup for `/docs` Folder](#build--deployment-setup-for-docs-folder)\n\n\u003c/details\u003e\n\n## Introduction\n\nA modular, lightweight and accessible pagination system for displaying content from various (and varied) JSON data sources. Supports multiple pages with a customisable number of items per page and dynamic pagination buttons.\n\n[View on GitPage](https://chrisnajman.github.io/paginators)\n\n[Back to menu](#menu)\n\n---\n\n## Features\n\n- Separate JSON files for different content types (`pages.json`, `posts.json`, `users.json`, etc.).\n- HTML `\u003ctemplate\u003e` elements for rendering content dynamically. Note: `\u003ctemplate\u003e` element is **required**.\n- Modular JS with `render-content.js`, `render-[type]-content.js`, and `paginator.js`.\n- URL syncing and browser back/forward support.\n- Optional preprocessing/normalization of JSON fields (`normalise-data.js`).\n- Supports nested data, arrays, and custom transformations per content type.\n- Pagination button rendering with max visible buttons and dynamic updates.\n- Paginator is only rendered if there are multiple pages.\n\n[Back to menu](#menu)\n\n---\n\n## File Structure Overview\n\nRelevant paginator-related files are listed below.\n\n- `index.html`, `posts.html`, `users.html` — HTML pages displaying paginated content via `\u003ctemplate\u003e` element.\n- `index.js`: Loads main JavaScript modules. Displays them conditionally, according to relevant HTML `body` id.\n- **`json/`**\n  - `pages.json`: Array of objects, with `\"content\"` field containing escaped HTML.\n  - `posts.json`: Object with key of `\"posts\"` containing an array of objects.\n  - `users.json`: Array of objects with two-level nesting.\n- **`js-modules/`**\n  - `globals.js`: Global constants (e.g., max visible pagination buttons, HTML `body` ids, etc).\n  - `loader.js`: Loader animation logic.\n  - `page-number.js`: (Optional) display page number in `\u003ch1\u003e`\n  - `details-page-nav.js`: Close `details` if you click outside it.\n  - **`pagination/`**\n    - `paginator.js`: Main paginator initialization logic, handles updates, renders content, pagination buttons, and URL sync.\n    - **`components/`**\n      - `live-region.js`: Accessibility: announces page changes.\n      - `render-page-buttons.js`: Generates pagination buttons.\n      - `render-pages-content.js`: Pages-type renderer that calls `render-content.js`.\n      - `render-posts-content.js`: Posts-type renderer that calls `render-content.js`.\n      - `render-users-content.js`: Users-type renderer that calls `render-content.js`.\n      - `render-content.js`: Generic renderer for JSON arrays to templates.\n      - `normalise-data.js`: Generic JSON preprocessing / normalization utility.\n      - **`render-customisations/`**\n        - `normalise-users-data.js`: Example of per-type custom transformations.\n    - **`helpers/`**\n      - `redirect_404.js`: Redirect to `404.html` if page number is out of range.\n      - `set-loader-timeout.js`: Utility for controlling loader display timing.\n      - `url-sync.js`: Syncs current page with URL and supports back/forward.\n  - **`page-item-types/`**: Item type-specific code that fetches JSON and calls `initPaginator`.\n    - `pages.js`\n    - `posts.js`\n    - `users.js`\n\n### Other\n\n- `theme.js`: Handles theme toggling (light/dark mode) and local storage management.\n- `about.html`\n\n[Back to menu](#menu)\n\n---\n\n## HTML\n\n\u003e [!IMPORTANT]\n\u003e To successfully output JSON (via `render-content.js`), a `\u003ctemplate\u003e` is used to output each instance of 'page', 'post' and 'user' content types.\n\n[Back to menu](#menu)\n\n---\n\n## Paginator\n\nThe paginator makes it easy to move through long lists of items without overwhelming the page. You can choose how many page buttons to display at once — for example, showing just a few nearby pages or spreading out more options.\n\nWhen there are too many pages to fit, the paginator automatically adds ellipsis (…) to show that more pages exist before or after the visible range. This keeps the navigation clean and simple, while still giving quick access to the first page, last page, and the pages closest to where you are.\n\nAfter a page loads, focus is set on the page `\u003ch1\u003e`\n\n[Back to menu](#menu)\n\n---\n\n## How to Add a New JSON Type\n\n### 1. Add the JSON File\n\nPlace your file in the `/json/` folder.\n\n**Example**: `json/products.json`\n\n**Structure**: an array of objects or an object containing an array under a specific key (e.g., \"items\").\n\n```json\n[\n  {\n    \"id\": 1,\n    \"title\": \"Digital camera\",\n    \"price\": \"£199.90\",\n    \"manufacturer\": \"Sony\"\n  },\n...\n]\n```\n\n### 2. Create a New HTML Page: `products.html`\n\n1. Add an id to the `body` tag:\n\n```javascript\n\u003cbody id=\"products-pagination\" class=\"flow\"\u003e\n```\n\n2. Add a `\u003ctemplate\u003e` to `products.html`:\n\n```html\n\u003ctemplate id=\"article-template-product\"\u003e\n  \u003carticle\u003e\n    \u003ch2\u003e\u003cspan data-id\u003e\u003c/span\u003e. \u003cspan data-title\u003e\u003c/span\u003e\u003c/h2\u003e\n    \u003cp\u003ePrice: \u003cspan data-price\u003e\u003c/span\u003e\u003c/p\u003e\n    \u003cp\u003eManufacturer: \u003cspan data-manufacturer\u003e\u003c/span\u003e\u003c/p\u003e\n  \u003c/article\u003e\n\u003c/template\u003e\n```\n\n3. Add Paginator and Pagination Containers\n\nMake sure your HTML page has a container and paginator element:\n\n```html\n\u003cdiv id=\"products-page-container\"\u003e\u003c/div\u003e\n\u003cdiv id=\"paginator\"\u003e\u003c/div\u003e\n```\n\n## 3. Create a Type-Specific Renderer\n\nCreate a new JS module in `js-modules/pagination/components/`, e.g., `render-products-content.js`:\n\n```javascript\nimport renderContent from \"./render-content.js\"\nexport default function renderProductsContent(\n  data,\n  template,\n  containerId,\n  page,\n  itemsPerPage\n) {\n  renderContent(data, template, containerId, {\n    page,\n    itemsPerPage,\n    contentKeys: [], // optional keys that contain escaped HTML - see 'pages.json' for an example.\n    itemsKey: null, // or the array key if JSON is { \"items\": [...] }\n  })\n}\n```\n\n**Optional**: preprocess your JSON via `normalise-data.js` or a custom file in `render-customisations/normalise-products-data.js` for things like formatting, splitting strings, etc.\n\n### 4. Create a Page-Specific Loader Script\n\n**Example**: `js-modules/products/products.js`\n\n```javascript\nimport { maxVisiblePaginationButtons } from \"../globals.js\"\nimport initPaginator from \"../pagination/paginator.js\"\n\nexport default async function loadProducts() {\n  try {\n    const res = await fetch(\"./json/products.json\")\n    if (!res.ok) throw new Error(`Failed to load products.json: ${res.status}`)\n    const data = await res.json()\n\n    const templateId = \"article-template-product\"\n    const containerId = \"products-page-container\"\n\n    initPaginator({\n      data,\n      templateId,\n      containerId,\n      itemsPerPage: 5, // Modify as required\n      // maxButtons: Modify as required, e.g. 'maxButtons: 7'.\n      // !If you set the value equal to or greater than the corresponding number of entries in users.json,\n      //  there won't be any ellipsis.\n      maxButtons: maxVisiblePaginationButtons, // Default = 5\n      itemsKey: null, // or \"items\" if JSON has a key\n    })\n  } catch (err) {\n    console.error(err)\n  }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", loadProducts)\n```\n\n### 5. Update `globals.js`\n\nAdd the products `body` id to the `PAGE_TYPES` variable:\n\n```javascript\nexport const PAGE_TYPES = {\n  PAGES: \"pages-pagination\",\n  POSTS: \"posts-pagination\",\n  USERS: \"users-pagination\",\n  PRODUCTS: \"products-pagination\", // Products\n}\n\n// Edit if defaultLoaderTimeout is not long enough/too long:\nexport const loaderTimeouts = {\n  [PAGE_TYPES.PAGES]: 500, // Loads images so more time required\n  // These can use defaultLoaderTimeout = 250 for now, but uncomment and change,\n  // if required\n  // [PAGE_TYPES.POSTS]: 250,\n  // [PAGE_TYPES.USERS]: 250,\n  // [PAGE_TYPES.PRODUCTS]: 250, // Products\n}\n```\n\n### 6. Update `paginator.js`\n\n- At the top of the file, add:\n\n```javascript\nimport renderProductsContent from \"./components/render-products-content.js\"\n```\n\n- Then, within `requestAnimationFrame()` add:\n\n```javascript\nif (bodyId === PAGE_TYPES.PRODUCTS)\n  renderProductsContent(data, template, containerId, page, itemsPerPage)\n```\n\n### 7. Update `index.js`\n\n- At the top of the file, add:\n\n```javascript\nimport products from \"./js-modules/page-item-types/products.js\"\n```\n\n- Then add:\n\n```javascript\nelse if (bodyId === PAGE_TYPES.PRODUCTS) {\n  products()\n\n\n```\n\nto the end of the `if` statement.\n\n### 8. Finish\n\nThe new type is now fully integrated into the paginator system.\n\nThis pattern keeps everything modular and the core `render-content.js` and `paginator.js` remain untouched.\n\n[Back to menu](#menu)\n\n---\n\n## Link Handling in Templates\n\n#### Example\n\n**JSON field**\n\n```json\n{\n  \"website\": \"https://example.com\",\n  \"url\": \"https://another.com\"\n}\n```\n\n**HTML Template**:\n\nNote the difference between the placement of the `data-[key]` in the following:\n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    \u003c!-- Inner content is hardcoded so `data-website` goes in the 'a' tag itself --\u003e\n    \u003ca\n      data-website\n      data-href=\"website\"\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n    \u003e\n      Visit website\n    \u003c/a\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    \u003c!-- No data-url in the 'a' tag because there's a 'span' to output inner content --\u003e\n    \u003ca\n      data-href=\"url\"\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n    \u003e\n      \u003cspan data-url\u003e\u003c/span\u003e\n      \u003c!-- data-url goes in the 'span' tag --\u003e\n    \u003c/a\u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n**Rendered HTML**:\n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    \u003ca\n      data-website\n      data-href=\"https://example.com\"\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n    \u003e\n      Visit website\n    \u003c/a\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    \u003ca\n      data-href=\"https://another.com\"\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n    \u003e\n      \u003cspan data-url\u003e\u003c/span\u003e\n    \u003c/a\u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n### Explanation\n\n- `data-[field-name]` → **required**. This is the `data-` attribute that `render-content.js` uses to find the element in the template.\n- `data-href=\"[field-name]\"` → **required**. This tells `render-content.js` which JSON key to use to populate the `href` attribute of the link.\n- Inner content ([Link text or inner span]) → **required**. Can be e.g. a `\u003cspan data-[key]\u003e` linked to another JSON key, or just plain text. `render-content.js` **does not set this automatically**.\n- `target=\"_blank\"` and `rel=\"noopener noreferrer\"` → **optional**, for opening external links safely.\n\nThe `render-content.js` script will set the `href` of each `\u003ca\u003e` automatically from the corresponding JSON key.\n\n[Back to menu](#menu)\n\n---\n\n## Email Address Handling in Templates\n\n**JSON field**\n\n```json\n\"email\": \"name@name.com\",\n\"email-address\": \"companyname@ecompanyname.com\"\n\n```\n\n**HTML Template**:\n\n```html\n\u003cli\u003e\n  \u003ca\n    data-email\n    data-mailto=\"email\"\n  \u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n  \u003ca\n    data-email-address\n    data-mailto=\"email-address\"\n  \u003e\u003c/a\u003e\n\u003c/li\u003e\n```\n\n**Rendered HTML**:\n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    \u003ca\n      data-email=\"\"\n      data-mailto=\"email\"\n      href=\"mailto:name@name.com\"\n      \u003ename@name.com\u003c/a\n    \u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    \u003ca\n      data-email-address=\"\"\n      data-mailto=\"email-address\"\n      href=\"mailto:ncompanyname@ecompanyname.com\"\n      \u003ecompanyname@ecompanyname.com\u003c/a\n    \u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n### Explanation\n\n- E.g. `data-mailto=\"email\"` → tells `render-content.js` to look up the JSON field e.g. `\"email\"` and build a `mailto:` link.\n- E.g. `data-email` → required to bind the element to the JSON field e.g. `\"email\"`.\n- The inner text of the `\u003ca\u003e `will automatically display the email address.\n\n[Back to menu](#menu)\n\n---\n\n## Telephone Number Handling in Templates\n\n**JSON field**\n\n```json\n\"phone\": \"+44 1234 567890\",\n\"telno\": \"+44 3333 444444\"\n\n```\n\n**HTML Template**:\n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    Phone:\n    \u003ca\n      data-phone\n      data-tel=\"phone\"\n    \u003e\u003c/a\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    Telno:\n    \u003ca\n      data-telno\n      data-tel=\"telno\"\n    \u003e\u003c/a\u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n**Rendered HTML**:\n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    \u003ca\n      data-phone=\"\"\n      data-tel=\"phone\"\n      href=\"tel:+441234567890\"\n      \u003e+44 1234 567890\n    \u003c/a\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\n    \u003ca\n      data-telno=\"\"\n      data-tel=\"telno\"\n      href=\"tel:+443333444444\"\n      \u003e+44 3333 444444\n    \u003c/a\u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\n### Explanation\n\n- E.g. `data-tel-link=\"tel\"` → tells `render-content.js` to look up the JSON field e.g. `\"tel\"` and build a `tel:` link.\n- E.g.`data-tel` → required to bind the element to the JSON field e.g. `\"tel\"`.\n- The inner text of the `\u003ca\u003e `will automatically display the email address.\n\n[Back to menu](#menu)\n\n---\n\n## CSS\n\n- `style.css`: `@imports` all files in `/css/` folder.\n- **`css/`**\n  - `root.css`\n  - `base.css`\n  - `loader.css`\n  - `navigation.css`\n  - `redirect.css`\n  - `details-transition.css`\n  - `theme-toggle.css`\n  - **`pagination/`**\n    - `index.css`: `@imports` all files in `pagination/` folder.\n    - `shared.css`\n    - `paginator.css`\n    - `pages.css`\n    - `posts.css`\n    - `users.css`\n\n---\n\n## Use of ChatGPT (Free version)\n\nUsually, I only consult the AI when I run into insurmountable difficulties with the JavaScript. This time, as an experiment, I decided to let ChatGPT handle **all** of the JavaScript from scratch, only prodding it when its output was faulty.\n\nThis resulted in a huge thread (about 20,000 lines) generated over several days. I ran into difficulties about halfway through: the size of the thread by that time was taxing the capabilities of the server and the page often became inactive. By the time it reached the 20,000 mark, it was almost totally inert, requiring that the browser be shut down after posting a question, then reopening it a few minutes later to see the response.\n\nHowever, ChatGPT came through in the end, and after much to-ing and fro-ing, it answered all my questions and fixed all the bugs.\n\nThe thread may be fat, but the generated code is pretty lean.\n\n[Back to menu](#menu)\n\n---\n\n## Accessibility\n\nThe site includes the following accessibility enhancements:\n\n- Fully keyboard-navigable using tab keys.\n- ARIA roles and attributes are implemented throughout (e.g. for navigation and live announcements).\n- A visually hidden skip link is provided for screen reader users.\n- An ARIA live region (`\u003cdiv id=\"live-region\"\u003e`) announces new content loaded when navigating between pages.\n\n### No JS\n\nIf JavaScript is disabled, a `\u003cnoscript\u003e` message is displayed. Additionally, the loading animation and theme-toggler cease to function.\n\n[Back to menu](#menu)\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[Back to menu](#menu)\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/11\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[Back to menu](#menu)\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[Back to menu](#menu)\n\n---\n\n## Build \u0026 Deployment Setup for `/docs` Folder\n\nIf you want to deploy a minified version of this project to **GitHub Pages**, read on.\n\n### 1. Install Required Packages\n\nRun this once in your project root to install dev dependencies:\n\n```bash\nnpm install\n```\n\n### 2. Run the full build process\n\nIn the terminal, run:\n\n```bash\nnpm run build\n```\n\n### 3. Deploy to GitHub Pages\n\nOnce you've created a repository and pushed the files,\n\n- go to `https://github.com/[your-name]/[your-project-name]/settings/pages`.\n- Under \"Build and deployment \u003e Branch\" make sure you set the branch to `main` and folder to `/docs`.\n- Click \"Save\".\n\n\u003e [!NOTE]\n\u003e For a detailed description of the build process, configuration files and npm packages see my [GitHub Pages Optimised Build](https://github.com/chrisnajman/github-pages-optimised-build).\n\n[Back to menu](#menu)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fpaginators","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisnajman%2Fpaginators","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisnajman%2Fpaginators/lists"}