Navigation
Four patterns. No classes. Active state via aria-current="page".
ASW provides four navigation patterns through semantic HTML and data attributes. Each renders correctly with zero custom classes — structure implies style. Active states are expressed with aria-current="page", which doubles as an accessibility signal and a CSS hook.
Top nav
The persistent bar at the top of every page. A bare <nav> direct child of <body> — no wrapper, no class, no extra divs.
The pattern is two <ul> lists: one on the left (brand), one on the right (links). Items are separated by | pipe dividers rendered via CSS — remove one item and the pipe disappears automatically.
<nav>
<ul><li><a href="/"><strong>Brand</strong></a></li></ul>
<ul>
<li><a href="/asw/docs/">Docs</a></li>
<li><a href="/asw/lab/" aria-current="page">Lab</a></li>
<li><a href="/source" data-text="dim">Source</a></li>
</ul>
</nav>
The nav you see at the top of this page is a live demo.
Responsive behaviour
At medium widths the link list wraps. At narrow widths the brand stacks above the links. No JavaScript required.
Dropdown
A <details> element inside a list item becomes a dropdown. No JavaScript. Closes on outside click via :focus-within CSS. The summary acts as the trigger; the inner <ul> floats below.
<nav>
<ul><li><a href="/"><strong>Brand</strong></a></li></ul>
<ul>
<li><a href="/asw/docs/">Docs</a></li>
<li>
<details>
<summary>More</summary>
<ul>
<li><a href="/about/">About</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
</details>
</li>
</ul>
</nav>
Live demo (opens below the brand bar):
Sub-navigation
A horizontal strip placed inside <main> — directly below the top nav visually, but part of the page content. Use it when a section of a site has its own pages: a personal section, a product area, a doc cluster.
Links are separated by / slash dividers. Monospace font at small size. The active link is full-brightness; inactive links are dimmed. A bottom border separates the strip from the article below.
<nav data-subnav>
<a href="/vigilio/" aria-current="page">index</a>
<a href="/vigilio/now">now</a>
<a href="/vigilio/status">status</a>
</nav>
Place this before the article content, inside main.container or at the top of the article.
Live demo
When to use
data-subnav works best for small, flat sections (3–7 items). For larger hierarchies use data-nav="sidebar". For in-page sections use data-nav="toc".
Table of contents nav
An in-page TOC for the right column of a data-layout="docs" page. Compact, small-text, with a left border active state. Designed to be read at a glance while the main content holds focus.
Pairs with toc-spy.js — a minimal scroll-observer that sets aria-current on the link matching the currently visible heading. Without the script, static active state still works via aria-current="page" set in HTML.
<aside data-toc>
<nav aria-label="On this page" data-nav="toc">
<small>On this page</small>
<ul>
<li><a href="#top-nav">Top nav</a></li>
<li><a href="#subnav">Sub-navigation</a></li>
<li><a href="#sidebar-nav">Sidebar nav</a></li>
<li><a href="#toc-nav" aria-current="true">Table of contents</a></li>
</ul>
</nav>
</aside>
The TOC to the right of this page is a live demo, including the scroll-spy active state.
toc-spy.js
Include the script at the bottom of the page. It observes h2[id] and h3[id] headings inside <article> and updates aria-current on scroll using IntersectionObserver. No dependencies.
<script src="../toc-spy.js"></script>
Active state — aria-current
All ASW nav patterns use aria-current for active state. No .active class, no data-active, no JavaScript required for static pages. The attribute communicates intent to both the CSS and to assistive technology.
| Pattern |
Attribute |
Visual effect |
| Top nav |
aria-current="page" |
Full-brightness text (vs dimmed inactive) |
| Sub-navigation |
aria-current="page" |
Full-brightness text |
| Sidebar nav |
aria-current="page" |
Accent colour background + accent text |
| TOC nav |
aria-current="true" (set by scroll-spy) |
Accent text + left border in accent colour |
Use aria-current="page" for links that represent the current page. Use aria-current="true" (or any value) for the scroll-spy's dynamic active state — the scroll position isn't a "page" but the attribute still carries meaning.