049

Documentation Site Template

£10

A complete six-page documentation site on a clean near-white reading surface, with a proper dark theme and a cool slate alternate shipped as one-attribute palette swaps: fixed top bar, cross-linked sidebar tree, article column and scrollspy "On this page" rail on every page, built around a Cmd+K command palette that fuzzy-searches an index read live from the site's navigation and the current page's headings.

ScrollTrigger Lenis intermediate
7 more details
command-palettefuzzy-searchscrollspysmooth-scrolltabsclick-toggle 3 colour palettes
Documentation Site Template - website template preview

This template reads better at your own screen size than in the frame below:

About this template

A documentation site you deploy as your documentation site, not a landing page about one. The template is the docs dashboard itself: a fixed top bar with wordmark, version badge and search affordance, a left sidebar tree with collapsible groups, a reading column, and a right-hand "On this page" rail whose marker follows you down the article. It ships as a complete six-page site for Cairn, a fictional static-site build CLI: an introduction, a quickstart, full configuration and CLI references, a themes guide and a deploying guide, cross-linked through the sidebar and a prev/next pager on every page. Between them the pages carry every docs pattern you need to copy: a tabbed install block, styled file trees, options and flags tables, CI recipes, and note and warning callouts.

Read the full template overview

The art direction is a calm reading surface, the kind of docs site you can read for an hour: a flat near-white ground with no texture or overlay, calm near-black ink, one deep viridian green for links and active states, and dark code panels, the only dark mass on the light page, because code is content rather than decoration. Three palettes ship: Paper is the default, Dark is a comfortable green-grey dark reading theme with the viridian lifted to mint, and Slate is a cool blue-grey light with a deep indigo accent. Each alternate is a data-palette block in the stylesheet restating every token, so switching is one attribute on the html tag. Chakra Petch gives the headings a squared technical voice, Wix Madefor Text carries the reading, and Ubuntu Sans Mono sets every code span, kbd hint and the version badge.

The signature is the command palette, present on every page. Cmd+K or Ctrl+K opens a centred dialog with a GSAP entrance, live fuzzy filtering with match highlighting, and full keyboard support. Its index is built from the DOM at load: the sidebar's links become page entries that navigate between pages, the current page's headings become scroll targets, and scripted actions round it out, so renaming or deleting a page or section can never leave a stale search result. Choosing a heading smooth-scrolls to it and flashes it briefly so the eye lands in the right place; choosing a page follows its sidebar link.

To make it yours, replace the articles and the sidebar tree with your pages: the palette, the scrollspy rail and the copy buttons all read the DOM, so they pick your structure up with no configuration. Choose Dark or Slate by setting data-palette on the html tag, or reskin entirely from the one token block with RGB triples; the site deploys as static files with no build step.

What's included

16 items
  • A complete six-page docs site, not a single demo page: introduction, quickstart, configuration reference, themes guide, deploying guide and CLI reference, cross-linked through the sidebar with the current page marked on every one
  • Complete docs-site chrome on every page: fixed top bar with version badge and search affordance, collapsible sidebar tree, breadcrumbs, reading column, scrollspy "On this page" rail, and a prev and next pager wired across the real page order
  • Signature command palette on Cmd+K / Ctrl+K: GSAP scale-and-fade entrance, backdrop dim, live fuzzy filtering with match highlighting, arrow-key navigation, Enter to jump, Escape to close, focus trapped and returned
  • The palette's index is read from the DOM: sidebar links become cross-page navigation entries, the current page's headings become scroll targets, and scripted actions appear only on pages that can perform them, so search can never drift from the site it searches
  • Scrollspy rail with a sliding marker, its entries and positions read from the markup, so deleted sections just shrink the rail
  • Choosing a palette or rail entry smooth-scrolls under the fixed bar via Lenis and flashes the target heading
  • Copy buttons on every code block, injected by the script so a no-JS page simply never has them, with a Copied state on the dark panel
  • Tabbed package-manager install block as a real ARIA tablist; without JavaScript all three panels stack, each naming itself
  • Complete shipped content for a fictional CLI across all six pages: tabbed install block, styled file trees, configuration and flags tables, a CI recipe, redirects examples, and note and warning callouts
  • Three measured palettes, each with its own display face (technical sans, mono, grotesk), swapped from one data-palette attribute on every page
  • A flat, undecorated reading ground in every palette, dark restrained code panels, and a signal red held to the warning border only
  • Sidebar groups are native open-by-default details, and the sidebar becomes an overlay drawer on phones; without JavaScript it joins the document flow so nothing is unreachable
  • Skip link, semantic landmarks, real table markup for the options, kbd-styled shortcut hints, and visible measured focus rings throughout
  • Reduced motion gets an instant palette, jumping marker and no smooth scroll, via both the CSS media query and the script's own motion branch
  • Reskins from one token block: every translucent value derives from RGB triples, with measured contrast tables for all three palettes in the stylesheet header
  • Lenis smooth scrolling wired on the shared GSAP clock and fully optional; the sidebar scrolls natively via data-lenis-prevent

