Skip to content
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
43b9865
Add top_menu feature for multi-section documentation
lmiq Jan 27, 2026
723cb0d
redirect to index.md directly
lmiq Jan 27, 2026
a4cf3ef
Update CHANGELOG with top_menu feature
lmiq Jan 27, 2026
562ce82
Fix: use local pretty_url and get_url functions for redirect generation
lmiq Jan 27, 2026
b7c8f4a
run runic formatter
lmiq Jan 27, 2026
ba1a6c2
format test code
lmiq Jan 27, 2026
7d5a009
Add comprehensive tests for html-topmenu build output
lmiq Jan 27, 2026
31eb82c
Fix top_menu test for Windows path separators
lmiq Jan 27, 2026
dd339c7
Merge branch 'master' into top_menu
lmiq Jan 27, 2026
c9d1315
add more tests for edge cases
lmiq Jan 28, 2026
5c1d667
format code with runic
lmiq Jan 28, 2026
c4328ca
fix tests
lmiq Jan 28, 2026
746cc88
fix tests to work in locally
lmiq Jan 28, 2026
ce5f5e0
runic formatting
lmiq Jan 28, 2026
65d27c9
adjust test that throws error
lmiq Jan 28, 2026
72d1a54
fix test for Julia 1.6
lmiq Jan 28, 2026
60da4b8
Merge branch 'master' into top_menu
lmiq Jan 29, 2026
2054106
Merge branch 'master' into top_menu
fingolfin Feb 15, 2026
fd1da01
add dropdown menu functionality to the top menu
lmiq Feb 19, 2026
bd5f539
Merge branch 'top_menu' of https://github.com/lmiq/Documenter.jl into…
lmiq Feb 19, 2026
192e55e
fix_drop_down branch
lmiq Feb 26, 2026
6a6f387
fix dropdowns in mobile mode
lmiq Feb 26, 2026
24e165a
Merge branch 'JuliaDocs:master' into top_menu
lmiq Feb 26, 2026
a6f5dc4
support single-item top_menu entries
lmiq Mar 4, 2026
500f421
wrap top menu entries on tight width pages
lmiq Mar 4, 2026
9b2fe20
format code
lmiq Mar 4, 2026
2dbbb04
fix CSS themes
lmiq Mar 4, 2026
7c19736
Merge branch 'master' into top_menu
lmiq Mar 4, 2026
a1b5d8b
use top_menu option in makedocs, and pages to build the top menu
lmiq Mar 9, 2026
783ca4f
Replace the use of Base.structdiff with a manual filtering of keyword…
lmiq Mar 9, 2026
e02dbaa
Merge branch 'master' into top_menu_from_pages
lmiq Mar 9, 2026
5d64676
fix tests for new API
lmiq Mar 9, 2026
d7f1d50
Merge branch 'top_menu_from_pages' of https://github.com/lmiq/Documen…
lmiq Mar 9, 2026
7241c2c
fix formatting
lmiq Mar 9, 2026
6f89f36
fix hiding of content anchor by top menu
lmiq Mar 23, 2026
599d506
sets scroll-padding-top on <html> (the scroll container) to exactly t…
lmiq Mar 23, 2026
c6285ea
fix padding of section header when link is accessed from a direct URL
lmiq Mar 25, 2026
e9d7807
Merge remote-tracking branch 'origin/master' into top_menu_from_pages
mortenpi Apr 17, 2026
c74aac8
regenerate themes
mortenpi Apr 17, 2026
1f89be0
1. Moving top_menu to Documenter.HTML
lmiq Apr 17, 2026
02f21bd
find first index.md to set landing page
lmiq Apr 17, 2026
86c3e39
redirect to first index.md if there is no root index.md file
lmiq Apr 17, 2026
a9a9aca
the Node(text::AbstractString) inner constructor was only initializin…
lmiq Apr 17, 2026
c826e94
when get_section_navtree finds the current page in a section whose na…
lmiq Apr 17, 2026
e51ac6d
Now for a leaf top-level page like "Home" => "index.md", the section'…
lmiq Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added `top_menu` keyword argument to `makedocs` for multi-section documentation with a top navigation bar. This enables upper-level organization above the sidebar, with each section maintaining its own sidebar navigation tree. ([#2866])
* The version selector now also preserves the anchor (hash) when switching between documentation versions. Additionally, the outdated/dev version warning banner now also tries to keep you on the same page (and position) when linking to the latest stable release. ([#2880])
* Added `Remotes.Forgejo` for specifying a `Remote` hosted on a Forgejo instance (such as codeberg.org). ([#2857])
* Doctests now default to the `parser_for_module` of the module that the docstring appears in, allowing modules that set their syntax version via `Base.Experimental.@set_syntax_version` to have their doctests parsed with the correct syntax automatically. Also added support for `DocTestSyntax` metadata and per-block `syntax=` attributes to explicitly specify a syntax version. ([#2874])
Expand Down
30 changes: 28 additions & 2 deletions assets/html/js/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// Manages the showing and hiding of the sidebar.
$(document).ready(function () {
var sidebar = $("#documenter > .docs-sidebar");
var sidebar = $("#documenter .docs-sidebar");
var sidebar_button = $("#documenter-sidebar-button");
sidebar_button.click(function (ev) {
ev.preventDefault();
Expand All @@ -13,7 +13,7 @@ $(document).ready(function () {
$("#documenter .docs-menu a.is-active").focus();
}
});
$("#documenter > .docs-main").bind("click", function (ev) {
$("#documenter .docs-main").bind("click", function (ev) {
if ($(ev.target).is(sidebar_button)) {
return;
}
Expand Down Expand Up @@ -42,6 +42,32 @@ $(document).ready(function () {
$(window).on("orientationchange", resize);
});

// Dynamically update --topmenu-height so that the sidebar, content wrapper, and
// sticky navbar all stay correctly positioned when top menu items wrap to a new line.
$(document).ready(function () {
var topMenu = $("#documenter .docs-top-menu");
if (topMenu.length === 0) return;
var documenter = document.getElementById("documenter");
function updateTopMenuHeight() {
var height = topMenu[0].offsetHeight + "px";
documenter.style.setProperty("--topmenu-height", height);
// Offset anchor-scroll targets so the fixed top menu doesn't cover them
document.documentElement.style.scrollPaddingTop = height;
}
updateTopMenuHeight();
$(window).resize(updateTopMenuHeight);
$(window).on("orientationchange", updateTopMenuHeight);
// Re-scroll to the hash anchor now that scroll-padding-top is set.
// The browser may have already scrolled to the anchor before JS ran,
// causing the header to be hidden under the fixed top menu.
if (location.hash) {
var target = document.getElementById(
decodeURIComponent(location.hash.substring(1))
);
if (target) target.scrollIntoView();
}
});

// Scroll the navigation bar to the currently selected menu item
$(document).ready(function () {
var sidebar = $("#documenter .docs-menu").get(0);
Expand Down
1 change: 1 addition & 0 deletions assets/html/scss/documenter/layout/_all.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import "main";
@import "sidebar";
@import "search";
@import "topmenu";
214 changes: 214 additions & 0 deletions assets/html/scss/documenter/layout/_topmenu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Top Menu Navigation Bar
// This provides the top-level section navigation when top_menu is configured
// Uses the same color scheme as the sidebar for consistency across themes

// Variables for top menu - derived from sidebar variables for theme consistency
$documenter-topmenu-height: 2.75rem !default;
$documenter-topmenu-background: $documenter-sidebar-background !default;
$documenter-topmenu-color: $documenter-sidebar-color !default;
$documenter-topmenu-hover-background: $documenter-sidebar-menu-hover-background !default;
$documenter-topmenu-hover-color: $documenter-sidebar-menu-hover-color !default;
$documenter-topmenu-active-background: $documenter-sidebar-menu-active-background !default;
$documenter-topmenu-active-color: $documenter-sidebar-menu-active-color !default;

// Top menu navigation bar
.docs-top-menu {
background-color: $documenter-topmenu-background;
min-height: $documenter-topmenu-height; // Allow growing when items wrap
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100; // Above sidebar and main content
display: flex;
align-items: flex-start;
border-bottom: 1px solid $border;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);

.container {
width: 100%;
max-width: none;
padding: 0;
margin: 0;
}

.docs-top-menu-list {
display: flex;
flex-direction: row;
flex-wrap: wrap; // Allow items to wrap to a new line when space is tight
list-style: none;
margin: 0;
padding: 0;
gap: 0;
// Keep overflow visible so absolutely-positioned dropdown menus are not clipped.
overflow: visible;
height: auto;

> li {
flex-shrink: 0;
display: flex;
align-items: stretch;

&.is-active {
.docs-top-menu-link {
background-color: $documenter-topmenu-active-background;
color: $documenter-topmenu-active-color;
font-weight: 700;
border-bottom: 3px solid $primary;
}
}
}
}

.docs-top-menu-link {
display: flex;
align-items: center;
justify-content: center;
padding: 0 1.5rem;
color: $documenter-topmenu-color;
text-decoration: none;
white-space: nowrap;
font-size: 0.95rem;
font-weight: 500;
letter-spacing: 0.02em;
height: $documenter-topmenu-height;
transition: background-color 0.15s ease, color 0.15s ease;
border-bottom: 3px solid transparent;

&:hover {
background-color: $documenter-topmenu-hover-background;
color: $documenter-topmenu-hover-color;
text-decoration: none;
}

.docs-top-dropdown-caret {
margin-left: 0.35em;
font-size: 0.75em;
opacity: 0.6;
line-height: 1;
}
}

// Dropdown menu for each top-menu item
.docs-top-dropdown {
position: relative;

.docs-top-dropdown-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
min-width: 14rem;
background-color: $documenter-topmenu-background;
border: 1px solid $border;
border-radius: 0 0 4px 4px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
z-index: 200;
padding: 0.3rem 0;
list-style: none;
margin: 0;

> li > .docs-top-dropdown-item {
display: block;
padding: 0.45rem 1rem;
color: $documenter-topmenu-color;
text-decoration: none;
font-size: 0.875rem;
white-space: nowrap;
transition: background-color 0.1s ease, color 0.1s ease;

&:hover {
background-color: $documenter-topmenu-hover-background;
color: $documenter-topmenu-hover-color;
text-decoration: none;
}
}
}

// Show dropdown on hover (desktop) and when focus is inside (keyboard navigation)
&:hover > .docs-top-dropdown-menu,
&:focus-within > .docs-top-dropdown-menu {
display: block;
}
}
}

// Adjustments when top menu is present
#documenter.has-top-menu {
// Content wrapper to hold sidebar and main content
.docs-content-wrapper {
display: flex;
padding-top: var(--topmenu-height, #{$documenter-topmenu-height});
min-height: 100vh;
}

// Adjust sidebar position
.docs-sidebar {
top: var(--topmenu-height, #{$documenter-topmenu-height});
height: calc(100vh - var(--topmenu-height, #{$documenter-topmenu-height}));

@include desktop {
top: var(--topmenu-height, #{$documenter-topmenu-height});
}
}

// Adjust main content area
.docs-main {
flex: 1;
min-width: 0;

@include desktop {
// Adjust for sidebar width when visible
margin-left: $documenter-sidebar-width + $documenter-sidebar-main-gap;
}
}
}

// Mobile adjustments
@include touch {
.docs-top-menu {
min-height: 2.5rem; // Slightly smaller on mobile; auto-grows when items wrap

.docs-top-menu-list {
// Allow wrapping on narrow screens instead of hiding items off-screen
flex-wrap: wrap;
justify-content: flex-start;
padding: 0 0.5rem;
}

.docs-top-menu-link {
padding: 0 1rem;
font-size: 0.875rem;
height: 2.5rem;
}

// Hide dropdowns on touch devices — they don't work with hover-only interaction
.docs-top-dropdown-menu {
display: none !important;
}
.docs-top-dropdown-caret {
display: none;
}
}

#documenter.has-top-menu {
.docs-content-wrapper {
padding-top: var(--topmenu-height, 2.5rem);
}

.docs-sidebar {
// On mobile, sidebar is overlay, needs to account for top menu
top: var(--topmenu-height, 2.5rem);
height: calc(100vh - var(--topmenu-height, 2.5rem));
}

.docs-main {
margin-left: 0;

> header.docs-navbar {
// Sticky navbar on mobile needs to account for top menu
top: var(--topmenu-height, 2.5rem);
}
}
}
}
2 changes: 1 addition & 1 deletion assets/html/themes/catppuccin-frappe.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/html/themes/catppuccin-latte.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/html/themes/catppuccin-macchiato.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/html/themes/catppuccin-mocha.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/html/themes/documenter-dark.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/html/themes/documenter-light.css

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
Changelog = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8"
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"

[sources]
Documenter = { path = ".." }
Documenter = {path = ".."}
59 changes: 59 additions & 0 deletions docs/src/man/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,65 @@ Using the `pages` argument you can organize your pages into subsections and hide
from the sidebar with the help of the [`hide`](@ref) functions.


## Top Menu for Multi-Section Documentation

For larger documentation projects, you can create a top-level navigation bar using the
`top_menu` boolean argument to [`makedocs`](@ref). This allows you to organize your documentation
into multiple distinct sections, each with its own sidebar navigation.

```julia
makedocs(
...,
top_menu = true,
pages = [
"Getting Started" => [
"Home" => "index.md",
"Installation" => "getting-started/install.md",
"Quick Start" => "getting-started/quickstart.md",
],
"User Guide" => [
"Overview" => "guide/index.md",
"Basic Usage" => "guide/basics.md",
"Advanced Topics" => [
"guide/advanced.md",
"guide/tips.md",
],
],
"API Reference" => [
"Public API" => "api/public.md",
"Internals" => "api/internals.md",
],
],
)
```

When `top_menu = true`, the first layer of the `pages` argument is used as the top menu
(each entry becomes a section). The `pages` section is still interpreted for sidebar
navigation within each section.

- A horizontal navigation bar appears at the top of the page with the section titles
- Each section has its own sidebar navigation showing only the pages in that section
- Clicking a section title navigates to the first page of that section
- The sidebar's previous/next navigation stays within each section

Each entry in the first layer of `pages` must be a `"Section Title" => pages_array` pair,
where `pages_array` follows the same format as the `pages` argument (supporting nested
subsections, page titles, etc.).

!!! note "Landing page"
The section containing `index.md` will be displayed first when the documentation is
opened, since `index.md` becomes the landing page. Make sure to place your main entry
point in the appropriate section.

!!! warning "Unique pages across sections"
Each page should appear in only one section. Having the same page in multiple sections
will cause navigation issues, as a single page can only belong to one section's
navigation tree.

If `top_menu = false` (the default), Documenter uses the standard single-sidebar
behavior controlled by the `pages` argument.


## Adding a logo or icon

You can easily add a logo or icon to your documentation which
Expand Down
Loading