Navigation Bar
The sticky navigation bar holds branding, primary links, secondary actions, and the mobile hamburger toggle.
<nav class="nds-main-nav nds-content-wrapper" id="ndsMainNav" aria-label="Primary navigation">
<div class="nds-nav-container">
<a class="nds-brand" href="/">
<img class="nds-brand-logo" src="logo.svg" width="120" height="40" alt="Brand Logo">
<span class="nds-brand-name">Brand Name
<span class="nds-brand-slogan">Slogan text</span>
</span>
</a>
<ul class="nds-nav-minimal" hidden>
<li class="nds-mainNav-toggler nds-nav-item">
<button class="nds-nav-link nds-btn nds-subtle nds-indicator" aria-label="Toggle navigation menu" aria-expanded="false" aria-controls="ndsNavCollapse">
<i class="nds-icon nds-hgi-menu-01" aria-hidden="true"></i>
</button>
</li>
</ul>
<div class="nds-collapse" id="ndsNavCollapse" hidden>
<div class="nds-collapse-content">
<ul class="nds-nav-primary">
<li class="nds-nav-item">
<a href="/services" class="nds-nav-link nds-btn nds-subtle nds-indicator">
<span class="nds-label">Services</span>
</a>
</li>
<li class="nds-nav-item">
<a href="/about" class="nds-nav-link nds-btn nds-subtle nds-indicator">
<span class="nds-label">About</span>
</a>
</li>
</ul>
<div class="nds-nav-item nds-show-more">
<a class="nds-nav-link nds-btn nds-subtle nds-indicator nds-full">
<i class="nds-icon nds-hgi-arrow-down-01" aria-hidden="true"></i>
</a>
</div>
<ul class="nds-nav-actions">
<li class="nds-nav-item">
<a href="/search" class="nds-nav-link nds-btn nds-subtle nds-indicator">
<i class="nds-icon nds-hgi-search-01" aria-hidden="true"></i>
<span class="nds-label" data-hidden="sm md sr">Search</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</nav>
<ul class="nds-nav-actions">
<!-- Stays reachable in minimal mode -->
<li class="nds-nav-item nds-icon-only nds-PAB">
<!-- Icon-only: the hover tooltip is built from the title, on a native-title delay -->
<a href="/notifications" class="nds-nav-link nds-btn nds-subtle nds-indicator nds-tooltip" data-tooltip-hover="500" title="Notifications">
<i class="nds-icon nds-hgi-notification-02" aria-hidden="true"></i>
<span class="nds-label" data-hidden="sm md sr">Notifications</span>
</a>
</li>
<!-- Compact button, placed first in the minimal bar -->
<li class="nds-nav-item nds-PAB nds-CTA">
<a href="/apply" class="nds-btn nds-primary" title="Apply Now">
<span class="nds-label">Apply Now</span>
</a>
</li>
</ul>
Dropdown Menu Content
Add nds-dropdown to a nav item to attach a dropdown panel. The panel's nds-dropdown-columns child supports three layouts for organizing content.
<li class="nds-nav-item nds-dropdown">
<a href="#" class="nds-nav-link nds-btn nds-subtle nds-menu-btn nds-indicator" aria-expanded="false">
<span class="nds-label">Services</span>
</a>
<div class="nds-dropdown-menu" hidden>
<div class="nds-dropdown-content nds-content-wrapper">
<div class="nds-dropdown-columns nds-colView">
<div class="nds-column">
<div class="nds-dropdown-title">For Citizens</div>
<div class="nds-list">
<a class="nds-btn nds-subtle nds-dropdown-item" href="/citizens/national-id">
<i class="nds-icon nds-hgi-identity-card" aria-hidden="true"></i>
<span class="nds-label">National ID</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="/citizens/travel">
<i class="nds-icon nds-hgi-door-01" aria-hidden="true"></i>
<span class="nds-label">Travel Services</span>
</a>
</div>
</div>
<div class="nds-column">
<div class="nds-dropdown-title">For Businesses</div>
<div class="nds-list">
<a class="nds-btn nds-subtle nds-dropdown-item" href="/business/licensing">
<i class="nds-icon nds-hgi-award-05" aria-hidden="true"></i>
<span class="nds-label">Licensing</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="/business/tax">
<i class="nds-icon nds-hgi-riyal-circular" aria-hidden="true"></i>
<span class="nds-label">Tax Filing</span>
</a>
</div>
</div>
</div>
</div>
</div>
</li>
<li class="nds-nav-item nds-dropdown">
<a href="#" class="nds-nav-link nds-btn nds-subtle nds-menu-btn nds-indicator" aria-expanded="false">
<span class="nds-label">Tools</span>
</a>
<div class="nds-dropdown-menu nds-fit" hidden>
<div class="nds-dropdown-content">
<div class="nds-dropdown-columns nds-rowView">
<div class="nds-list">
<a class="nds-btn nds-subtle nds-dropdown-item" href="/tools/search">
<i class="nds-icon nds-hgi-search-01" aria-hidden="true"></i>
<span class="nds-label">Search</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="/tools/translate">
<i class="nds-icon nds-hgi-translation" aria-hidden="true"></i>
<span class="nds-label">Translate</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="/tools/support">
<i class="nds-icon nds-hgi-headphones" aria-hidden="true"></i>
<span class="nds-label">Support</span>
</a>
</div>
</div>
</div>
</div>
</li>
<li class="nds-nav-item nds-dropdown">
<a href="#" class="nds-nav-link nds-btn nds-subtle nds-menu-btn nds-indicator" aria-expanded="false">
<span class="nds-label">All Services</span>
</a>
<div class="nds-dropdown-menu" hidden>
<div class="nds-dropdown-content nds-content-wrapper">
<div class="nds-dropdown-columns nds-colView">
<div class="nds-column">
<div class="nds-dropdown-title">Directory</div>
<div class="nds-list nds-multi-column-list">
<a class="nds-btn nds-subtle nds-dropdown-item" href="#">
<span class="nds-label">Education</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="#">
<span class="nds-label">Health</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="#">
<span class="nds-label">Transport</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="#">
<span class="nds-label">Housing</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="#">
<span class="nds-label">Employment</span>
</a>
<a class="nds-btn nds-subtle nds-dropdown-item" href="#">
<span class="nds-label">Legal</span>
</a>
</div>
</div>
</div>
</div>
</div>
</li>
Built-in Features
Activates when .nds-main-nav is on the page. Dropdowns, collapse, overflow detection, and scroll behavior attach automatically.
Switches from a horizontal nav bar to a hamburger menu at a configurable breakpoint, with animated expand and collapse transitions.
Primary nav items that exceed the available width become scrollable, with a show-more button that scrolls through hidden items.
Dropdown content can be organized in column, row, or multi-column list layouts that adapt to narrower screens.
Same-page anchor links in the nav close open menus and scroll smoothly to the target section.
Primary nav supports horizontal drag scrolling and converts vertical mouse wheel input to horizontal scroll when items overflow.
Scroll direction, drag behavior, and layout flip automatically in right-to-left contexts.
All header transitions respect the prefers-reduced-motion setting, skipping animations for users who have requested it.
Nav items marked with nds-PAB automatically relocate to the minimal nav bar on small screens and return to their original position on larger viewports.
Opening a dropdown or the mobile collapse displays a backdrop that closes the menu on outside clicks, preventing interaction with page content beneath.
Usage Guidelines
Best Practices
- Place the navigation bar at the top of every page, inside the
<header>element. See Header for how it composes with the top bar above it. - Keep primary nav items between 3 and 8 links. Overflow handling activates automatically, but excessive items reduce usability.
- Use dropdown menus for grouping related pages under a single primary nav item. Organize content into columns using
nds-colViewfor category-based layouts ornds-rowViewfor flat lists. - Reserve the secondary nav for utility actions: search, language toggle, user profile, notifications. These persist across all breakpoints as icon-only buttons on smaller screens.
- Pages with no header still need a language switch. Put it in the page's own top area. The Sign in example puts it in the card header, next to the brand logo.
- Use
nds-CTAon a nav item to render it as a compact button instead of a link, for the one action the page is steering people toward: sign in, start a service, book an appointment. Omitdata-hiddenon its label so the text stays readable at every width. - Do not use the main navigation for in-page section links. Use Tabs for switching between content panels on the same page, or anchor links within the page body.
- Do not place critical actions only inside dropdown menus. Users on mobile may not discover them. Promote key actions to the primary or secondary nav level.
- Do not add more than one level of dropdown nesting. The component supports single-level dropdowns only.
- Set the brand logo dimensions explicitly with
widthandheightattributes to prevent layout shift during page load. - Use
nds-icon-onlyon secondary nav items that should display as icon buttons at every width. For labels that should collapse only on smaller screens, stampdata-hidden="sm md sr"on the label as the markup above shows: thesrtoken keeps the accessible name while the label is visually hidden. See the Hidden utility. - Mark nav items that must stay reachable at every breakpoint with
nds-PAB. Search, language, and account are the usual ones. Do not mark every action: unmarked items stay in the expanded menu, and a minimal bar carrying everything defeats it. JavaScript moves the item into the minimal bar and returns it to the position you authored, so do not reparent a PAB item yourself at runtime. - A PAB carrying
nds-CTAis placed first in the minimal bar, ahead of the other persistent items. Use that when the call to action must be the one control a small screen never hides.
Modifier Classes
| Class | Element | Description |
|---|---|---|
nds-dropdown | nav-item | Enables dropdown menu behavior |
nds-CTA | nav-item | Renders the item as a compact button rather than a link, sizing the inner button to 32px |
nds-icon-only | nav-item | Displays as icon button, hides label |
nds-PAB | nav-item | Persistent action button. JavaScript moves the item into .nds-nav-minimal in minimal mode and returns it to its authored position on wider viewports |
nds-show-more | nav-item | Marks the overflow scroll button |
nds-oncolor | nav-link | Applies on-color text styling for dark backgrounds |
nds-menu-btn | nav-link | Transparent background style with indicator for primary nav items (mobile) |
nds-fit | dropdown-menu | Sizes the dropdown to fit its content width |
nds-colView | dropdown-columns | Organizes content in vertical columns |
nds-rowView | dropdown-columns | Organizes content in a horizontal row layout |
nds-multi-column-list | list inside dropdown | Renders items in a 3-column grid |
Data Attributes
| Attribute | Element | Description |
|---|---|---|
data-state="current" | nav-link | Marks the current page. You write it, and nothing in the component removes it. This is the only attribute that highlights the current page in the main nav. aria-current="page" is for screen readers and drives no styling, so a link with that alone renders with no highlight at all — set both. NDS.Init.audit() reports a link that is missing it |
data-state="active" | nav-link | Written and removed by the component while that link's dropdown is open. Never author it. On a current-page link it looks correct at first and then disappears, because the next dropdown close strips it. The Side Menu uses the same word on its <li> elements, where it does mean the current page — a different element doing a different job. Do not carry that spelling over to the main nav |
data-hidden="sm md sr" | action-item label | Collapses the label to icon-only below 960px while screen readers keep the name. Omit on nds-CTA items so their labels stay visible. See the Hidden utility |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--nds-nav-height | 72px | Height of the navigation bar |
--nds-minimal-nav-bp | 960px | Breakpoint width for switching to mobile/minimal mode |
--nds-transition-speed | 0.2 | Base transition speed in seconds |
--nds-minimal-nav-item-height | 40px | Height of each nav item in the mobile collapse panel |
JavaScript API
The main navigation initializes automatically when .nds-main-nav exists on the page, and wires its own click handling through event delegation: the hamburger toggle and dropdown triggers respond without any inline onclick attributes. The functions below are exposed on the NDS.Mainnav namespace for driving the nav from external scripts. If your app renders the header itself, read the re-initialize note at the end of the block.
// ── Toggle Functions ─────────────────────────
// Exposed on the NDS.Mainnav namespace for external scripts
// and integrations. Bind to your own triggers as needed.
// Toggle the mobile hamburger collapse.
// Opens the collapse panel if closed, closes it if open.
// Queues the action if an animation is already running.
NDS.Mainnav.toggleNavbar();
// Toggle a dropdown menu.
// Pass the native click event from the trigger element.
// The function finds the closest .nds-dropdown ancestor,
// closes any other open dropdown first, then opens the target.
NDS.Mainnav.toggleDropdown(event);
// ── Re-initialize ────────────────────────────
// Call when the nav ELEMENT itself is new. Two cases,
// both silent without it: the nav renders after the
// bundle runs, which happens when a framework mounts
// the chrome, or a route change replaces the nav. In
// both, the CSS still paints the nav and no click works.
NDS.Mainnav.reinit();
// Or make the one call that covers every component.
NDS.Init.refresh(document.body);
// Adding or removing nav ITEMS inside a live nav needs
// no call. The nav watches for that and re-runs its
// overflow and placement work on its own. Re-running on
// an unchanged nav is a no-op, so both calls above are
// safe to make as often as you like.
// init() wires the nav once, on page load. Calling it
// again does nothing, so reach for reinit() instead.
NDS.Mainnav.init();
// For top bar widget APIs (date, clock, weather, DGA stamp)
// see the Top Bar documentation page.