Component

Scroll menu

<DomScrollMenu>

A smooth-scrolling table of contents that keeps real fragment links highlighted as their target headings move through a page or scroll container.

Demo

Basic

Pass each fragment href directly to DomScrollMenuItem and it renders the real anchor for you. Smooth scrolling is enabled by default.

Overview

Scroll the panel or select this section from the menu.

Installation

Scroll the panel or select this section from the menu.

Usage

Scroll the panel or select this section from the menu.

Basic.vuevue
vue
<script setup>
import { DomScrollMenu, DomScrollMenuItem } from '@getdom/studio/vue';

const sections = [
	{ id: 'basic-scroll-menu-overview', label: 'Overview' },
	{ id: 'basic-scroll-menu-installation', label: 'Installation' },
	{ id: 'basic-scroll-menu-usage', label: 'Usage' },
];
</script>

<template>
	<div class="grid w-full gap-6 lg:grid-cols-[13rem_minmax(0,1fr)]">
		<aside class="lg:pt-2">
			<DomScrollMenu scroll-root="#basic-scroll-menu-content">
				<DomScrollMenuItem
					v-for="section in sections"
					:key="section.id"
					:href="`#${section.id}`"
				>
					{{ section.label }}
				</DomScrollMenuItem>
			</DomScrollMenu>
		</aside>

		<div
			id="basic-scroll-menu-content"
			class="h-[24rem] overflow-y-auto rounded-2xl border border-border bg-canvas px-6"
		>
			<section
				v-for="section in sections"
				:id="section.id"
				:key="section.id"
				class="min-h-64 scroll-mt-6 border-b border-border py-8 last:min-h-[20rem] last:border-b-0"
			>
				<h2 class="text-2xl font-semibold tracking-tight text-canvas-fg">
					{{ section.label }}
				</h2>
				<p class="mt-3 max-w-xl text-sm leading-6 text-muted-fg">
					Scroll the panel or select this section from the menu.
				</p>
			</section>
		</div>
	</div>
</template>

Demo

Custom link markup

Omit the href prop when you need to supply the anchor yourself. The item discovers the fragment link and exposes active state for richer content.

Section 1

Overview

What the component watches. Scroll this panel or use the custom fragment link in the menu.

Section 3

Scroll containers

Viewport or nested panel. Scroll this panel or use the custom fragment link in the menu.

Section 4

Accessibility

Current-location semantics. Scroll this panel or use the custom fragment link in the menu.

vue
<script setup>
import { ref } from 'vue';
import { DomScrollMenu, DomScrollMenuItem } from '@getdom/studio/vue';

const activeHref = ref('');
const sections = [
	{
		id: 'custom-scroll-menu-overview',
		label: 'Overview',
		description: 'What the component watches',
		depth: 0,
	},
	{
		id: 'custom-scroll-menu-fragment-links',
		label: 'Fragment links',
		description: 'Native URLs and browser history',
		depth: 1,
	},
	{
		id: 'custom-scroll-menu-scroll-root',
		label: 'Scroll containers',
		description: 'Viewport or nested panel',
		depth: 1,
	},
	{
		id: 'custom-scroll-menu-accessibility',
		label: 'Accessibility',
		description: 'Current-location semantics',
		depth: 0,
	},
];
</script>

<template>
	<div class="grid w-full gap-6 lg:grid-cols-[16rem_minmax(0,1fr)]">
		<aside class="lg:pt-2">
			<DomScrollMenu
				v-model="activeHref"
				scroll-root="#custom-scroll-menu-content"
			>
				<DomScrollMenuItem
					v-for="section in sections"
					:key="section.id"
					:depth="section.depth"
				>
					<template #default="{ active }">
						<a
							:href="`#${section.id}`"
							:title="`Jump to ${section.label}`"
						>
							<span class="flex items-start justify-between gap-3">
								<span class="min-w-0">
									<span class="block">{{ section.label }}</span>
									<span class="mt-0.5 block text-xs font-normal text-muted-fg">
										{{ section.description }}
									</span>
								</span>
								<span
									v-if="active"
									class="mt-0.5 shrink-0 rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary"
								>
									Reading
								</span>
							</span>
						</a>
					</template>
				</DomScrollMenuItem>
			</DomScrollMenu>
		</aside>

		<div
			id="custom-scroll-menu-content"
			class="h-[28rem] overflow-y-auto rounded-2xl border border-border bg-canvas px-6"
		>
			<section
				v-for="(section, index) in sections"
				:id="section.id"
				:key="section.id"
				class="min-h-72 scroll-mt-6 border-b border-border py-8 last:min-h-[24rem] last:border-b-0"
			>
				<p class="text-xs font-semibold uppercase tracking-[0.18em] text-muted-fg">
					Section {{ index + 1 }}
				</p>
				<h2 class="mt-2 text-2xl font-semibold tracking-tight text-canvas-fg">
					{{ section.label }}
				</h2>
				<p class="mt-3 max-w-xl text-sm leading-6 text-muted-fg">
					{{ section.description }}. Scroll this panel or use the custom fragment link in the menu.
				</p>
			</section>
		</div>
	</div>
</template>

Scroll menu reference

Props

Control props

NameTypeTSDefaultDescription
modelValuestringstring''Active fragment href. Use v-model when the surrounding page needs the current section.
asstringstring'nav'Root element rendered around the scroll menu.
listAsstringstring'ul'Element rendered around the menu items.
labelstringstring'On this page'Accessible navigation label and default visible heading.
showLabelbooleanbooleantrueShow the visual navigation label above the item list.
scrollRootstring | objectRecord<string, unknown>Optional scroll-container element or CSS selector. By default the nearest scrollable ancestor of the first target is used.
scrollBehaviorstringstring'smooth'Fragment navigation behavior. Smooth scrolling respects the user’s reduced-motion preference.
rootMarginstringstring'0px 0px -70% 0px'IntersectionObserver root margin defining the activation band. Use a negative top value to account for sticky headers.

Auto-generated from Scroll menu.props and inline _edit hints.

Events

NamePayloadDescription
@update:modelValuehrefEmitted when scrolling or navigation changes the active fragment link.
@change{ href, target }Emitted with the active fragment and its target element.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Slots

NameScopeDescription
#(default){ activeHref }DomScrollMenuItem rows or equivalent custom content.
#label{ label }Custom visible navigation label.

Scroll menu item reference

Props

Control props

NameTypeTSDefaultDescription
hrefstringstring''Optional fragment href shorthand, including the leading #. Omit it when the slot provides its own anchor.
asstringstring'li'Element wrapping the anchor. Use li with the default DomScrollMenu list.
depthnumbernumber0Visual nesting depth for subordinate page headings.
disabledbooleanbooleanfalseRemove the fragment link from navigation and scrollspy selection.

Auto-generated from Scroll menu item.props and inline _edit hints.

Events

NamePayloadDescription
@navigate{ href, active }Emitted when the fragment link is activated with an unmodified primary click.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Slots

NameScopeDescription
#(default){ active, href }Link contents when href is set, or one custom fragment anchor when href is omitted.