Application shells¶
The shell is the frame around every page: sidebar, top bar, content. Four layouts ship, and all four render the same DOM — so a user's choice of layout is a class on one element, and nothing below it has to know.
<div class="ads-app"> <!-- vertical: sidebar left (default) -->
<div class="ads-app ads-app--folded"> <!-- icons only, expands on hover/focus -->
<div class="ads-app ads-app--right"> <!-- sidebar on the trailing edge -->
<div class="ads-app ads-app--horizontal"><!-- nav in a top bar, no sidebar -->
Structure¶
<div class="ads-app">
<a class="ads-skip" href="#content">Skip to content</a>
<aside class="ads-sidebar">
<h1 class="ads-brand"><a href="/"><span class="ads-brand__mark">◈</span> Acme</a></h1>
<nav>
<ul class="ads-nav__list">
<li class="ads-nav__group">
<details open>
<summary class="ads-nav__section">Workspace</summary>
<ul class="ads-nav__sub">
<li>
<a class="ads-nav__link" aria-current="page" href="/contacts">
<span class="ads-nav__icon">…</span><span>Contacts</span>
</a>
</li>
</ul>
</details>
</li>
</ul>
</nav>
</aside>
<div class="ads-main">
<header class="ads-topbar">…</header>
<main class="ads-page-body" id="content">…</main>
</div>
</div>
Groups are <details>
Collapsible nav sections need no JavaScript: <details> gives keyboard focus, Enter/Space to
toggle, and Tab through the items for free. In the horizontal layout the same markup becomes
a dropdown menu — which is why the layouts can share one DOM.
The folded layout¶
ads-app--folded narrows the sidebar to --ads-sidebar-folded (4rem) and hides the labels,
expanding on hover or focus-within.
The labels are hidden with the visually-hidden pattern, not display: none and not
opacity: 0. Each of those was tried and each was wrong: display: none removes the accessible
name, so a screen-reader user loses the nav entirely; opacity: 0 leaves the label taking space
and squeezes the icon; overflow: hidden clips labels mid-word during the transition.
aria-current, not .is-active¶
The stylesheet keys the highlight off [aria-current]. A class gets you nothing. This is
deliberate: ten kinds of "current item" in this system were once marked in CSS alone and were
invisible to assistive technology. Keying the look to the attribute means the two cannot drift.
Omit the attribute when the item is not current — never write aria-current="false", which is
valid ARIA for not current and would still match [aria-current].
Page header¶
<div class="ads-page-header">
<div class="ads-pretitle">Directory</div>
<h1 class="ads-page-title">Contacts</h1>
<p class="ads-muted">Everyone your team has spoken to.</p>
</div>
Containers¶
<div class="ads-container">…</div> <!-- centred, max --ads-container -->
<div class="ads-container ads-container--fluid">…</div>
<div class="ads-container ads-container--narrow">…</div> <!-- reading width -->
Right-to-left¶
The shell and every component use logical properties — padding-inline-start,
border-inline-end, inset-inline-start — so dir="rtl" on <html> mirrors the whole
interface with no second stylesheet. The reference application serves six languages and has an
RTL route in the browser sweep.