Sticky Navv0.0.6Documentation Under Review
A flexible navigation component that sticks to the top of the viewport, built with Radix UI Navigation Menu primitives. Supports tab-based navigation with active states, gradient styling, and responsive behavior.
Installation
Install the StickyNav component from the Catalyst component library:
npm install @pmi/catalyst-sticky-navOverview
The Sticky Nav component provides a sticky navigation interface built on Radix UI's Navigation Menu primitives. It features:
- Sticky positioning - Stays at the top of the viewport while scrolling
- Active state management - Highlights the current section with visual indicators
- Gradient styling - Supports customizable gradient border effects
- Responsive design - Adapts to mobile views with dropdown select menus
- Intersection Observer - Automatically updates active state based on scroll position
- Accessible - Built with WCAG 2.1 AA compliant Radix UI primitives
Examples
Basic Tab Navigation
A basic sticky navigation with tab-style links and active state tracking:
Gradient Primary
Sticky navigation with a primary gradient border effect on the active tab:
Gradient Secondary
Sticky navigation with a secondary gradient border effect:
Gradient Tertiary
Sticky navigation with a tertiary gradient border effect:
Responsive Navigation with Intersection Observer
A complete responsive navigation example that uses Intersection Observer to track scroll position and switches to a dropdown menu on mobile:
Overview
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit reprehenderit tenetur obcaecati amet ipsum esse mollitia impedit.
What you'll learn
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ab sed saepe nobis, facilis necessitatibus doloremque optio molestiae dolores natus qui earum corrupti.
You may also like
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis molestiae omnis ratione nobis nostrum corporis sint iste quam totam quod.
Component Structure
The StickyNav component is composed of several sub-components that work together:
- StickyNavRoot - The container component that wraps the entire navigation
- StickyNavList - The list container for navigation items
- StickyNavItem - Individual navigation item wrapper
- StickyNavLink - The clickable link element with active state support
- StickyNavContent - Content area for dropdown menus (optional)
- StickyNavTrigger - Trigger element for dropdown menus (optional)
- StickyNavSub - Sub-navigation container (optional)
- StickyNavViewport - Viewport for dropdown content (optional)
- StickyNavIndicator - Visual indicator for active items (optional)
API Reference
StickyNavRoot
Prop
Type
StickyNavList
Prop
Type
StickyNavItem
Prop
Type
StickyNavLink
Prop
Type
Styling with Tailwind
The Sticky Nav component uses Tailwind CSS design tokens and data attributes for styling:
Active State Styling
Use the data-[active] attribute to style active navigation items:
<StickyNavLink
className={cn(
'text-[color:var(--text-neutral-soft)]',
'data-[active]:text-[var(--text-primary)]',
'data-[active]:border-b-[length:var(--border-md)]',
'data-[active]:border-[var(--border-off-black-dark)]'
)}
active={isActive}
>
Tab Name
</StickyNavLink>Gradient Borders
Apply gradient borders using CSS custom properties:
<StickyNavLink
className={cn(
'border-b-[length:var(--border-md)]',
'data-[active]:[border-image:linear-gradient(to_right,var(--gradient-medium-primary-from),var(--gradient-medium-primary-to))_1]'
)}
active={isActive}
>
Tab Name
</StickyNavLink>Design Tokens
Common design tokens used in Sticky Nav:
- Colors:
--text-primary,--text-neutral-soft,--surface-primary - Borders:
--border-md,--border-off-black-dark,--components-navigation-menu-border - Spacing:
--scale-24,--scale-56,--scale-80,--scale-96,--scale-160 - Gradients:
--gradient-medium-primary-from/to,--gradient-medium-secondary-from/to,--gradient-medium-tertiary-from/to
Patterns and Best Practices
Intersection Observer Pattern
Use Intersection Observer to automatically update the active state based on scroll position:
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(`#${entry.target.id}`);
}
});
},
{
threshold: 1,
rootMargin: '0px 0px -75%',
}
);
const anchors = sections.map((section) => document.querySelector(section.href)!);
anchors.forEach((node) => observer.observe(node));
return () => {
anchors.forEach((node) => observer.unobserve(node));
};
}, [sections]);Hash Change Handling
Track URL hash changes to update active state:
useEffect(() => {
const hashChangeHandler = () => {
const hashValue = window.location.hash;
const tab = tabs.find((tab) => tab.href === hashValue);
if (tab) {
setActiveTab(hashValue);
}
};
window.addEventListener('hashchange', hashChangeHandler);
return () => {
window.removeEventListener('hashchange', hashChangeHandler);
};
}, [tabs]);Responsive Navigation
Provide a mobile-friendly dropdown alternative:
{
/* Desktop */
}
<div className="hidden sm:block">
<StickyNavRoot>{/* Navigation items */}</StickyNavRoot>
</div>;
{
/* Mobile */
}
<div className="sm:hidden">
<SelectRoot value={activeSection} onValueChange={changeSection}>
{/* Dropdown menu */}
</SelectRoot>
</div>;Scroll Offset
Add scroll margin to account for sticky navigation height:
<h2 id="section-id" className="scroll-mt-[var(--scale-96)] sm:scroll-mt-[var(--scale-160)]">
Section Title
</h2>Accessibility
The Sticky Nav component is built with accessibility in mind, using Radix UI's Navigation Menu primitive:
WCAG 2.1 AA Compliance
- Keyboard Navigation: Full keyboard support with Tab, Arrow keys, Escape, and Enter
- Focus Management: Visible focus indicators and logical focus order
- Screen Reader Support: Proper ARIA labels and roles for assistive technologies
- Color Contrast: Design tokens ensure sufficient contrast ratios (4.5:1 minimum)
Keyboard Interactions
| Key | Description |
|---|---|
Tab | Move focus to the next focusable element |
Shift + Tab | Move focus to the previous focusable element |
Enter / Space | Activate the focused link |
Arrow Left/Right | Navigate between navigation items (horizontal orientation) |
Arrow Up/Down | Navigate between navigation items (vertical orientation) |
Home | Move focus to the first navigation item |
End | Move focus to the last navigation item |
ARIA Attributes
The component automatically applies appropriate ARIA attributes:
role="navigation"- Identifies the navigation landmarkaria-current="page"- Indicates the current active itemaria-label- Provides accessible names for navigation regions
Best Practices
- Provide meaningful link text - Use descriptive labels that clearly indicate the destination
- Maintain focus visibility - Ensure focus indicators are clearly visible
- Use semantic HTML - The component uses proper nav and link elements
- Test with screen readers - Verify navigation is announced correctly
- Ensure sufficient contrast - Active and inactive states should have adequate color contrast
Related Components
- Select - Used in responsive navigation for mobile dropdowns
- Separator - Visual separator between content sections
- Icons - Icons used in navigation UI elements
See Also
- Navigation Menu Primitive - Radix UI Navigation Menu documentation
- Intersection Observer API - MDN documentation
- WCAG Navigation Guidelines - W3C accessibility standards
Pagination
A navigation component for traversing paged content. Built on Radix UI Slot with CVA variants, accessible ARIA semantics, and full asChild polymorphic composition.
Tabs
Interactive tab navigation for organizing content into separate views with accessible keyboard navigation and state management.