Sidebar
The fixed left sidebar is the primary navigation element for all Syhrezz pages. It is role-specific (Admin, Director, Supervisor, Sales), collapsible, and persists its state in localStorage.
Role Variants
Dark — Primary Role
Dark — Director Accent
Light — Clean White
Blue — Sales / Colorful
Anatomy
| Part | Class | Details |
|---|---|---|
| Container | .sidebar | position:fixed, height:100vh, width:268px, dark bg, box-shadow right |
| Collapsed | .sidebar.collapsed | width:68px. Labels fade out via opacity:0 + translateX(-10px) |
| Logo area | .sidebar-logo | Gradient icon (36-40px) + name + role subtitle. border-bottom. |
| Nav wrapper | .sidebar-nav | flex:1, overflow-y:auto, hidden scrollbar |
| Section label | .sidebar-section-label | 10px fw-700-800, UPPERCASE, letter-spacing:1.5px, opacity fades on collapse |
| Nav item | .nav-item | flex, gap:12px, padding:10-12px, radius:10-12px, hover:translateX(4px) |
| Active item | .nav-item.active | ::before left bar: 3-4px wide, 18-20px tall, accent color, glow |
| Nav badge | .nav-badge | margin-left:auto, 10px fw-800, pill, accent bg |
| Toggle button | — | Bottom of sidebar. Saves state to localStorage key: {role}-sidebar-collapsed |
Color Variants by Role
| Variant | Sidebar BG | Active Accent | Logo Gradient | Use For |
|---|---|---|---|---|
| Dark | #0F172A | #3B5BDB | #1E293B → #334155 | Admin, Supervisor |
| Dark Indigo | #0C0F1A | #6366F1 | #6366F1 → #8B5CF6 | Director / Executive |
| Light | #F8FAFC + border | #6366F1 | #6366F1 → #8B5CF6 | Manager / Light mode |
| Blue | linear-gradient(#1e3a8a, #1e40af) | #93c5fd | rgba(255,255,255,.2) | Sales / Colorful brand |
AI Implementation Notes
For AI coding assistants: The sidebar is always
position:fixed; height:100vh; display:flex; flex-direction:column. The nav uses flex:1; overflow-y:auto with hidden scrollbar. Collapse state is stored in localStorage.getItem('{role}-sidebar-collapsed'). On collapse, labels get opacity:0; transform:translateX(-10px); pointer-events:none. The topbar and main content both have their left offset updated when the sidebar collapses. Active nav item has a ::before left accent bar — NOT a background change on the left edge.When to Use
The sidebar is used on every application page as the primary navigation. It is not an optional component — every authenticated page must include the sidebar. The only exceptions are auth pages (login, forgot password) and full-page error screens (404, 403).
Properties
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
| role | string | required | — | Determines sidebar background, accent color, and logo gradient. 'primary' | 'accent' | 'alternative' |
| collapsed | boolean | optional | false | Collapses sidebar to icon-only rail (68px). State persisted in localStorage. |
| items | array | required | — | Navigation items: {label, href, icon, badge, section}. Items with section property render a section label above them. |
| activePage | string | required | — | Current page identifier. Matched against item href to set .active class. |
Design Decisions
Why persist collapse state in localStorage instead of a cookie or URL param?
Sidebar collapse is a UI preference, not application state. It should persist across page loads and browser sessions without affecting the URL (which would break sharing) or requiring server storage. localStorage is the correct mechanism for client-only UI preferences.
Changelog
v1.0.0July 2026Added
- 3 role variants (primary, accent, alternative) with distinct backgrounds and accent colors
- Collapsible to 68px icon rail with localStorage persistence
- 2-3 level nested submenu support with CSS max-height transition
- Section labels, nav badges, active indicator bar
States
Dashboard
Active item
bg:rgba(255,255,255,.12), left accent bar
Hover item
Hover
bg:rgba(255,255,255,.07), translateX(3px)
Collapsed (68px)
Icon-only rail, labels hidden
Accessibility
| Requirement | Implementation |
|---|---|
| role="navigation" | Add to <nav> element. Use aria-label="Main navigation". |
| Keyboard | All nav items are focusable via Tab. Enter/Space activates. Current page item has aria-current="page". |
| Collapsed | When collapsed, labels are visually hidden but icon buttons still need aria-label for screen readers. |
| Skip link | Add a visually-hidden skip navigation link before the sidebar: <a href="#main-content" class="skip-link">Skip to content</a> |
Composition
Allowed
Every authenticated page must include sidebar
Sidebar + Topbar always appear together
Nested submenus up to 3 levels
Section labels to group nav items
Forbidden
Auth pages (login, forgot password)
Full-page error screens (404, 403)
More than 3 levels of submenu nesting
Best Practices
Do
Group navigation items logically with section labels. Use the same section structure across all roles.
Consistent structure reduces cognitive load when users switch contexts or roles.
Don't
Put all navigation items in a single flat list without sections.
Unsectioned nav becomes hard to scan as the item count grows.
Related Components
Content Rules
Good nav label
Dashboard
Single noun or concept. Recognizable. Short.
Bad nav label
Go to Dashboard Page
Too verbose. Nav labels should be 1-2 words maximum.
Good section label
MAIN
UPPERCASE, 1 word, broad category
Bad section label
Navigation Items
Too descriptive — section labels are categories, not descriptions
Nested Submenus (2-3 Levels)
Nested navigation allows complex app structures to be organized hierarchically without overwhelming the user. Syhrezz supports up to 3 levels of nesting. Each level uses progressively smaller text, increased indentation, and a distinct visual treatment to maintain clarity.
Visual Hierarchy Rules
| Level | Class | Indent | Font | Indicator | Icon |
|---|---|---|---|---|---|
| L1 — Parent | .sd-item-dark | 10px | 13px fw-500 | Left 3px bar + accent bg | 18px SVG |
| L2 — Child | .sd-sub-item | 28px | 12px fw-500 | 4px dot ::before | None |
| L3 — Grandchild | .sd-sub3-item | 44px | 11px fw-400 | 3px dot, muted color | None |
Live Demo — 3-Level Nested Sidebar
Dark — Full 3-level navigation
Level anatomy
L1 — Main navigation. Has icon + label + optional chevron + optional badge.
L2 — Sub-items. No icon. Dot marker when active. 28px left padding. Can have chevron for L3.
L3 — Grandchildren. 44px padding. 11px font. Muted color. No further nesting.
Submenu UX Rules
| Rule | Detail |
|---|---|
| Max 3 levels | Never nest deeper than L3 (grandchild). If structure requires more depth, reconsider the IA. |
| max-height animation | Submenus expand/collapse via max-height: 0 → 300px with 0.25s cubic-bezier(0.4,0,0.2,1). Never use display:none toggle — it prevents animation. |
| Chevron rotation | Chevron rotates 90deg when open. Transition: transform 0.2s. Indicates direction of expansion. |
| Parent highlight | When a child is active, the parent L1 item gets .parent-active class (color:#a5b4fc) even when collapsed. User can always tell which section they're in. |
| Multiple open | Multiple L1 submenus can be open simultaneously. This is intentional — don't auto-close other menus when opening one. |
| Collapsed sidebar | When sidebar is collapsed to 68px, all submenus are hidden. Hovering an L1 item shows a flyout tooltip with the label. No nested flyouts on collapsed state. |
| L2 section labels | Optional section labels within L2 level. Same style as L1 section labels but smaller (8.5px) and indented to 28px. |
| Keyboard | Enter/Space on L1 toggle toggles submenu. Arrow keys navigate within open submenus. Escape collapses current submenu. |
Implementation Pattern
/* CSS structure */
.sd-sub-wrap {
max-height: 0;
overflow: hidden;
transition: max-height .25s cubic-bezier(.4,0,.2,1);
}
.sd-sub-wrap.open { max-height: 300px; }
/* JavaScript toggle */
function toggleSub(trigger, menuId) {
const menu = document.getElementById(menuId);
const isOpen = menu.classList.contains('open');
menu.classList.toggle('open', !isOpen);
trigger.classList.toggle('parent-open', !isOpen);
}
/* HTML structure — 3 levels */
<!-- L1 parent -->
<div class="sd-item-dark" onclick="toggleSub(this,'sub-sales')">
<svg>...</svg> Sales
<svg class="sd-chevron">...</svg>
</div>
<!-- L2 children -->
<div class="sd-sub-wrap" id="sub-sales">
<div class="sd-sub-item">Prospects</div>
<!-- L2 parent → L3 children -->
<div class="sd-sub-item has-children" onclick="toggleSub3(this,'sub3-customers')">
Customers
<svg class="sd-chevron">...</svg>
</div>
<!-- L3 grandchildren -->
<div class="sd-sub3-wrap" id="sub3-customers">
<div class="sd-sub3-item">All Customers</div>
<div class="sd-sub3-item">Active</div>
</div>
</div>Behavior
| Trigger | Behavior |
|---|---|
| Toggle collapse | Width transitions 268px ↔ 68px via CSS transition (0.35s cubic-bezier). Labels fade out (opacity:0 + translateX(-10px)). State saved to localStorage. Topbar and main content left offset update simultaneously. |
| Nav item click | Navigates to href. Active item gets .active class with left accent bar. Previous active item loses class. |
| Submenu toggle | max-height: 0 → 400px transition (0.25s cubic-bezier). Chevron rotates 90deg. Multiple submenus can be open simultaneously. |
| Mobile open | translateX(-100%) → translateX(0). Dark overlay appears behind. Tapping overlay closes sidebar. |
| Nav item hover | translateX(4px) + bg:rgba(255,255,255,.07) + color:#fff. Transition: 0.2s cubic-bezier. |