Usage
HTML + Javascript
This component requires Javascript.
To use a sidebar, you will need the sidebar itself and a sibling container for the content of the page (e.g. <main>
), with the following structure for the sidebar:
- A
<div class="sidebar">
which wraps around the entire component and holds it state (e.g. open/close). It accepts an optionaldata-side
set toleft
, orright
to specify the side of the sidebar (defaults toleft
). - A
<nav>
that contains the actual sidebar with the following children:- A
<header>
and<footer>
for the header and footer of the sidebar (fixed position). - A
<section>
for the main navigation list. To add links, you must wrap them in a<div role="group">
first. These groups can contain<h3">
for group headings and lists (i.e.<ul>
) of links or buttons. You can also use<details>
to wrap around collapsbile sections.
- A
Any elememt on the page can toggle, open or close the sidebar by dispatching the sidebar:toggle
, sidebar:open
or sidebar:close
events.
You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. x-bind
, x-data
, @click
).
<div class="sidebar" data-uninitialized data-side="left" aria-hidden="false" x-data="sidebar(true)" x-bind="$main">
<nav aria-label="Sidebar navigation">
<section class="scrollbar">
<div role="group" aria-labelledby="group-label-content-1">
<h3 id="group-label-content-1">Getting started</h3>
<ul>
<li>
<a href="#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="m7 11 2-2-2-2" />
<path d="M11 13h4" />
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
</svg>
<span>Playground</span>
</a>
</li>
<li>
<a href="#">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 8V4H8" />
<rect width="16" height="12" x="4" y="8" rx="2" />
<path d="M2 14h2" />
<path d="M20 14h2" />
<path d="M15 13v2" />
<path d="M9 13v2" />
</svg>
<span>Models</span>
</a>
</li>
<li>
<details id="submenu-content-1-3">
<summary aria-controls="submenu-content-1-3-content">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
<circle cx="12" cy="12" r="3" />
</svg>
Settings
</summary>
<ul id="submenu-content-1-3-content">
<li>
<a href="#">
<span>General</span>
</a>
</li>
<li>
<a href="#">
<span>Team</span>
</a>
</li>
<li>
<a href="#">
<span>Billing</span>
</a>
</li>
<li>
<a href="#">
<span>Limits</span>
</a>
</li>
</ul>
</details>
</li>
</ul>
</div>
</section>
</nav>
</div>
<main>
<button type="button" @click="$dispatch('sidebar:toggle')">Toggle sidebar</button>
<h1>Content</h1>
</main>
<script>
window.basecoat = window.basecoat || {};
window.basecoat.registerSidebar = function (Alpine) {
if (Alpine.components && Alpine.components.sidebar) return;
Alpine.data("sidebar", (initialOpen = true, initialMobileOpen = false) => ({
open: window.innerWidth >= 768 ? initialOpen : initialMobileOpen,
init() {
this.$nextTick(() => {
this.$el.removeAttribute("data-uninitialized");
});
},
$main: {
"@sidebar:open.window"(e) {
this.open = true;
},
"@sidebar:close.window"(e) {
this.open = false;
},
"@sidebar:toggle.window"(e) {
this.open = !this.open;
},
"@click"(e) {
if (e.target === this.$el) this.open = false;
},
":aria-hidden"() {
return !this.open;
},
":inert"() {
return !this.open;
},
},
}));
};
document.addEventListener("alpine:init", () => {
window.basecoat.registerSidebar(Alpine);
});
</script>
Jinja and Nunjucks
You can use the sidebar()
Nunjucks or Jinja macro for this component.
{% set menu = [
{ type: "group", label: "Getting started", items: [
{ label: "Playground", url: "#" },
{ label: "Models", url: "#" },
{ label: "Settings", type: "submenu", items: [
{ label: "General", url: "#" },
{ label: "Team", url: "#" },
{ label: "Billing", url: "#" },
{ label: "Limits", url: "#" }
] }
]}
] %}
{{ sidebar(
label="Sidebar navigation",
menu=menu
) }}
<main>
<h1>Content</h1>
</main>