Navigation

v0.0.4
Site pages

Documentation

Navigation, an article at a reading measure, and the contents of the page.

A documentation page has three columns: the navigation of the whole site, the article at a reading measure, and the contents of this page. It is the app shell with a header and an aside. As the room narrows the contents leave first, then the navigation, and the article keeps its measure throughout.

Documentation

<div class="shell bg-surface-base fg-text-body text-sm" style="--shell-sidebar: 15rem">  <a class="skip" href="#docs-main">Skip to content</a>  <header class="navbar shell-header bb(1) bb-solid b-line-hair">    <button type="button" class="btn btn-quiet btn-square btn-sm shell-toggle" commandfor="docs-nav" command="show-modal" aria-label="Open navigation">      <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">        <path d="M4 8h16M4 16h16" />      </svg>    </button>    <a class="navbar-brand" href="#">      <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">        <path d="M12 3a9 9 0 1 0 0 18a9 9 0 0 0 0-18M12 3a9 9 0 0 0 0 18" />      </svg>Atlas</a>    <div class="navbar-end">      <label class="input-group input-group-sm w(220) hidden flex@container(--xl)">        <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">          <path d="M4 11a7 7 0 1 0 14 0a7 7 0 0 0-14 0M20 20l-4-4" />        </svg>        <input class="input-group-field" type="search" placeholder="Search the docs" aria-label="Search the docs">        <kbd class="kbd">/</kbd>      </label>      <button type="button" class="btn btn-sm">Dashboard</button>    </div>  </header>  <div class="shell-sidebar p-sm bxe(1) bxe-solid b-line-hair">    <nav class="nav" aria-label="Documentation">      <div class="nav-label">Getting started</div>      <a class="nav-item" href="#">Introduction</a>      <a class="nav-item" href="#" aria-current="page">Installation</a>      <a class="nav-item" href="#">Theme</a>      <div class="nav-label">Deploy</div>      <a class="nav-item" href="#">Build settings</a>      <a class="nav-item" href="#">Environment variables</a>      <a class="nav-item" href="#">Domains</a>      <a class="nav-item" href="#">Regions</a>      <div class="nav-label">Reference</div>      <a class="nav-item" href="#">Command line</a>      <a class="nav-item" href="#">Configuration file</a>      <a class="nav-item" href="#">Limits</a>    </nav>  </div>  <main class="shell-main" id="docs-main" tabindex="-1">    <article class="flex flex-col gap-xl max-w(720) mx-auto px-lg py-xl">      <header class="flex flex-col gap-sm">        <nav aria-label="Breadcrumb">          <ol class="breadcrumb">            <li class="breadcrumb-item">              <a class="breadcrumb-link" href="#">Docs</a>            </li>            <li class="breadcrumb-item">              <a class="breadcrumb-link" href="#">Getting started</a>            </li>            <li class="breadcrumb-item">              <a class="breadcrumb-link" href="#" aria-current="page">Installation</a>            </li>          </ol>        </nav>        <h1 class="heading heading-2xl">Installation</h1>        <p class="lead">Install the command line, link a folder to a project, and deploy it. It takes about two minutes.</p>      </header>      <section class="flex flex-col gap-sm">        <h2 class="heading heading-lg flash" id="docs-requirements">Requirements</h2>        <p class="m(0) leading-md">Atlas runs on any machine with Node 22 or later. The command line talks to the same API as the dashboard, so anything you can click you can also script.</p>        <p class="m(0) leading-md">You need an account and a project. Both are free to create, and neither asks for a card.</p>      </section>      <section class="flex flex-col gap-sm">        <h2 class="heading heading-lg flash" id="docs-install">Install the command line</h2>        <p class="m(0) leading-md">Install it globally, or run it without installing through your package manager. The second way always uses the latest release.</p>        <p class="m(0) leading-md">After the first run it asks you to sign in. The token is stored in your home directory and can be revoked from the dashboard.</p>      </section>      <section class="flex flex-col gap-sm">        <h2 class="heading heading-lg flash" id="docs-link">Link a project</h2>        <p class="m(0) leading-md">Run the link command in the folder of your app. It finds the framework, writes a small configuration file and connects the folder to a project.</p>        <p class="m(0) leading-md">Commit the configuration file. Everyone on the team then deploys the same way.</p>      </section>      <section class="flex flex-col gap-sm">        <h2 class="heading heading-lg flash" id="docs-deploy">Deploy</h2>        <p class="m(0) leading-md">Every push to a branch makes a preview with its own address. A push to the main branch goes to production.</p>        <p class="m(0) leading-md">A deployment is immutable. Rolling back is choosing an earlier one, and takes a few seconds.</p>      </section>      <nav class="pager" aria-label="Guide pages">        <a class="pager-link" rel="prev" href="#">          <span class="pager-label">Previous</span>          <span class="pager-title">Introduction</span>        </a>        <a class="pager-link" rel="next" href="#">          <span class="pager-label">Next</span>          <span class="pager-title">Your first deployment</span>        </a>      </nav>    </article>  </main>  <div class="shell-aside py-xl pxe-lg">    <nav class="toc" aria-label="On this page">      <p class="toc-title">On this page</p>      <ul class="toc-list">        <li>          <a class="toc-link" href="#docs-requirements">Requirements</a>        </li>        <li>          <a class="toc-link" href="#docs-install">Install the command line</a>        </li>        <li>          <a class="toc-link" href="#docs-link">Link a project</a>        </li>        <li>          <a class="toc-link" href="#docs-deploy">Deploy</a>        </li>      </ul>    </nav>  </div>  <dialog class="dialog dialog-drawer dialog-drawer-start" id="docs-nav" closedby="any" aria-label="Navigation">    <div class="dialog-body flex flex-col gap-md">      <a class="navbar-brand h(40) px-xs" href="#">        <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">          <path d="M12 3a9 9 0 1 0 0 18a9 9 0 0 0 0-18M12 3a9 9 0 0 0 0 18" />        </svg>Atlas</a>      <nav class="nav" aria-label="Documentation, in the drawer">        <div class="nav-label">Getting started</div>        <a class="nav-item" href="#">Introduction</a>        <a class="nav-item" href="#" aria-current="page">Installation</a>        <a class="nav-item" href="#">Theme</a>        <div class="nav-label">Deploy</div>        <a class="nav-item" href="#">Build settings</a>        <a class="nav-item" href="#">Environment variables</a>        <a class="nav-item" href="#">Domains</a>        <a class="nav-item" href="#">Regions</a>        <div class="nav-label">Reference</div>        <a class="nav-item" href="#">Command line</a>        <a class="nav-item" href="#">Configuration file</a>        <a class="nav-item" href="#">Limits</a>      </nav>    </div>    <button type="button" class="btn btn-quiet btn-square btn-sm dialog-close" commandfor="docs-nav" command="close" aria-label="Close navigation">      <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">        <path d="M6 6l12 12M18 6 6 18" />      </svg>    </button>  </dialog></div>
Scroll the page: both side columns stay, and the contents mark the section in view. At Tablet the contents leave; at Phone the navigation moves into a drawer.

