Basic Sidebar
A static navigation column - brand, icon-and-label links, and one clearly marked current page.
6 個のフレームワーク初級
Expandable sections of sub-links built on the disclosure pattern - real triggers, real panels.
<nav class="nsidebar" aria-label="Sidebar">
<ul class="nsidebar__list">
<li>
<a class="nsidebar__link" href="/dashboard" aria-current="page">Dashboard</a>
</li>
<li class="nsidebar__group">
<!--
The disclosure pattern: aria-expanded on the trigger, aria-controls
pointing at the panel it owns. The chevron is decoration; those two
attributes are what communicate state.
-->
<button
class="nsidebar__trigger"
type="button"
aria-expanded="true"
aria-controls="nsidebar-panel-projects"
>
<span>Projects</span>
<svg class="nsidebar__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
<path d="M6 9l6 6 6-6" />
</svg>
</button>
<ul class="nsidebar__sub" id="nsidebar-panel-projects">
<li><a class="nsidebar__sublink" href="/projects/active">Active</a></li>
<li><a class="nsidebar__sublink" href="/projects/archived">Archived</a></li>
<li><a class="nsidebar__sublink" href="/projects/templates">Templates</a></li>
</ul>
</li>
<li class="nsidebar__group">
<button
class="nsidebar__trigger"
type="button"
aria-expanded="false"
aria-controls="nsidebar-panel-settings"
>
<span>Settings</span>
<svg class="nsidebar__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
<path d="M6 9l6 6 6-6" />
</svg>
</button>
<ul class="nsidebar__sub" id="nsidebar-panel-settings" hidden>
<li><a class="nsidebar__sublink" href="/settings/general">General</a></li>
<li><a class="nsidebar__sublink" href="/settings/billing">Billing</a></li>
</ul>
</li>
</ul>
</nav>
<script>
document.querySelectorAll('.nsidebar__trigger').forEach(function (trigger) {
trigger.addEventListener('click', function () {
var panel = document.getElementById(trigger.getAttribute('aria-controls'));
var expanded = trigger.getAttribute('aria-expanded') === 'true';
trigger.setAttribute('aria-expanded', String(!expanded));
/*
* hidden, not just height:0. A collapsed panel that is merely invisible
* still hands its links to Tab and to a screen reader - the classic
* half-built disclosure.
*/
panel.hidden = expanded;
});
});
</script>| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
items必須 | SidebarGroup[] | - | レンダリングする項目の配列。 |
pathname必須 | string | - | 現在のパス。一致する項目が現在のページとして示されます。 |
defaultOpenIds | string[] | [] | マウント時に展開しておく項目の id。 |
allowMultiple | boolean | true | 複数の項目を同時に開けるようにします。 |
ariaLabel | string | 'Sidebar' | ボタンのアクセシブルな名前。アイコンのみのボタンでは必須です。 |
Each group is a disclosure: `aria-expanded` on the trigger button, `aria-controls` pointing at the `id` of the `<ul>` it owns. The collapsed panel uses the `hidden` attribute rather than `height: 0` or `opacity-0` - an invisible panel that still hands its links to Tab is the classic half-built tree, where a keyboard user falls into sub-links they cannot see. `hidden` removes them from both the tab order and the accessibility tree in one attribute. These are `<ul>`s of links, deliberately **not** `role="tree"`: a tree implies arrow-key traversal and roving tabindex, and if you claim the role without building the behaviour you have made things worse than plain nav. `allowMultiple={false}` turns the groups into an accordion. The indent guide is the sub-list border-left doing double duty - one border, no spacer divs.