Responsive State
Permanently expanded on tablet and above, collapsible with toggle arrows on mobile
Fit Mode
Stretches to fill parent height for equal-height columns in grid layouts
Latest Updates
Rich List Items
List items with icon, title, status tag, and a description on a second row. Status on the <li data-status> cascades to the featured icon and tag automatically.
- success Document Approved Your business license application has been reviewed and approved by the licensing authority.
- warning Payment Overdue Your annual business registration fee of 1,200 SAR is past due. Complete payment to avoid service suspension.
- info System Maintenance Scheduled maintenance window from 2:00 AM to 4:00 AM on Friday. Some services may be unavailable.
- error Login Attempt Blocked An unauthorized login attempt was detected from an unrecognized device. Your account has been temporarily locked.
- info New Message Received You have a new message from the Ministry of Commerce regarding your trade license renewal.
View all notifications
Built-in Features
Works on page load with just HTML markup. For dynamically added drawers, call reinit() to activate new instances.
Smooth animated expand and collapse with automatic sibling closing. Submenus nest to any depth and inherit the same animation, indicator, and state tracking at every level.
Open, closing, and active states tracked via data-state with custom events on every transition. Active nested items auto-reveal their parent menus on load.
Static expanded list on desktop that collapses into an accordion on mobile, controlled per-drawer or per-item with a single data attribute.
Height-constrained mode with gradient fade and a show-more button that auto-detects overflow and flips at scroll end.
A drawer whose list can run out of items (notifications, search results, a filtered menu) takes nds-empty on the <ul class="nds-drawer-list">: the Empty component fills it with a placeholder while it holds no items and clears it as soon as one arrives. Set the wording and icon with data-empty-message and data-empty-icon on the same list.
Programmatic toggle, overflow check, init, destroy, and custom events fired on every submenu open and close.
Usage Guidelines
Best Practices
- Use drawers for sidebar navigation with nested menu structures. The accordion behavior keeps the interface tidy by closing siblings automatically when a new submenu opens.
- Works equally well for flat link lists like quick links, latest updates, promotional items, or related resources where no nesting is needed.
- Use the Rich List Items layout for inline notification feeds, activity lists, or any list where each row needs an icon, title, status tag, and a supporting description line.
- Do not use a drawer for top-level page navigation. Use the Header instead, which is built to host primary nav, search, and user controls.
- Do not use a drawer for compact action menus attached to a single trigger. Use the Dropmenu, which handles positioning and dismissal for overlay menus.
- Reach for
nds-fitwhen the drawer sits in a grid column that should match the height of sibling cards. Combine withnds-cardandnds-strokefor a framed block. - Add
nds-dividedwhen rows carry multi-line content (descriptions, timestamps, tags). Leave it off for dense flat link lists where the indicator alone is enough visual separation. - Submenus nest to any depth, but keep trees shallow (two or three levels) for readability. Deeper structures read like a tangled outline and often hint at a page that should be split up.
- Mark the current page with
data-state="active"on the deepest<li>. The drawer automatically opens all ancestor submenus on load so users land with the active path revealed. - Constrain long lists by setting
--drawer-max-heighton the<nav>and wrapping the list in.nds-scroll-more. The fade mask and show-more button appear only when content actually overflows. - Apply
nds-oncolorwhen the drawer sits on a dark or tinted surface. It rebalances dividers and indicators so rows remain legible without touching text colors.
Modifier Classes
| Class | Description |
|---|---|
nds-lg |
Roomier size with taller items (48px) and wider indentation (--spacing-2xl). |
nds-divided |
Adds horizontal divider lines between list items. Useful for rows with multi-line content. |
nds-fit |
Stretches the drawer to fill its parent height for equal-height columns in grid layouts. |
nds-oncolor |
Rebalances dividers and indicators for use on dark or colored backgrounds. |
nds-card |
Displays the drawer as a card-width block, letting it sit alongside other cards in a grid. |
nds-lined |
Adds a vertical side rail (::before pseudo-element) alongside every sub-list. Opt-in — add to .nds-drawer. Used by the site sidemenu and TOC. Rail width and block-axis inset are controlled by --drawer-lined-width and --drawer-lined-block. |
element: li > ul > li |
No class needed. A li directly inside a sub-ul is automatically treated as a 3-level group header: the element selector restores display: flex, full text color, and applies semibold weight to its direct button. Nest another ul inside to create the collapsible sub-list. |
nds-divided on ul |
Applied to an inner sub-ul (not the drawer root) to add border-block-end dividers between its immediate children. The last child never gets a divider. Useful for separating group items within a 3-level drawer. |
Data Attributes
| Attribute | Description |
|---|---|
data-state="active" |
Set on <li> to mark the current page. The button indicator activates and parent menus expand automatically. |
data-open-on |
Set on drawer or individual <li>. Submenus start expanded at the matching breakpoint but remain toggleable. tablet opens on tablet and everything larger. tablet-max opens on tablet and everything smaller, closed on desktop. Values: mobile, tablet, tablet-max, desktop, desktop-max, large-desktop, always, never. |
data-always-open-on |
Set on drawer only. Locks all submenus open and disables interaction at the matching breakpoint. Arrows hidden, buttons non-clickable. Below that breakpoint, reverts to normal accordion. Same values as data-open-on. |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--drawer-gap |
0px |
Vertical spacing between list items. |
--drawer-indent |
calc(var(--spacing-xl)) |
Submenu indentation, derived from the button's inline padding. Overridden to --spacing-2xl by nds-lg. |
--drawer-divider |
var(--divider-color) |
Color of the divider lines when nds-divided is applied. |
--drawer-indicator-width |
5px |
Thickness of the active/hover indicator bar. |
--drawer-indicator |
transparent |
Default indicator color for inactive items. |
--drawer-indicator-active |
var(--background-primary) |
Indicator color for the active item (data-state="active"). |
--drawer-indicator-hover |
var(--colors-neutral-400) |
Indicator color on hover. |
--drawer-truncate |
1 |
Maximum number of visible lines per label before truncation. |
--drawer-transition |
var(--nds-transition) |
Animation timing for submenu expand and collapse. |
--drawer-max-height |
400px |
Maximum height before scroll overflow activates (requires .nds-scroll-more wrapper). |
--drawer-btn-height |
fit-content |
Height applied to each row button. Set to 100% when nds-fit is used. |
--drawer-btn-gap |
var(--spacing-md) |
Gap between the icon, label, and trailing content inside a row. |
--drawer-lined-width |
1px |
Width of the vertical side rail rendered by nds-lined. |
--drawer-lined-block |
0px |
Block-axis inset (top and bottom trim) of the side rail. Increase to shorten the rail so it does not reach the very top or bottom edge of its sub-list. |
JavaScript API
NDS.Drawer initializes automatically on page load for all .nds-drawer elements. For dynamically added drawers, call NDS.Drawer.create(element) (legacy alias: NDS.Drawer.initDrawer).