#How it is built

RegionClassesHolds
Headernavbar shell-headerThe brand, a search field, a way to the product
Sidebarshell-sidebarA nav with nav-label over each group
Mainshell-mainAn <article> at max-w(720) mx-auto
Asideshell-asideA table of contents
  • The article sets the measure. 720px holds about 75 characters of body text, the upper end of a comfortable line. The column around it takes whatever is left, so the article stays centered between the side columns.
  • The side columns scroll by themselves. Each is as tall as the viewport under the header, so a long navigation never moves the page and the page never moves the navigation.
  • The contents mark themselves. The table of contents follows the page's scroll where the browser supports scroll-target-group; elsewhere, set aria-current from a script.
  • Headings clear the header. Each heading is a flash: it stops 5rem under the top of the viewport when a link jumps to it, clear of the sticky bar, and a ring shows which heading the link landed on.
  • The article ends with a way on. A pager names the page before and the page after.

The search field is hidden with hidden flex@container(--xl): it appears when the navbar, which is a size container, is at least 36rem wide.

#Accessibility

  • Four navigations need four labels: the site's ("Documentation"), the page's ("On this page"), the breadcrumb's and the pager's. The two side columns are plain elements around a <nav>, so each is one landmark, not two.
  • The order in the source is header, navigation, article, contents. A reader of the page in order meets the article's <h1> after the navigation, so the first element is a skip link to the main region, which has tabindex="-1".
  • On a narrow screen the contents are not shown. That loses a convenience, not content: every heading is still in the article.