Skip to content

Mark the current section in the page menu with CSS scroll-driven animations - #1322

Merged
nojaf merged 6 commits into
fsprojects:mainfrom
nojaf:view-timelines
Sep 13, 2026
Merged

nojaf merged 6 commits into
fsprojects:mainfrom
nojaf:view-timelines

Conversation

@nojaf

@nojaf nojaf commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Current section in the page menu, pure CSS

The "On this page" menu of the default template now marks the section the reader is in: the entry of the last heading scrolled past the top of the content gets a coloured left border.

No scroll listener. It is built on scroll-driven animations:

  • The page content list generator numbers every heading and its menu entry (data-fsdocs-heading) and emits a <style> next to the menu.
  • Each heading gets a named view timeline (view-timeline-name); each menu entry runs two animations, on the timelines of its own heading and of the next one (animation-timeline); timeline-scope on body makes the names visible to the menu, which is a sibling of main.
  • The animations flip two registered custom properties to 1 once a heading crosses the reading line (just below where anchor links scroll a heading to). An entry is current when its own flag is set and the next one is not.
  • The last two entries also watch a scroll timeline on the content scroller, so reaching the bottom of the page counts as reaching the last heading, which may never cross the line.
  • Browsers without scroll-driven animations show the menu as before (@supports).

The colour and the reading line are the --page-menu-active-border-color and --page-menu-reading-line variables.

Hotkeys

A heading focused with j / k takes over from the scroll position and marks its own entry. The hotkeys also scroll that entry into view when the menu overflows, and put the heading's anchor in the URL with replaceState, so the page URL can be copied with the right anchor without a history entry per key press.

fsdocs watch progress bar

The dev server renders a page on its first request, which can take a while for a big script, and the browser keeps showing the old page with no sign that anything is happening. The live reload script now shows an indeterminate bar along the bottom of the window when a link to another page of the site is followed, or when a reload starts after a file change, until the new page paints. Watch mode only.

Also

  • The HTML API docs generator marked the "All Namespaces" menu entry as active on every page when menu templates were used; the flag is now false, as in the Markdown generator, with a test.
  • Cuts 23.0.0-alpha.4.

active on every documentation page whenever custom menu templates were
used, so a page without an entry of its own, like the docs index, showed
it as the current page and the new h hotkey entered the menu there. I
set that flag to false, as the Markdown generator already did, extended
the existing menu template test to assert the active class is empty, and
added a release note.
…ations

The "On this page" menu of the default template now shows which section
the reader is in: the entry of the last heading scrolled past the top of
the content gets a coloured left border. There is no scroll listener; it
is done with named view timelines. The page content list generator
numbers every heading and its menu entry (data-fsdocs-heading) and emits
a <style> next to the menu that gives each heading a view timeline,
binds each entry's animations to the timelines of its own heading and
the next one, and hoists the names to body with timeline-scope so the
menu, a sibling of main, can see them. Two registered properties flip
to 1 when a heading crosses the reading line and an entry is current
when its own flag is set and the next one is not. The last two entries
also watch the content scroller, so reaching the bottom of the page
counts as reaching the last heading, which may never cross the line.
Browsers without scroll-driven animations show the menu as before.

A heading focused with the j / k hotkeys takes over from the scroll
position and marks its own entry. The hotkeys also scroll that entry
into view when the menu overflows and put the heading's anchor in the
URL with replaceState, so the page URL can be copied with the right
anchor without adding a history entry per key press.
…alpha.4

The dev server renders a page on its first request, which can take a
while for a big script, and the browser keeps showing the old page until
the response arrives with nothing to suggest anything is happening. The
live reload script now shows an indeterminate bar along the bottom of
the window when a link to another page of the site is followed, or when
a reload starts after a file change, until the new page paints. It is
hidden again when the navigation is cancelled with Escape or the page
comes back from the back/forward cache. Watch mode only: the script is
not part of a build.

Cut 23.0.0-alpha.4 and add the pull request links to the release notes.
@nojaf nojaf changed the title Use view-timeline-name Mark the current section in the page menu with CSS scroll-driven animations Sep 13, 2026
@nojaf
nojaf marked this pull request as ready for review September 13, 2026 17:11
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
Comment thread src/FSharp.Formatting.Common/PageContentList.fs Fixed
…e edit

With custom menu templates, editing _menu_template.html or
_menu-item_template.html while the watch was running updated the
documentation sections of the menu but not the API Reference section,
which kept the templates as they were at startup until an assembly
changed. The namespace list is rendered with the menu templates, but the
dev server memoised it per page root inside the API state, which is only
rebuilt on assembly or project changes.

The memo now lives in an adaptive value keyed on the API state and the
menu template stamps, so a template edit gives a fresh memo without
rebuilding the API model. Content pages using the namespace list and the
API pages both read from it. A dev server test with a fake API generator
covers the edit and asserts the generator ran once.

Also give the interpolated holes in the page content list generator
their format specifiers, as the analyzers asked.

Fixes fsprojects#1323
The build script is source like anything under src/, and the only F#
here no project compiles, so it was never analyzed. The AnalyzeSolution
target now passes it as a script in the same run as the projects, so one
report covers the repository. The scripts NuGet generates for the
`#r "nuget: ..."` references are excluded, they are not ours.

fsharp-analyzers goes to 0.39.2, G-Research.FSharp.Analyzers to 0.25.0
and Ionide.Analyzers to 0.19.0. The findings that came with that: the
interpolated holes in build.fsx get their format specifiers, and the
Ctrl+C prompt of the tool no longer goes through printfn, which has
nothing to format there.
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.

2 participants