Perfect for

5 use cases
  • Product and API documentation for developer tools, CLIs and libraries
  • Internal engineering handbooks, runbooks and onboarding guides
  • Open-source project docs that need to look maintained without a docs framework
  • Knowledge bases and help centres for technical products
  • Learning pattern: an accessible command palette with DOM-derived search and a GSAP-driven scrollspy

How it works

5 sections

The command palette

At load the script walks the sidebar links and the current page's h2 and h3 headings and builds the search index from what it finds, adding scripted actions; the copy-install action only exists on pages that carry the install block. Cmd+K, Ctrl+K or the search button opens the dialog with a backdrop fade and a scale-and-fade entrance; typing runs a subsequence fuzzy match that scores word starts and tight runs, and the matched characters render as highlights. Enter on a heading closes the palette, scrolls to it beneath the fixed bar, and briefly flashes it; Enter on a page entry follows that page's sidebar link, so the palette is also the site's navigation. Focus is trapped on the input while open and returned to the opener on close.

Pages and navigation

Six pages ship as plain HTML files sharing one assets folder, so the whole site deploys as static files with no build step. Every page carries the same chrome; the sidebar holds real relative links with the current page marked aria-current, and a prev and next pager runs across the real page order, so with JavaScript disabled navigation is ordinary links and nothing else. One page-agnostic script runs everywhere: every lookup is guarded and the palette index, scrollspy and copy buttons are read from whatever the current page contains, which is also what makes adding a page a copy-and-edit job rather than a configuration change.

Scrollspy and smooth scroll

Lenis drives window scrolling from the GSAP ticker so ScrollTrigger and the scroll position share one clock, and it is opt-out via a data attribute, a URL parameter, or reduced motion. A single ScrollTrigger drives the spy: the last heading above a line 30% down the viewport is active, the foot of the page always lights the final entry even when its section is too short to reach the line, and a 2px marker tweens to the active link. Entries pair rail links with headings by id and skip any whose heading is gone, so removing an article section removes its rail entry with no other edit. The sidebar carries data-lenis-prevent and scrolls natively inside its fixed panel.

Docs chrome, not content motion

The article itself never animates: no scroll reveals, no staggered paragraphs, because documentation is for reading and re-reading. Motion is spent on the chrome instead: the palette entrance, the sliding rail marker, the tab swap in the install block, the copy button's Copied state, and the drawer on phones. Reduced motion swaps each of those for an instant state through both the CSS media query and the script's own motion branch.

Without JavaScript

The page is a plain document that JavaScript upgrades. The search button and menu button are gated behind a class set synchronously in the head and removed if GSAP fails to load, so a blocked CDN shows the full static page. Sidebar groups are native details elements, open by default; the install tabs stack as three labelled blocks; the rail is plain anchor links; copy buttons are injected at runtime so they never exist to fail. Nothing on the page is pre-hidden.

Plugins ScrollTrigger
Difficulty Intermediate
Smooth scroll Lenis integration
Colour palettes 3 ready-made palettes, each measured for AA contrast. Switch with one attribute, or copy the values you want into :root. Try them in the preview above.
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this template's demo (6 pages, lowest score shown), 9 September 2026. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Questions about this template

Is this really six pages, or one page dressed up?

Six real, cross-linked HTML pages: introduction, quickstart, configuration reference, themes guide, deploying guide and CLI reference, each with the sidebar marking the current page and a prev/next pager wired across the real page order. It is a docs site, not a docs-page demo.

Does the Cmd+K search need an index I have to maintain?

No. The palette reads its index from the DOM: sidebar links become cross-page navigation entries and the current page's headings become scroll targets. Search therefore cannot drift from the site it searches, and adding a page or a heading needs no separate index file.

Do I have to adopt a docs framework?

No, and that is the reason to pick it over Docusaurus or MkDocs. It is static HTML you edit directly, which suits an open-source project that wants docs looking maintained without taking on another toolchain. The trade is that you write pages by hand rather than in Markdown.

Is there a dark theme?

Three palettes ship: a clean near-white default, a comfortable dark reading theme and a cool slate light, each a token block applied with one attribute on the html tag. Measured contrast tables for all three are in the stylesheet header, so switching does not quietly break legibility.

