Sidebar
A reusable navigation surface with independent scrolling.
Live TypeScript example
Try the component here, then use the source below in your application.
Choose a page to update the main content, or hide the sidebar to expand the workspace.
import '@bluui/web/styles.css'
import { createSidebar, createButton } from "@bluui/web";
export function mount(host: HTMLElement) {
const frame = document.createElement("section");
frame.setAttribute("aria-label", "Workspace preview");
frame.style.cssText = "width:100%;min-width:0;border:1px solid var(--bluui-border);border-radius:10px;overflow:hidden;background:var(--bluui-surface)";
const toolbar = document.createElement("div");
toolbar.style.cssText = "display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px;border-bottom:1px solid var(--bluui-border)";
const brand = document.createElement("strong");
brand.textContent = "Atlas workspace";
brand.style.fontSize = "12px";
toolbar.append(brand);
const actions = document.createElement("div");
toolbar.append(actions);
const layout = document.createElement("div");
layout.style.cssText = "display:grid;grid-template-columns:minmax(108px,30%) minmax(0,1fr);min-height:310px";
const navigation = document.createElement("div");
navigation.style.minWidth = "0";
const content = document.createElement("section");
content.setAttribute("aria-label", "Workspace content");
content.style.cssText = "min-width:0;padding:20px 14px;overflow-wrap:anywhere;background:var(--bluui-background)";
layout.append(navigation, content);
frame.append(toolbar, layout);
host.append(frame);
const pages: Record<string, { title: string; description: string; rows: string[] }> = {
overview: { title: "Workspace overview", description: "A snapshot of your team’s work.", rows: ["3 active projects", "12 tasks completed", "2 reviews awaiting feedback"] },
customers: { title: "Customers", description: "Manage the people and teams you work with.", rows: ["Atlas Labs · Active", "Northstar Studio · Active", "Cedar & Co. · New"] },
reports: { title: "Reports", description: "Recent activity across your workspace.", rows: ["Weekly summary · Ready", "Project progress · Updated today", "Team activity · 12 updates"] },
};
function showPage(key: string) {
const page = pages[key];
content.replaceChildren();
const title = document.createElement("h3");
title.textContent = page.title;
title.style.cssText = "font-size:20px;margin:0 0 12px";
const description = document.createElement("p");
description.textContent = page.description;
description.style.cssText = "font-size:13px;line-height:1.6;color:var(--bluui-text-muted)";
const list = document.createElement("ul");
list.style.cssText = "list-style:none;padding:0;margin:20px 0 0;border:1px solid var(--bluui-border);border-radius:8px;background:var(--bluui-surface)";
page.rows.forEach((text, index) => {
const row = document.createElement("li");
row.textContent = text;
row.style.cssText = "padding:13px 10px;font-size:12px";
if (index) row.style.borderTop = "1px solid var(--bluui-border)";
list.append(row);
});
content.append(title, description, list);
}
const sidebar = createSidebar(navigation, {
ActiveKey: "overview",
AccessibleLabel: "Workspace navigation",
Items: [
{ Key: "group", Label: "Workspace", IsGroup: true },
{ Key: "overview", Label: "Overview" },
{ Key: "customers", Label: "Customers" },
{ Key: "reports", Label: "Reports" },
],
}, { Navigate: showPage });
sidebar.element.style.height = "100%";
let open = true;
const toggle = createButton(actions, { Label: "Hide sidebar", Variant: "Outlined", Size: "Small" }, {
Click: () => {
open = !open;
sidebar.update({ IsOpen: open });
navigation.hidden = !open;
layout.style.gridTemplateColumns = open ? "minmax(108px,30%) minmax(0,1fr)" : "minmax(0,1fr)";
toggle.update({ Label: open ? "Hide sidebar" : "Show sidebar" });
syncToggle();
},
});
function syncToggle() {
const button = toggle.element.querySelector("button")!;
button.setAttribute("aria-expanded", String(open));
button.setAttribute("aria-controls", sidebar.element.id);
button.focus({ preventScroll: true });
}
const toggleButton = toggle.element.querySelector("button")!;
toggleButton.setAttribute("aria-expanded", "true");
toggleButton.setAttribute("aria-controls", sidebar.element.id);
showPage("overview");
return { destroy() { sidebar.destroy(); toggle.destroy(); frame.remove(); } };
}
const instance = mount(document.getElementById('example')!)
// When this page or framework component is removed:
// instance.destroy()
Install the library and add the host element
npm install https://bluui.blutekmedia.com/downloads/bluui-web-0.2.6.tgz<div id="example"></div>This example uses the downloadable @bluui/web package. Your application handles persistence and authentication through its typed callbacks. The starter ZIP includes this same example.
This reference documents the published Block’s parameters, events, and types. The web examples adapt those inputs to browser callbacks. Open native showcase ↗
- Web examples
- Supported4 states checked · no automated failures
- TypeScript
- Supported2 states checked · no automated failures
- OutSystems
- Needs work2 states checked · 2 rule failures
Minimum requirement: WCAG 2.1 AA. Supported means the listed checks passed; full conformance is not yet declared.
Evidence and remaining verification →Purpose and use
Keep recurring destinations in a stable sidebar. The application layout owns its open state and responsive placement.
Keyboard design guidance
The menu toggle announces expanded state. A closed sidebar is removed from keyboard navigation. Mark the current destination with aria-current.
This describes the intended interaction. The dated audit above identifies verified behavior and current gaps.
In your application
Provide useful labels, preserve visible focus, and test the component with your actual content. Use text alongside status colors and check contrast in each state.
Events0No events yet.
Interact with an example to see its events.