Skip to content

Navbar with Sliding Active Indicator

Dark responsive navbar with a glowing underline that slides between links on hover, focus and click, built with CSS custom properties and vanilla JS.

Live preview

Open in new tab

Code

A dark navigation bar in pure CSS and vanilla JavaScript with a glowing amber underline that slides between the links. The indicator follows the mouse as a preview, glides back to the active page when the pointer leaves, and moves for good when a link is clicked. It fits SaaS sites, landing pages and documentation headers.

How it works

The root header.sn-nav holds .sn-bar, a CSS grid with three areas: the .sn-brand logo, the nav.sn-menu with its ul.sn-list of .sn-link anchors, and the .sn-cta button. Inside the menu sits a single span.sn-indicator: an absolutely positioned gradient bar with a soft light cone drawn by its ::before pseudo-element.

On DOMContentLoaded the script initializes every .sn-nav on the page. moveTo() reads the offsetLeft and offsetWidth of a link and writes them into the custom properties --sn-x and --sn-w on the root, which the indicator uses for transform and width. Both properties transition, so the bar stretches and slides at once. The script listens to mouseenter and focus on each link, mouseleave and focusout on the menu, and click to call setActive(), which moves aria-current="page" and fires an sn:change event with detail.link and detail.href.

While a non-active link is previewed, the root gets .sn-previewing and the bar dims. Transitions are only enabled after the first position is set (the .sn-ready class), so the bar does not fly in on load. Without JavaScript the active link shows a static inset underline instead.

Below 768px the menu moves to its own row and scrolls horizontally; the indicator scrolls with the links, and a clicked link is centered.

Customization

  • --sn-accent and --sn-accent-2 on .sn-nav: the gradient and glow of the indicator and the outline color of .sn-cta.
  • --sn-indicator-height: thickness of the sliding bar, 3px by default; try 100% with a low-opacity accent for a full highlight block.
  • --sn-speed and --sn-ease: duration and easing of the slide; a springy curve such as cubic-bezier(0.34, 1.56, 0.64, 1) adds a slight overshoot.
  • data-sn-follow="false" on .sn-nav: the indicator stays on the active link and only moves on click or keyboard focus.
  • --sn-bg, --sn-text and --sn-text-active: background, idle link color and the color of the hovered or active link.
  • Change the aria-current="page" link in the markup to set the page that is active on load.
  • Remove the .sn-indicator::before rule if you want a flat bar without the light cone.

Accessibility

The links live in a nav landmark with aria-label="Main", and the current page carries aria-current="page", which the script keeps in sync. The indicator is decorative and marked aria-hidden="true". Keyboard focus moves the indicator just like hover, and every link, the logo and the call to action have a visible :focus-visible outline. With prefers-reduced-motion: reduce all transitions are switched off and scrolling is instant.

Usage notes

Each .sn-nav keeps its own state, so several navbars can share a page. The indicator is recalculated on resize and after web fonts load.

Requirements

No dependencies. Pure HTML, CSS and JavaScript.

Navbar with Sliding Active Indicator – Pure CSS navbars component screenshot
Navbar with Sliding Active Indicator · Pure CSS Navbars

More Navbars

Navbar with Centered Logo – Tailwind CSS navbars preview

Navbar with Centered Logo

Dark Tailwind CSS navbar with links split around a centered logo, animated underlines and an accessible slide-down mobile menu.