Does the shipped content have to be replaced before launch?

Yes, all six pages carry complete written docs for a fictional CLI: tabbed install block, file trees, configuration and flags tables, a CI recipe, redirects examples and callouts. That is there so you can see the patterns working against real content, but shipping it would document somebody else's tool.

Paid template

Purchase to unlock the code.

Buying Documentation Site Template opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£10 Standard license, unlimited projects

Browse free effects

Documentation

What's Included

Code snippet omitted: it ships with the download.

No build step and no images. Open any page and it runs; every page shares the one assets/ folder.

The reading order (which is also the prev/next pager order) is: Introduction → Quickstart → Configuration → Themes → Deploying → CLI reference. The Quickstart lives at index.html so a deployed site lands visitors on the page most of them want.

Quick Start

  1. Replace the brand in the top bar: the wordmark text, the version badge, and the github.com/cairn-build link. The top bar is repeated markup, so this edit happens in all six files (a find-and-replace across *.html does it in one pass).
  2. Replace each article with your own content (see Writing pages — the search, rail and copy buttons follow your markup automatically). Delete any page you don't need, and remove its sidebar entry from the other files.
  3. Update each page's <title> and <meta name="description">.
  4. Deploy the folder anywhere that serves static files.

Adding a Page

The honest cost of plain HTML with no build step: shared chrome is repeated per file, so adding a page touches every file once.

  1. Copy the existing page closest to the one you want (a reference page for tables, a guide for prose).
  2. Edit its <title>, meta description, crumbs, <h1> and article content, and rebuild its "On this page" list to match the new headings (href="#id" per h2[id]/h3[id]; the scrollspy and palette pick them up from the markup).
  3. Add the page's .sb-link to the sidebar in all six existing files, and move aria-current="page" to the right link in the new file.
  4. Wire it into the pager: the new page's prev/next, plus the next/prev of its two neighbours.

Steps 3 and 4 are the repetition tax. Everything else (search index, scrollspy, copy buttons, palettes) reads the DOM and needs no registration.

Writing Pages and Sections

The chrome is driven by the markup, so editing content is the whole job:

  • Add or rename a section: give it an <h2 id="..."> (or <h3 id="...">). The command palette indexes it at load, and it becomes a scroll target.
  • List it in the rail: add a matching <li><a href="#id"> to the "On this page" list. The scrollspy pairs rail links with headings by id and silently skips links whose heading is gone, so a deleted section never breaks the rail.
  • Delete anything: every lookup in the script is guarded. Remove the rail, the palette markup, the tabs, or whole article sections and the rest keeps initialising.
  • Code blocks: a dark panel is <div class="code"> with a .code-bar label row and a <pre data-code>. Any pre[data-code] gets a copy button injected into its bar at runtime. Syntax colour is hand-marked: wrap commands/keys in <span class="tk-a">, strings/values in tk-s, comments in tk-c.
  • Callouts: <div class="callout"> is a note (green border); add callout--warn for the red-bordered warning. The red is a border only — never use it as a text colour, it is not measured for that.
  • Tabs: the install block pairs data-tab data-target="name" buttons with data-tab-panel="name" panels by name, not index, so delete or add pairs freely (keep id/aria-controls matched). Without JavaScript the panels stack, each showing its own label.

The Command Palette (the signature)

Cmd+K / Ctrl+K, or the search button, opens the palette on any page. It fuzzy-filters as you type (matched characters highlight), arrows move the selection, Enter jumps — navigating to a page entry, or smooth-scrolling to a heading on the current page and flashing it — and Escape closes, returning focus to wherever you opened it from.

The index is built from the DOM at load: every sidebar link becomes a page entry (grouped under its sidebar heading; choosing one follows the link, or scrolls to the top if it is the current page), every article h2[id]/h3[id] on the current page becomes a heading entry, plus scripted actions ("Copy install command" reads the pre[data-code="install"] block and only exists on pages that have one; "Back to top"). There is no list to maintain — your edits to the sidebar and article are the search index. To add a custom action, push one more { label, kind, group, run } object where the shipped actions are defined in initPalette() in assets/script.js.

Palettes

The template ships with three palettes, each paired with its own display face so the three read as three identities rather than one design in three colours. Paper is the default in :root; Dark and Slate are the ALTERNATE PALETTES blocks at the end of assets/style.css.

Palette Ground Accent Display face
Paper (default) near-white, barely warm deep viridian green Chakra Petch, a squared technical sans
Dark green-grey near-black mint JetBrains Mono 700, a monospace
Slate cool blue-grey white deep indigo Space Grotesk 600, a grotesk

