Basic Sidebar
A static navigation column - brand, icon-and-label links, and one clearly marked current page.
6 फ्रेमवर्कशुरुआती
An off-canvas sidebar with a backdrop, a real focus trap, Escape to close and focus restoration.
<button
class="dsidebar__open"
type="button"
aria-expanded="false"
aria-controls="dsidebar-panel"
aria-label="Open sidebar"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false">
<path d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<div class="dsidebar__backdrop" hidden></div>
<div
class="dsidebar__panel"
id="dsidebar-panel"
role="dialog"
aria-modal="true"
aria-label="Sidebar"
hidden
>
<div class="dsidebar__head">
<span class="dsidebar__brand">Adysre</span>
<button class="dsidebar__close" type="button" aria-label="Close sidebar">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false">
<path d="M6 6l12 12M18 6L6 18" />
</svg>
</button>
</div>
<nav aria-label="Sidebar">
<ul class="dsidebar__list">
<li><a class="dsidebar__link" href="/dashboard" aria-current="page">Dashboard</a></li>
<li><a class="dsidebar__link" href="/projects">Projects</a></li>
<li><a class="dsidebar__link" href="/team">Team</a></li>
<li><a class="dsidebar__link" href="/settings">Settings</a></li>
</ul>
</nav>
</div>
<script>
(function () {
var openBtn = document.querySelector('.dsidebar__open');
var closeBtn = document.querySelector('.dsidebar__close');
var backdrop = document.querySelector('.dsidebar__backdrop');
var panel = document.getElementById('dsidebar-panel');
function focusables() {
return Array.prototype.slice.call(
panel.querySelectorAll('a[href], button:not([disabled])')
);
}
function open() {
panel.hidden = false;
backdrop.hidden = false;
openBtn.setAttribute('aria-expanded', 'true');
/* Stops the page scrolling under a finger while the drawer sits still. */
document.body.style.overflow = 'hidden';
requestAnimationFrame(function () {
panel.dataset.open = 'true';
var first = focusables()[0];
if (first) first.focus();
});
document.addEventListener('keydown', onKeyDown);
}
function close() {
panel.hidden = true;
backdrop.hidden = true;
delete panel.dataset.open;
openBtn.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
document.removeEventListener('keydown', onKeyDown);
/* Focus goes back to the trigger, not to the top of the document. */
openBtn.focus();
}
function onKeyDown(event) {
if (event.key === 'Escape') {
close();
return;
}
if (event.key !== 'Tab') return;
/*
* The focus trap. Without it the page behind still takes Tab and a
* keyboard user walks straight out of the open drawer into links they
* cannot see - the classic half-built modal.
*/
var items = focusables();
if (items.length === 0) return;
var first = items[0];
var last = items[items.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
}
openBtn.addEventListener('click', open);
closeBtn.addEventListener('click', close);
backdrop.addEventListener('click', close);
})();
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
itemsआवश्यक | SidebarItem[] | - | रेंडर की जाने वाली आइटम की सूची। |
title | string | 'Adysre' | कार्ड की हेडिंग टेक्स्ट। |
ariaLabel | string | 'Open sidebar' | बटन का एक्सेसिबल नाम। सिर्फ़ आइकन वाले बटन के लिए ज़रूरी है। |
direction | 'left' | 'right' | 'left' | दिखते समय एलिमेंट जिस दिशा से आता है। |
A drawer is a modal, and a modal that only *looks* modal is the classic half-built one: the page behind it still takes Tab, so a keyboard user walks straight out of the open drawer into links they cannot see. Four things fix it and all four are here - `role="dialog" aria-modal="true"`; a focus trap that wraps Tab off the last item back to the first and Shift+Tab off the first to the last; Escape closing it with focus returned to the button that opened it, not dumped at the top of the document; and `overflow: hidden` on `<body>` so the page does not scroll under your finger while the drawer sits still. The Next.js variant also closes on route change - skip that and the drawer hangs over the page you just navigated to. The slide is decoration and sits behind `motion-safe:`/`motion-reduce:`: someone who asked for less motion still gets the drawer, it simply arrives. The `focusables()` selector covers links and buttons; widen it if you add inputs.