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
| Region | Classes | Holds |
|---|---|---|
| Header | navbar shell-header | The brand, a search field, a way to the product |
| Sidebar | shell-sidebar | A nav with nav-label over each group |
| Main | shell-main | An <article> at max-w(720) mx-auto |
| Aside | shell-aside | A 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, setaria-currentfrom 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 hastabindex="-1". - On a narrow screen the contents are not shown. That loses a convenience, not content: every heading is still in the article.
#Related
- App shellA sidebar that stays while the main region scrolls, and a drawer when it is narrow.
- PageA header, the main region and a footer in a frame as tall as the viewport.
- NavbarThe bar at the top of a page, which takes its material once content scrolls under it.
- NavA vertical list of destinations for a sidebar.
- Table of contentsThe headings of a page as links, with the section in view marked by CSS.
- BreadcrumbThe path to the current page as a list of links.
- HeadingA title in the ink of the page, with the line over it and the paragraph under it.
- FlashA ring that shows where a link has landed, then fades.
- PagerThe page before and the page after, at the foot of an article.
- Skip linkThe first link of a page, shown while it has focus, that jumps past the navigation.
- DialogA native modal on an overlay sheet, opened and closed without script.
- InputText fields cut into the surface, with labels, hints, errors and groups.