The face and the colours are independent tokens (--font-display and --font-display-weight sit in each block next to the colours), so keep the palette and swap the face, or the other way round. All three faces are in the Google Fonts <link> in the <head> of every page; a browser only downloads the one the page uses, but once you have settled, trim the link to the family you kept. The reading face (Wix Madefor Text) and the code face (Ubuntu Sans Mono) are the same in every palette.

To use one, set the attribute on the <html> tag of every page (the palette is pure CSS, so the attribute is the whole mechanism):

Code snippet omitted: it ships with the download.

That is the whole change. Each block restates every colour token and its --*-rgb triple, and everything on the page already draws from those tokens, so nothing else needs touching. Once you have settled on one, the tidier end state is to copy its values into the :root block at the top of style.css and delete the alternates, so the stylesheet states one palette rather than three.

Dark is a reading theme, not a terminal. The ground stays off black, raised panels go lighter than the ground, ink is a soft off-white rather than pure white, hover links go lighter (--accent-deep holds a paler mint, where the light palettes hold a darker green), and code panels sit slightly above the ground where in the light palettes they are the page's one dark mass. It also restates color-scheme: dark, so native scrollbars and form chrome follow.

Two tokens exist only because of the dark palette. --scrim (the dialog and drawer backdrop) and --shadow-rgb (box shadows) are stated per palette instead of deriving from --ink-rgb: in Dark, --ink is near-white, and a backdrop derived from it would render a white fog instead of dimming the page.

The default token block, for reference:

Code snippet omitted: it ships with the download.

Contrast. Every palette is measured to the same bar, composited on the surface it lands on: ink and both its dim variants on the ground, the sidebar panel and the cards; the accent as text on all three and on both washes; the code panel's ink, comment, string, label and command colours; text on the solid-accent "Copied" state; and the red border at the 3:1 non-text bar. The full tables are in the header comment at the top of style.css. Everything clears 4.5:1 (the red is a border only, never text, in every palette). If you edit a palette: in Paper the first pair to fail is accent text on --accent-wash over the sidebar (keep --accent at or darker than #1E7A57); in Dark it is the same pair as the mint darkens (keep --accent at or lighter than #74B892).

The picker on the demo is not in your download. The dock in the corner of the online demo is preview scaffolding that lives in a file the zip does not contain. Your copy has the palettes without it, which is the right way round: a real docs site should not carry a theme switcher.

Type

  • --font-display — Chakra Petch: wordmark, headings, sidebar group labels, table headers. Squared, technical, used at conservative sizes. --font-display-weight (600) is the weight those labels and h2/h3 use; the wordmark and h1 stay at 700. Dark and Slate set both tokens to their own faces (see Palettes).
  • --font-body — Wix Madefor Text: all reading text.
  • --font-mono — Ubuntu Sans Mono: code, the version badge, kbd hints.

Swap them by editing the tokens and the Google Fonts <link> in the <head> of every page together.

Accessibility

  • Skip link to the article; real <nav>, <main>, <article>, <aside> landmarks; heading order h1 → h2 → h3; the options table is a real <table> with a caption and column headers.
  • The palette is role="dialog" aria-modal="true" with a combobox input driving a listbox via aria-activedescendant; focus is trapped while open and returned on close. The install tabs are a real ARIA tablist with roving tabindex, arrow keys, Home and End.
  • Visible focus rings everywhere (2px accent outline; mint on the dark code panels), measured in the table above.
  • Reduced motion: the palette opens and closes instantly, the rail marker jumps, tabs swap without fading, and Lenis never starts, via both the CSS media query and the gsap.matchMedia branch.
  • Without JavaScript nothing is lost: the sidebar groups are native open <details>; the rail is plain anchor links; the install panels stack, each labelled; the search and menu buttons (which can only drive JS) are hidden; copy buttons are injected at runtime so they never exist to fail. The has-js class is added synchronously in <head> and removed by the script if GSAP fails to load, so a blocked CDN degrades to the same plain document.

Smooth Scrolling

Lenis drives window scrolling on the shared GSAP clock and is optional: remove the CDN tag and the lenis block near the top of script.js to drop it. It is also switchable without edits — data-smooth="off" on <html> or ?smooth=off in the URL — and it never runs under reduced motion. The sidebar carries data-lenis-prevent, which keeps the mouse wheel native inside its own scroll; put that attribute on any other scrollable panel you add.

Dependencies

  • GSAP 3.15.0 (core)
  • ScrollTrigger
  • Lenis 1.3.17 (optional)

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

Everything in the catalogue, plus everything we release next.