Skip to content

feat(web): add Table of Contents - #291

Open
danestves wants to merge 1 commit into
panel-ui:mainfrom
danestves:feat/web-table-of-contents
Open

danestves wants to merge 1 commit into
panel-ui:mainfrom
danestves:feat/web-table-of-contents

Conversation

@danestves

Copy link
Copy Markdown
Contributor

What this changes

Adds TableOfContents, a scroll-aware anchor list for long pages, to the web collection (panelui-web) — the component, its docs page, registry item, demos, and generated agent-skill reference.

Why

The navigation category had no way to show where you are inside a long page. The native package has SectionRail, but that is a floating position indicator rather than a linked outline. TableOfContents fills the gap: a real <ol> of anchors that tracks the section at the reading line, with one indicator that moves to it.

import { TableOfContents } from 'panelui-web/table-of-contents';

<TableOfContents defaultValue="install" observe offset={96}>
  <TableOfContents.Title>On this page</TableOfContents.Title>
  <TableOfContents.List>
    <TableOfContents.Item value="intro">
      <TableOfContents.Link href="#intro">Introduction</TableOfContents.Link>
    </TableOfContents.Item>
    <TableOfContents.Item value="install" depth={1}>
      <TableOfContents.Link href="#install">Installation</TableOfContents.Link>
    </TableOfContents.Item>
  </TableOfContents.List>
</TableOfContents>

Type of change

  • Bug fix
  • New component
  • New prop, variant, or behaviour on an existing component
  • Breaking change — an existing API is renamed, removed, or behaves differently
  • Documentation only
  • Internal: refactor, tooling, CI

How it was tested

Web only — the component has no iOS or Android surface.

Command Result
npm run typecheck --workspace=panelui-web pass
npm run typecheck --workspace=docs pass (after npx fumadocs-mdx generated the gitignored .source/)
node --test packages/web/test/package-contract.test.mjs packages/web/test/table-of-contents-spy.test.mjs 16 pass
node --test apps/docs/test/web-registry.test.mjs apps/docs/test/registry-budgets.test.mjs 15 pass
node --test apps/docs/test/*.test.mjs 47 pass
npm run build --workspace=panelui-web pass (202 files)
node scripts/verify-registry-budgets.mjs pass — web registry 75 files / 2,247,708 bytes
SSR smoke render via react-dom/server pass
  • Web (docs app)
  • iOS
  • Android
  • Light and dark themes — both captured below.

Screenshots or recording

Captured from the running docs page at 2× — the docs-style ToC, nesting, a scrolling article with live spy, and dark.

Overview Nested
Table of Contents beside a guide page Nested table of contents with indentation
Live scroll spy Dark
Table of Contents tracking a scrolling article Table of Contents in dark mode

Checklist

  • npm run typecheck passes
  • npm run build passes
  • Commits follow Conventional Commits
  • Animations use Reanimated on the UI thread — N/A for the web collection; motion is CSS-based and respects prefers-reduced-motion
  • No hardcoded colours — everything resolves through the theme tokens
  • Interactive parts carry the web equivalents: nav landmark, aria-current="location", <ol>/<li> semantics, focus rings
  • Every new part accepts className

If this touches a component's API

  • web-meta.json and web-usage/table-of-contents.json are updated, with addedIn set to 0.15.0
  • build-web-registry, gen-web, build-web-skill, and build-skill-assets have been run and the generated files are committed
  • Props tables read correctly — they come from the TypeScript interfaces and their JSDoc

If this adds a component

  • Exported from packages/web/src/index.ts, with its types and named twins for every dotted part
  • Registry item, docs page, sidebar entry, demo map, and agent-skill reference generated and committed

Notes for the reviewer

  • The spy collects its targets when its effect runs — on mount, or when observe, offset or root changes. Anchors added to an already-mounted tree without one of those changing are not picked up. A stable root ref is the intended pattern; the docs note this.
  • root is optional: the viewport is the default scroll container. Pass an element or ref for an article with its own scroller.
  • Decoration is outside the accessibility tree: the rail and travelling segment are pseudo-elements, so the <ol> keeps only <li> children and screen readers hear links, not chrome.
  • Version: addedIn is 0.15.0 while panelui-web is still 0.14.0, so the docs page carries the "new" badge until the release lands. No version bump in this PR, per CONTRIBUTING.
  • No new dependencies: React, @base-ui/react and tailwind-variants, all already declared.

@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 2 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 596e8101-99cd-4e1a-8a1f-5bc92f359c7d
Commits

Reviewing files that changed from the base of the PR and between e9b83e7 and e258c76.

Files ignored due to path filters: 1

  • apps/docs/components/web-demos/index.generated.tsx is excluded by !**/*.generated.*
Files selected for processing: 20

  • apps/docs/components/web-demos/table-of-contents/controlled.tsx
  • apps/docs/components/web-demos/table-of-contents/nested.tsx
  • apps/docs/components/web-demos/table-of-contents/overview.tsx
  • apps/docs/components/web-demos/table-of-contents/scrolling.tsx
  • apps/docs/components/web-demos/table-of-contents/thumb.tsx
  • apps/docs/content/docs/web/components/index.mdx
  • apps/docs/content/docs/web/components/meta.json
  • apps/docs/content/docs/web/components/table-of-contents.mdx
  • apps/docs/public/r/web/registry.json
  • apps/docs/public/r/web/table-of-contents.json
  • apps/docs/public/skills/panelui-web/components.md
  • apps/docs/public/skills/panelui-web/components/table-of-contents.md
  • apps/docs/scripts/web-meta.json
  • apps/docs/scripts/web-usage/table-of-contents.json
  • packages/web/src/components/table-of-contents/index.tsx
  • packages/web/src/components/table-of-contents/spy.ts
  • packages/web/src/index.ts
  • packages/web/test/table-of-contents-spy.test.mjs
  • skills/panelui-web/components.md
  • skills/panelui-web/components/table-of-contents.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant