Skip to content

feat(portal): add title anchors and content page table of contents#86

Draft
BatLeDev wants to merge 6 commits into
masterfrom
feat-content-page-toc
Draft

feat(portal): add title anchors and content page table of contents#86
BatLeDev wants to merge 6 commits into
masterfrom
feat-content-page-toc

Conversation

@BatLeDev

@BatLeDev BatLeDev commented Jun 26, 2026

Copy link
Copy Markdown
Member

Add title anchors and a content-page table of contents to the portal.

  • Per-title anchor: setting a slug makes the title copiable (hover copy-link button with a native title, RGAA) and gives the heading a stable id; the URL hash is set on click and scrolled to on load (header offset handled via scroll-margin-top)
  • Opt-in table of contents: an anchored title is listed in the page's table of contents only when its "show in the table of contents" toggle is enabled, with an optional shorter label overriding the displayed title
  • Table of contents rendering: navigation drawer top-right on lg+, collapses to a button + dropdown menu otherwise; hidden on full-width pages; shown only when at least one anchored title opts in
  • Header and navigation bar stay a single app bar (the navigation bar is its extension) so they don't overlap during SSR; the table of contents, its FAB and the navigation drawer are positioned below the navigation bar even when the header hides on scroll, the offset being derived manually since Vuetify collapses --v-layout-top to 0 in that case
  • config._toc is computed server-side from the opted-in anchored titles in document order, reusing the shared element traversal so titles nested in columns or catalog blocks are included; anchor slugs are validated server-side (canonical format + unique per page)
  • Long titles wrap instead of being truncated (portals_v1 regression)
  • API and e2e tests (slug validation, recursive collection of opted-in titles, navigation-drawer rendering, copy anchors, single app bar kept below which the table of contents stays on scroll)

Why: a standing TODO to let a title block carry an anchor and to display a navigable table of contents on content pages.

Heads-up:

  • layout-page.vue is a shared layout used by many pages — the table of contents renders only when at least one title opts into it, otherwise the layout is unchanged.
  • _toc is computed on create/patch only, so existing pages show no table of contents until their next edit + publish (the portal degrades gracefully via _toc ?? []). No backfill migration is included.

@github-actions github-actions Bot added feature and removed feature labels Jun 26, 2026
@BatLeDev BatLeDev force-pushed the feat-content-page-toc branch from d2c2b02 to 59ec325 Compare June 26, 2026 13:08
@github-actions github-actions Bot added feature and removed feature labels Jun 26, 2026
@BatLeDev BatLeDev force-pushed the feat-content-page-toc branch 2 times, most recently from b227b2e to d37935f Compare June 29, 2026 12:02
@github-actions github-actions Bot added feature and removed feature labels Jun 29, 2026
@BatLeDev BatLeDev force-pushed the feat-content-page-toc branch 4 times, most recently from fa918d0 to 7896f99 Compare June 30, 2026 14:38
@github-actions github-actions Bot added feature and removed feature labels Jun 30, 2026
BatLeDev added 5 commits June 30, 2026 16:53
- title block: optional anchor (enable switch) + opt-in table-of-contents
  entry with a short label override
- the anchor slug is generated from the title content at build and
  deduplicated per page, so duplicating a title just works
- copiable anchor on hover with native title (RGAA)
- URL hash set on click and scrolled-to on load (scroll-margin-top = header offset)
- sommaire: sticky aside on lg+, collapses to a button + menu otherwise
- sommaire hidden on full-width pages, shown only when a title is anchored
- sommaire panel on background color with a rounded bottom-left corner so it
  stays readable over a banner
- long titles wrap instead of being truncated (portals_v1 regression)
…navigation bar

Keep the header and navigation bar as a single app bar (the navigation bar is
the bar's extension): two stacked app bars overlap during server-side rendering
because Vuetify only computes their stacked top offset after mount. Since a
single bar with scroll-behavior=hide collapses the layout offset
(--v-layout-top) to 0 even though the navigation bar extension stays visible,
derive the navigation bar's bottom offset ourselves to position app-positioned
children (the table of contents drawer/FAB and the navigation drawer) below it.
A single banner landmark is preserved via the tag prop.
Use an explicit v-list-item-title with text-wrap instead of the title prop,
whose default v-list-item-title truncates long titles with an ellipsis.
Give the navigation container min-width:0 so it can shrink below the tabs'
content width. Without it v-tabs stretched the navigation bar and was clipped
instead of overflowing internally, so Vuetify never flagged the slide group as
overflowing and the swap to the drawer never triggered.
@BatLeDev BatLeDev force-pushed the feat-content-page-toc branch 3 times, most recently from 23dbbaa to d431379 Compare July 1, 2026 15:56
Full-bleed banners (banner-fluid) break out to width:100vw, which includes
the vertical scrollbar width and adds a few pixels of horizontal overflow: a
thin dark gutter down the right edge. It only becomes visible once scrolled
past the banner, e.g. when landing on an anchor (#section), which made it look
tied to the URL hash. Clip horizontal overflow at the app root, which keeps
the full-bleed banner intact.
@BatLeDev BatLeDev force-pushed the feat-content-page-toc branch from d431379 to db33358 Compare July 2, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant