Header
Definition
The Header navigation component is a basic navigation bar that is used to help users navigate websites and applications. It is typically fixed to the top of the page but not to the top of the viewport, meaning that if a user scrolls down the navigation scrolls with the rest of the content.
The Header consists of:
-
A global navigation bar, required
- A login button/dropdown, optional
- A local navigation bar with logo, required
- A search bar, optional
Purpose
The purpose of the header navigation component is to provide users with an easy and intuitive way to navigate websites and applications. It allows users to quickly access different pages or sections of the site, as well as switch between different products or services if applicable.
Structure and Function
The current structure affords the ability to navigate to internal pages and other products (other URLs) that might be child products of the parent organization.
Use the primary colored top bar for links that go to ICPSR pages. Use the white bar below for links within your current application or product.
The navigation component will switch to a "drawer" that is opened with a navbar-toggler on smaller screens.
Styling
The navigation component's color will be based on the theme color for the product implementing it.
Dropdowns
For large websites, use dropdown menus to help users preview lower-level content. If lower-level sections are closely related and users will need to quickly jump between them, consider using the navigation sidebar instead of — or in addition to — a dropdown.
Accessibility Considerations
Semantic markup conveys the menu structure to users. Menus coded semantically can easily adapt to different situations, such as small screen displays, screen magnification, and other assistive technology.
Identify the menu using the HTML5 <nav> element to allow users
access to the menu directly.
Use an ordered list when the sequence of the menu items is important. Use an unordered list when the menu items are not in a specific order.
Include skip navigation links to allow users with screen readers to bypass long navigation lists. Make sure you include an id at the beginning of your main content and that it matches the skipnav link. See the code snippet at the end of this page for an example.
Labels
Label menus to make them easier to find and understand. Labels should be short but descriptive, to allow users to distinguish between multiple menus on a web page. Make sure the following labels are present in your navigation component:
<nav class="header" aria-label="Main Navigation">
<button class="btn btn-close btn-close-white" aria-label="Close"></button> Overall, the header navigation component is an essential part of any website or application, providing users with an intuitive and accessible way to navigate the site or app. By following these guidelines, you can ensure that your navigation component is effective, efficient, and accessible to all users.
Sample code
Scroll the container below to explore the full header navigation example.
See a full page example of the header navigation
<header>
<div id="skip"><a href="#skipto">Skip to Main Content</a></div>
<nav class="header full">
<a class="department-logo" href="/static/docs/">
<picture>
<source
media="(max-width: 1400px)"
srcset="https://test.datalumos.org/datalumos/resources/commons/images/datalumos/datalumos-logo.png"
/>
<source
media="(min-width: 1401px)"
srcset="https://test.datalumos.org/datalumos/resources/commons/images/datalumos/datalumos-logo.png"
/>
<img
src="https://test.datalumos.org/datalumos/resources/commons/images/datalumos/datalumos-logo.png"
alt="Data Lumos"
/>
</picture>
</a>
<button
class="btn btn-icon offscreen-toggler"
popovertarget="offscreen-nav"
popovertargetaction="show">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path
d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"
></path></svg
>
</button>
<div class="offscreen" id="offscreen-nav">
<button
class="btn btn-close btn-close-white"
aria-label="Close"
popovertarget="offscreen-nav"
popovertargetaction="hide"></button>
<div class="global-nav">
<ul class="nav-list">
<li class="nav-item">
<a class="nav-link" href="#" aria-current="false">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" aria-current="false">Help</a>
</li>
<li class="nav-item">
<details class="nav-item">
<summary class="nav-link">ICPSR Membership</summary>
<ul>
<li><a class="dropdown-item" href="#">Overview</a></li>
<li><a class="dropdown-item" href="#">Member list</a></li>
<li><a class="dropdown-item" href="#">How to join</a></li>
<li>
<a class="dropdown-item" href="#">Official rep tools</a>
</li>
<li><a class="dropdown-item" href="#">Promoting ICPSR</a></li>
<li>
<a class="dropdown-item" href="#">News & publications</a>
</li>
<li><a class="dropdown-item" href="#">Biennial meeting</a></li>
</ul>
</details>
</li>
</ul>
</div>
<div class="local-nav">
<ul class="nav-list">
<li class="nav-item">
<a class="nav-link" href="#" aria-current="false">Find data</a>
</li>
<li class="nav-item">
<details class="nav-item">
<summary class="nav-link">Share data</summary>
<ul>
<li><a class="dropdown-item" href="#">Option 1</a></li>
<li><a class="dropdown-item" href="#">Option 2</a></li>
<li><a class="dropdown-item" href="#">Option 3</a></li>
</ul>
</details>
</li>
<li class="nav-item">
<details class="nav-item">
<summary class="nav-link">Summer program</summary>
<ul>
<li><a class="dropdown-item" href="#">Lorem</a></li>
<li><a class="dropdown-item" href="#">Lorem ipsum</a></li>
<li>
<a class="dropdown-item" href="#"
>Lorem ipsum dolor sit amet</a
>
</li>
</ul>
</details>
</li>
<li class="nav-item">
<details class="nav-item">
<summary class="nav-link">Teaching & learning</summary>
<ul>
<li><a class="dropdown-item" href="#">Lorem</a></li>
<li><a class="dropdown-item" href="#">Lorem ipsum</a></li>
<li>
<a class="dropdown-item" href="#"
>Lorem ipsum dolor sit amet</a
>
</li>
</ul>
</details>
</li>
<li class="nav-item">
<details class="nav-item">
<summary class="nav-link">Data management</summary>
<ul>
<li><a class="dropdown-item" href="#">Lorem</a></li>
<li><a class="dropdown-item" href="#">Lorem ipsum</a></li>
<li>
<a class="dropdown-item" href="#"
>Lorem ipsum dolor sit amet</a
>
</li>
</ul>
</details>
</li>
</ul>
</div>
<div class="login-nav">
<ul class="nav-list">
<li class="nav-item">
<a class="btn nav-link" href="#" aria-current="false">
<svg
class="icon-inline"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512">
<path
d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z"
></path></svg
>
Login
</a>
</li>
</ul>
</div>
</div>
<div class="nav-search">
<form action="">
<search class="search-input" role="search">
<select
class="form-select"
id="search-options"
name="search options"
aria-label="Search options">
<option value="data" selected=""> Search for data </option>
<option value="content" selected=""> Search for content </option>
</select>
<div class="input-group">
<input
class="form-control"
type="text"
aria-label="Enter keywords and submit to search ICPSR for data"
aria-describedby="nav-search"
placeholder="Search by ID, PI name, etc"
/>
<button class="btn btn-primary" id="nav-search" type="submit">
<i class="fas fa-search"></i>
<span class="visually-hidden">Search</span>
</button>
</div>
</search>
</form>
</div>
</nav>
</header> The mobile menu display is built using the Popover API. The navigation is designed to automatically switch from desktop style to the mobile style depending on the horizontal space available. This means that sites with more top level navigation items (and/or longer labels) will switch to the mobile menu style at a wider viewport width than a site wih fewer top level navigation items.
The javascript code below is required to implement the automatic switching between the mobile and desktop navigation styles.
<script is:inline>
// close menus automatically
const detailsElements = Array.from(document.querySelectorAll("details"));
document.addEventListener("click", (e) => {
if (
!detailsElements.some(
(f) => e.target instanceof Node && f.contains(e.target)
)
) {
detailsElements.forEach((f) => f.removeAttribute("open"));
}
});
detailsElements.forEach((deet) => {
deet.addEventListener("toggle", toggleOpenOneOnly);
});
function toggleOpenOneOnly() {
if (this.open) {
detailsElements.forEach((deet) => {
if (deet !== this && deet.open) deet.open = false;
});
}
}
// close all menus if focus moves to outside the navigation
const main = document.querySelector("main");
const login = document.querySelector(".login-nav");
main?.addEventListener("focusin", (event) => {
detailsElements.forEach((deet) => {
deet.open = false;
});
});
login?.addEventListener("focusin", (event) => {
detailsElements.forEach((deet) => {
deet.open = false;
});
});
// Function to check the viewport width and add/remove attributes accordingly
const html = document.querySelector("html");
const navHeader = document.querySelector("nav.header");
const offscreenElement = document.querySelector("#offscreen-nav");
const offscreenClose = document.querySelector("#offscreen-nav .btn-close");
const offscreenToggler = document.querySelector(".offscreen-toggler");
const inertElements = document.querySelectorAll(
"main, footer.site-footer, .site-banner, #skip, .department-logo, .nav-search, .offscreen-toggler"
);
// Create ResizeObserver for local-nav
function checkHasNavOverflow(
offscreenElement,
navHeader,
inertElements,
offscreenToggler,
offscreenClose
) {
// let navHasOverflow;
const navItemsArray = [];
navItemsArray.length = 0;
const localNavItems = document.querySelectorAll(
"header .local-nav > ul > li"
);
localNavItems.forEach((navItem) => {
navItemsArray.push(navItem.getBoundingClientRect().width);
});
// console.log(navItemsArray);
const totalNavItemsWidth = navItemsArray.reduce(
(accumulator, currentValue) => {
return accumulator + currentValue;
}
);
// console.log('totalNavItemsWidth', totalNavItemsWidth);
const localNavObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
let localNavWidth = entry.contentRect.width;
if (totalNavItemsWidth > localNavWidth || totalNavItemsWidth === 0) {
offscreenElement.setAttribute("popover", "auto");
navHeader.classList.remove("full");
offscreenToggler?.classList.remove("d-none");
offscreenClose?.classList.remove("d-none");
} else {
offscreenElement.removeAttribute("popover");
navHeader.classList.add("full");
offscreenToggler?.classList.add("d-none");
offscreenClose?.classList.add("d-none");
// Remove the 'inert' attribute regardless of popover state (since full nav will show)
inertElements.forEach((element) => {
element.removeAttribute("inert");
});
}
});
});
// Attach ResizeObserver
const localNav = document.querySelector(".local-nav");
localNavObserver.observe(localNav);
}
function setOffscreenState() {
if (offscreenElement) {
if (html.clientWidth < 992) {
// Set the menu to offscreen state
offscreenElement.setAttribute("popover", "true");
navHeader.classList.remove("full");
} else {
// Set the menu to full state (not offscreen)
offscreenElement.removeAttribute("popover");
navHeader.classList.add("full");
// Remove the 'inert' attribute regardless of popover state (since viewport is larger)
inertElements.forEach((element) => {
element.removeAttribute("inert");
});
}
}
}
// Check if the offscreen element popover is open using :popover-open pseudo-class
offscreenElement.addEventListener("toggle", () => {
if (offscreenElement.matches(":popover-open")) {
offscreenClose.focus();
inertElements.forEach((element) => {
element.setAttribute("inert", "true");
});
} else {
inertElements.forEach((element) => {
element.removeAttribute("inert");
});
}
});
// Attach the event listener to detect changes in the viewport size
window.addEventListener("resize", setOffscreenState);
// Call the function once to set the initial state
setOffscreenState(html, offscreenElement, navHeader, inertElements);
checkHasNavOverflow(
offscreenElement,
navHeader,
inertElements,
offscreenToggler,
offscreenClose
);
</script>