Waypoint / Scrollspy

Highlights the navigation entry for the section currently in view as the user scrolls, driven by the useWaypoint composable.

Sidebar Navigation Scrollspy

Introduction

Waypoint automatically highlights navigation links as the user scrolls through corresponding content sections. It observes section visibility and updates the .is-active class on the matching nav link.

Setup

Add data-vd-waypoint-nav to the nav container and point it to the scrollable parent. Each link href should reference a section ID within the scroll container.

Usage

Waypoint supports vertical scroll containers of any height. Links are highlighted top-down - the topmost visible section wins. Works with both window scroll and overflow containers.

API

Call useWaypoint(root) once in setup — it wires every [data-vd-waypoint-nav] inside the root and tears down its observers automatically on unmount; there are no manual refresh/destroy calls.

<!-- Sidebar scrollspy navigation -->
<nav class="vd-waypoint-nav"
     data-vd-waypoint-nav="#scroll-container">
  <a href="#section-1" class="is-active">Section 1</a>
  <a href="#section-2">Section 2</a>
  <a href="#section-3">Section 3</a>
</nav>

<div id="scroll-container" style="overflow-y:auto;">
  <div id="section-1">...</div>
  <div id="section-2">...</div>
  <div id="section-3">...</div>
</div>
Underline Variant

Overview of the current section with a compact underline indicator.

<nav class="vd-waypoint-nav vd-waypoint-underline"
     data-vd-waypoint-nav="window">
  <a href="#overview" class="is-active">Overview</a>
  <a href="#features">Features</a>
  <a href="#pricing">Pricing</a>
</nav>
Pill Variant

Getting started stays selected while the card remains anchored in place.

<nav class="vd-waypoint-nav vd-waypoint-pill"
     data-vd-waypoint-nav="#content">
  <a href="#start" class="is-active">Getting Started</a>
  <a href="#components">Components</a>
  <a href="#utilities">Utilities</a>
</nav>
API Reference

Wiring

import { ref } from 'vue';
import { useWaypoint } from "@vanduo-oss/vd3";

const root = ref<HTMLElement | null>(null);
useWaypoint(root);   // wires [data-vd-waypoint-nav] inside root; cleanup on unmount

// react to the active section changing
root.value?.addEventListener('waypoint:change', (e) => {
  console.log('active', e.detail.activeId);
});

CSS Classes

ClassDescriptionType
.vd-waypoint-navBase scrollspy navigation container — requiredBase
.vd-waypoint-borderAdds a left border highlight on the active link (sidebar style)Modifier
.vd-waypoint-pillRounded pill-shaped background on the active linkModifier
.vd-waypoint-underlineBottom underline indicator on the active link (tab style)Modifier
.is-activeApplied automatically to the currently visible section's linkState

Data Attributes

AttributeDescriptionDefault
data-vd-waypoint-navSelector for the scrollable container to observe (e.g. "#content" or "window")window
data-vd-waypoint-offsetPixel offset from the top to trigger activation earlier or later0
data-vd-scrollspy-navAlias for data-vd-waypoint-nav

Composable API

SymbolDescription
useWaypoint(root)Composable — wires every [data-vd-waypoint-nav] inside the root ref; highlights the link of the topmost visible section. Call once in setup().
(automatic cleanup)The IntersectionObserver and click listeners are removed on component unmount.

Events

EventDescriptionDetail
waypoint:changeFired on the nav element when the active section changes{ activeId, link }
Accessibility
  • Navigation container uses role="navigation" with aria-label="Table of contents"
  • Active link receives aria-current="true" in addition to the .is-active class
  • Links use standard anchor elements for native keyboard navigation and focus management
  • Smooth scrolling respects prefers-reduced-motion media query
  • Section targets use semantic heading hierarchy for screen reader landmark navigation