A lightweight, zero-dependency TypeScript implementation of the Priority+ navigation pattern. When horizontal space runs out, lower-priority items move into an accessible overflow menu while the first items stay visible.
- Content-aware navigation without viewport breakpoints
- Keeps items in DOM order and moves the lowest-priority items first
- Hidden measurement copy avoids layout flicker
- Reacts to resize, font loading, content changes, and dynamic items
- Accessible overflow toggle with outside-click and Escape handling
- Multiple independent instances
- Full TypeScript declarations
- ESM and UMD/CommonJS bundles
- Zero runtime dependencies
npm install @jmartsch/priority-nav
# or
bun add @jmartsch/priority-navItems are prioritized in source order: the first item has the highest priority.
<nav class="priority-nav" data-priority-nav aria-label="Primary navigation">
<div class="priority-nav__items" data-priority-nav-items>
<a href="/">Home</a>
<a href="/schedule">Schedule</a>
<a href="/speakers">Speakers</a>
<a href="/partners">Partners</a>
</div>
<div class="priority-nav__control">
<button type="button" data-priority-nav-toggle hidden>More</button>
<div class="priority-nav__menu" data-priority-nav-menu hidden>
<div class="priority-nav__overflow" data-priority-nav-overflow></div>
</div>
</div>
</nav>The items, toggle, menu, and overflow elements must be descendants of the root passed to PriorityNav.
.priority-nav {
display: flex;
align-items: center;
gap: 1rem;
}
.priority-nav__items {
display: flex;
flex: 1 1 auto;
min-width: 0;
align-items: center;
justify-content: flex-end;
gap: 1rem;
white-space: nowrap;
}
.priority-nav__control {
position: relative;
flex: 0 0 auto;
}
.priority-nav__menu {
position: absolute;
top: 100%;
right: 0;
z-index: 10;
}
.priority-nav__overflow {
display: flex;
flex-direction: column;
gap: 0.75rem;
white-space: nowrap;
}
[hidden] {
display: none !important;
}import PriorityNav from '@jmartsch/priority-nav'
const root = document.querySelector<HTMLElement>('[data-priority-nav]')
const navigation = new PriorityNav(root)The constructor initializes the instance automatically.
const navigation = new PriorityNav(root, {
items: '.my-visible-items',
overflow: '.my-overflow-items',
toggle: '.my-overflow-toggle',
menu: '.my-overflow-menu',
offsetPixels: 8,
classNames: {
overflowing: 'has-overflow',
open: 'overflow-is-open',
},
onChange: ({ visibleCount, overflowCount }) => {
console.log({ visibleCount, overflowCount })
},
})Each element option accepts either a selector resolved inside the root or an HTMLElement.
| Option | Default | Description |
|---|---|---|
items |
[data-priority-nav-items] |
Container for currently visible items |
overflow |
[data-priority-nav-overflow] |
Container receiving overflow items |
toggle |
[data-priority-nav-toggle] |
Overflow menu button |
menu |
[data-priority-nav-menu] |
Overflow menu panel |
offsetPixels |
0 |
Extra safety space subtracted before fitting items |
classNames.overflowing |
priority-nav--overflowing |
Root class while items overflow |
classNames.open |
priority-nav--open |
Root class while the menu is open |
onChange |
— | Called when visible or overflow counts change |
Rebuilds the measurement copy and recalculates the distribution. Normal DOM mutations and resizes are detected automatically.
Control the overflow menu programmatically.
Disconnects observers and listeners, removes the measurement copy, and restores all items to their original container and order.
Read the current state.
Checks for required browser APIs.
The root dispatches prioritynavchange whenever the distribution changes:
root.addEventListener('prioritynavchange', (event) => {
const { visibleCount, overflowCount } = (event as CustomEvent).detail
console.log({ visibleCount, overflowCount })
})The overflow button receives a data-count attribute with the current number of hidden items.
The package targets modern Chrome, Firefox, Safari, and Edge. It requires ResizeObserver, MutationObserver, and requestAnimationFrame.
Importing the package during SSR is safe; constructing an instance must happen in the browser.
bun install
bun run verify
bun run build
npm pack --dry-runThe package is configured as the public scoped package @jmartsch/priority-nav.
Because npm only allows trusted-publisher configuration after a package exists, publish the initial version manually:
npm login
npm publish --access publicAfter the initial release, configure jmartsch/priority-nav and .github/workflows/publish.yml as the package's trusted GitHub Actions publisher on npm. Subsequent GitHub releases are then published automatically with provenance.
MPL-2.0