Components Scroll Spy <arc-scroll-spy> Guidelines
When to use
- Place ScrollSpy in a sidebar-right or sticky aside column next to the scrollable content
- Give every target section a unique id attribute that matches the spy-link target
- Set the offset prop to match the height of your sticky header or TopBar
- Listen for the arc-change event to synchronize breadcrumbs, analytics, or URL hash updates
- Keep spy-link labels short -- they should match or abbreviate section headings
When not to use
- Use ScrollSpy for primary site navigation -- it is for in-page section tracking only
- Forget to import arc-spy-link; ScrollSpy depends on it to collect its link definitions
- Place ScrollSpy inside a scrollable container other than the document -- the observer watches document-level intersections
- Add dozens of spy-links to a single ScrollSpy; more than 10-12 links make the list hard to scan
- Omit the target attribute on spy-links -- they will be silently ignored by the observer
Features
- IntersectionObserver-based scroll tracking with zero scroll-event overhead
- Declarative link registration via <arc-spy-link target="id"> children
- Sticky positioning with automatic height capping to prevent overflow
- Smooth-scroll click navigation to target sections
- Active link highlighting with accent-primary background and aria-current="true"
- Configurable offset prop to account for sticky headers of varying heights
- arc-change custom event dispatched when the active section changes
- Thin scrollbar styling for long tables of contents
Preview
Overview
Installation
Usage
API Reference
Examples
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<arc-scroll-spy>
<arc-spy-link target="section-1">Section 1</arc-spy-link>
<arc-spy-link target="section-2">Section 2</arc-spy-link>
</arc-scroll-spy>
import { ScrollSpy, SpyLink } from '@arclux/arc-ui-react';
<ScrollSpy>
<SpyLink target="section-1">Section 1</SpyLink>
<SpyLink target="section-2">Section 2</SpyLink>
</ScrollSpy>
<script setup>
import { ScrollSpy, SpyLink } from '@arclux/arc-ui-vue';
</script>
<template>
<ScrollSpy>
<SpyLink target="section-1">Section 1</SpyLink>
<SpyLink target="section-2">Section 2</SpyLink>
</ScrollSpy>
</template>
<script>
import { ScrollSpy, SpyLink } from '@arclux/arc-ui-svelte';
</script>
<ScrollSpy>
<SpyLink target="section-1">Section 1</SpyLink>
<SpyLink target="section-2">Section 2</SpyLink>
</ScrollSpy>
import { Component } from '@angular/core';
import { ScrollSpy, SpyLink } from '@arclux/arc-ui-angular';
@Component({
imports: [ScrollSpy, SpyLink],
template: `
<ScrollSpy>
<SpyLink target="section-1">Section 1</SpyLink>
<SpyLink target="section-2">Section 2</SpyLink>
</ScrollSpy>
`,
})
export class MyComponent {}
import { ScrollSpy, SpyLink } from '@arclux/arc-ui-solid';
<ScrollSpy>
<SpyLink target="section-1">Section 1</SpyLink>
<SpyLink target="section-2">Section 2</SpyLink>
</ScrollSpy>
import { ScrollSpy, SpyLink } from '@arclux/arc-ui-preact';
<ScrollSpy>
<SpyLink target="section-1">Section 1</SpyLink>
<SpyLink target="section-2">Section 2</SpyLink>
</ScrollSpy>
API
| Prop | Type | Default | Description |
active | string | '' | The id of the currently active section. Reflects to an attribute and updates automatically as the user scrolls. |
offset | number | 80 | Pixel offset from the top of the viewport used in the IntersectionObserver rootMargin. Increase this value to account for taller sticky headers. |
Events
| Event | Description |
arc-change | Fired when the active spy target changes during scroll |
SpyLink
<arc-spy-link> Navigation anchor that highlights when its target section is in view.
| Prop | Type | Default | Description |
target | string | — | ID of the section to observe |
level | number | 0 | Nesting depth for visual indentation. Level 0 links render at default size; level 1+ links are indented and use a smaller font size. |