Phoundry UI

Anchor Experimental

Scroll-aware table-of-contents navigation. Provide items manually or point it at a scrollable container to auto-discover headings.

import { Anchor } from 'phoundry-ui';

Basic (Manual Items)

Introduction

Welcome to the Anchor component. It provides scroll-aware navigation for long pages.

Installation

Install via npm: npm install phoundry-ui

Usage

Import the component and pass your items array.

API Reference

See the props table below for full API documentation.

Show code
<Anchor items={[
  { key: 'intro', href: '#intro', title: 'Introduction' },
  { key: 'install', href: '#install', title: 'Installation' },
  { key: 'usage', href: '#usage', title: 'Usage' },
  { key: 'api', href: '#api', title: 'API Reference' },
]} />

Auto-Discovery

Overview

The auto mode scans the container for headings and builds the item list.

Configuration

Use headingSelector to control which elements are picked up.

Scroll Tracking

Active state updates automatically as the user scrolls.

Events

onChange fires whenever the active link changes.

Show code
<Anchor target={scrollContainer} headingSelector="h3" />

Horizontal

Introduction

Horizontal anchor for compact navigation.

Installation

Same install process.

Usage

Set direction to horizontal.

API Reference

Full API below.

Show code
<Anchor
  items={items}
  direction="horizontal"
/>

Nested Items

Getting Started

Prerequisites

Node.js 18+, Svelte 5.

Setup

Run the init script.

Components

Button

Primary action trigger.

Input

Text input field.

Select

Dropdown selector.

Show code
<Anchor items={[
  { key: 'start', href: '#start', title: 'Getting Started',
    children: [
      { key: 'prereqs', href: '#prereqs', title: 'Prerequisites' },
      { key: 'setup', href: '#setup', title: 'Setup' },
    ]},
  { key: 'components', href: '#components', title: 'Components',
    children: [
      { key: 'button', href: '#button', title: 'Button' },
      { key: 'input', href: '#input', title: 'Input' },
    ]},
]} />

Offsets, replaceState, onChange

offsetTop shifts which heading counts as “active” while scrolling. targetOffset controls the gap after clicking (defaults to offsetTop). replace avoids cluttering history on each jump.

Introduction

Scroll to see onChange update.

Installation

Middle segment.

Usage

Another section.

API Reference

Last segment.

Show code
<Anchor
  items={items}
  target={scrollEl}
  offsetTop={24}
  targetOffset={16}
  replace
  affix={false}
  onChange={(href) => (active = href)}
/>

Props

PropTypeDefaultDescription
items AnchorItem[]Explicit anchor items (manual mode).
target HTMLElementScrollable container to scan for headings (auto mode).
headingSelector string'h2, h3'CSS selector for discoverable headings in auto mode.
direction 'vertical' | 'horizontal''vertical'Layout direction. Horizontal disables nesting.
offsetTop number0Pixel offset for calculating the active section position.
targetOffset numberScroll-to offset when clicking a link. Defaults to offsetTop.
bounds number5Bounding distance in px for active section detection.
affix booleantrueStick the anchor with position: sticky.
replace booleanfalseUse replaceState instead of pushState for URL hash.
onChange (activeHref: string) => voidFires when the highlighted link changes (hash string, e.g. `#section-id`).
onclick (e: MouseEvent, item: AnchorItem) => voidClick handler for anchor links.
getCurrentAnchor (href: string) => stringOverride which link is highlighted.
class stringAdditional CSS classes.

AnchorItem

PropTypeDefaultDescription
key requiredstringUnique identifier.
href requiredstringHash target (e.g. #section-id).
title requiredstringDisplay text.
children AnchorItem[]Nested child links (vertical only).

Usage tips

  • Pass items for full control, or target to auto-discover headings from a container.
  • In auto mode, headings without id attributes get one assigned automatically (slugified from text).
  • Use headingSelector to control which heading levels are included (e.g. "h2, h3" or just "h3").
  • Set direction="horizontal" for compact inline navigation. Nesting is disabled in horizontal mode.
  • The component renders nothing when there are no items — safe to include in layouts unconditionally.
  • Add to a layout file with target={mainEl} to get a page ToC on every page automatically.