Skip to content

Display a table of contents in the sidebar of man pages #2036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: gh-pages
Choose a base branch
from

Conversation

jvns
Copy link

@jvns jvns commented Aug 8, 2025

Some of git's man pages are very long and have many sections, and I think it would be easier to navigate them on the web if there were a table of contents. For comparison, man7.org includes a table of contents at the top for its man pages.

This updates script/update-docs.rb to add the data needed to generate the table of contents to the table contents and renders that data in the sidebar.

Here's what the results look right now in context:

image

Some possible issues that come to mind:

  1. It might be confusing to put the table of contents next to the site's overall navigation. It could be better to put the TOC at the top, like man7.org does it.
  2. In the screenshot, you can see that it's hard to read long titles that are split across two lines (like RECOVERING FROM UPSTREAM REBASE)
  3. If putting the table of contents in the sidebar does make sense, then I think it would be nice to set position: sticky so that it stays visible as you scroll the page. I ran into some CSS problems getting that to work but would be happy to figure that out.

Also, I tried regenerating all of the generated HTML pages for all git tags, but ran into some issues so I just generated the v2.50.1 tag for now. Am I meant to regenerate them manually or is it normally handled in CI?

jvns added 2 commits August 8, 2025 16:54
Adds the table of contents to the frontmatter of the generated HTML
pages, then renders it in the Hugo template.
@dscho
Copy link
Member

dscho commented Aug 9, 2025

  1. It might be confusing to put the table of contents next to the site's overall navigation. It could be better to put the TOC at the top, like man7.org does it.

I think that's a very valid point. Personally, I would not have looked for the table of contents of the current manual page in the sidebar. I've tried two things:

Option 1: moving the TOC next to the Versions, Topics and Language menus

image
Diff
diff --git a/assets/sass/reference.scss b/assets/sass/reference.scss
index 4873a402c..14d5bc423 100644
--- a/assets/sass/reference.scss
+++ b/assets/sass/reference.scss
@@ -111,6 +111,19 @@ h3.plumbing {
     &#reference-languages-trigger {
       float: right;
     }
+
+    // Ensure the TOC trigger floats right like topics/languages
+    &#reference-toc-trigger {
+      float: right;
+    }
+  }
+
+  // Place the "last updated" span inline next to the left-floating versions trigger
+  > span.light {
+    display: inline-block;
+    clear: none;
+    margin-left: 6px;
+    vertical-align: middle;
   }
 }
 
@@ -213,6 +226,28 @@ h3.plumbing {
   }
 }
 
+// Position the Table of Contents dropdown under the right-floated TOC trigger
+#toc-dropdown {
+  width: 300px;
+  right: 12px;
+  padding: 12px;
+  font-weight: normal;
+  line-height: 1;
+
+  ul {
+    margin: 0;
+  }
+
+  li {
+    line-height: $base-line-height * 0.8;
+  }
+
+  a {
+    font-weight: normal;
+    padding: 0;
+  }
+}
+
 ol.reference-previous-versions {
   @extend .unstyled !optional;
   margin-top: 3px;
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
index e83e4554b..3bbc0c824 100644
--- a/layouts/_default/baseof.html
+++ b/layouts/_default/baseof.html
@@ -123,6 +123,7 @@
           <div id='reference-version'>
             {{ partial "ref/languages.html" . }}
             {{ partial "ref/topics.html" . }}
+            {{ partial "ref/toc.html" . }}
             {{ partial "ref/versions.html" . }}
           </div>
 
diff --git a/layouts/partials/ref/toc.html b/layouts/partials/ref/toc.html
new file mode 100644
index 000000000..d9d21daf4
--- /dev/null
+++ b/layouts/partials/ref/toc.html
@@ -0,0 +1,13 @@
+{{ $headings := .Params.headings }}
+{{ if $headings }}
+<a class="dropdown-trigger" id="reference-toc-trigger" data-panel-id="toc-dropdown" href="#">Table of Contents Γû╛</a>
+<div class='dropdown-panel right' id='toc-dropdown'>
+  <div>
+    <ul class="toc">
+      {{ range $i, $item := $headings }}
+        <li><a href="#{{ .id }}">{{ .text }}</a></li>
+      {{ end }}
+    </ul>
+  </div>
+</div>
+{{ end }}
diff --git a/layouts/partials/sidebar.html b/layouts/partials/sidebar.html
index 8347b1d2f..69e7c2e72 100644
--- a/layouts/partials/sidebar.html
+++ b/layouts/partials/sidebar.html
@@ -50,19 +50,6 @@
       </li>
     </ul>
 
-    {{ $headings := .Params.headings }}
-    {{ if $headings }}
-    <ul>
-      <li> Table of Contents
-        <ul class="expanded">
-          {{ range $i, $item := $headings }}
-            <li><a href="#{{ .id }}">{{ .text }}</a> </li>
-          {{ end }}
-        </ul>
-      </li>
-    </ul>
-    {{ end }}
-
     {{ if (eq .Params.Sidebar "book") }}
       <hr class="sidebar">
       {{- /* If this page displays a section of the ProGit book, map all the translations thereof */ -}}

Option 2: Inside the same <div> as the manual page, in the top-right

image

This took a bit of finagling until the text flowed around the table of contents.

Diff
diff --git a/assets/sass/reference.scss b/assets/sass/reference.scss
index 4873a402c..383a24dc6 100644
--- a/assets/sass/reference.scss
+++ b/assets/sass/reference.scss
@@ -213,6 +213,68 @@ h3.plumbing {
   }
 }
 
+// New inline TOC box styles
+.toc-box {
+  float: right;
+  width: 200px;
+  margin: 0 0 15px 20px;
+  padding: 10px 12px;
+  font-size: 13px;
+  background-color: var(--callout-color);
+  border: 1px solid var(--base-border-color);
+  @include border-radius(3px);
+
+  .toc-box-header {
+    font-weight: bold;
+    margin-bottom: 8px;
+  }
+
+  .toc-list {
+    margin: 0;
+    padding-left: 18px;
+    list-style: disc;
+  }
+
+  .toc-list li {
+    margin: 2px 0;
+    line-height: $base-line-height * 0.8;
+  }
+
+  .toc-list a {
+    font-weight: normal;
+  }
+}
+
+// Ensure content can flow around the TOC naturally
+#documentation #main {
+  .sectionbody {
+    overflow: visible;
+  }
+
+  .verseblock {
+    overflow: visible;
+
+    .content {
+      overflow: visible;
+
+      pre {
+        overflow: visible;
+        white-space: pre-wrap;
+      }
+    }
+  }
+}
+
+// Stack TOC box full-width on small screens
+@media (max-width: $mobile-m) {
+  .toc-box {
+    float: none;
+    width: 80%;
+    max-width: none;
+    margin-right: 0;
+  }
+}
+
 ol.reference-previous-versions {
   @extend .unstyled !optional;
   margin-top: 3px;
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
index e83e4554b..75fdcc6a4 100644
--- a/layouts/_default/baseof.html
+++ b/layouts/_default/baseof.html
@@ -135,6 +135,7 @@
 	    <p>Please note that this information is only relevant to you if you plan on <a href="{{ relURL "community#contributing" }}">contributing to the Git project itself</a>. It is in no shape or form required reading for regular Git users.</p>
 	    </div>
 	    {{ end }}
+            {{ partial "ref/toc.html" . }}
             {{ .Content }}
             {{ $match := findRESubmatch "(?s)>NAME</h2>.*?<p[^>]*>(git-)?([^ ]+)" .Content 1 }}
             {{ if (eq ($match | len) 1) }}
diff --git a/layouts/partials/ref/toc.html b/layouts/partials/ref/toc.html
new file mode 100644
index 000000000..1bab34434
--- /dev/null
+++ b/layouts/partials/ref/toc.html
@@ -0,0 +1,11 @@
+{{ $headings := .Params.headings }}
+{{ if $headings }}
+<div id='toc-box' class='toc-box'>
+  <div class='toc-box-header'>Table of Contents</div>
+  <ul class="toc-list">
+    {{ range $i, $item := $headings }}
+      <li><a href="#{{ .id }}">{{ .text }}</a></li>
+    {{ end }}
+  </ul>
+</div>
+{{ end }}
diff --git a/layouts/partials/sidebar.html b/layouts/partials/sidebar.html
index 8347b1d2f..69e7c2e72 100644
--- a/layouts/partials/sidebar.html
+++ b/layouts/partials/sidebar.html
@@ -50,19 +50,6 @@
       </li>
     </ul>
 
-    {{ $headings := .Params.headings }}
-    {{ if $headings }}
-    <ul>
-      <li> Table of Contents
-        <ul class="expanded">
-          {{ range $i, $item := $headings }}
-            <li><a href="#{{ .id }}">{{ .text }}</a> </li>
-          {{ end }}
-        </ul>
-      </li>
-    </ul>
-    {{ end }}
-
     {{ if (eq .Params.Sidebar "book") }}
       <hr class="sidebar">
       {{- /* If this page displays a section of the ProGit book, map all the translations thereof */ -}}

@jvns what do you think? I like option 2 best, but maybe you want to go for something even different?

I tried regenerating all of the generated HTML pages for all git tags, but ran into some issues so I just generated the v2.50.1 tag for now. Am I meant to regenerate them manually or is it normally handled in CI?

The idea is to run the update-git-version-and-manual-pages.yml and update-translated-manual-pages.yml workflows manually in your own fork, and checking the "re-building" checkbox:

image

Updated via the `update-translated-manual-pages.yml` GitHub workflow.
@jvns
Copy link
Author

jvns commented Aug 9, 2025

I like the way Option 2 looks, though I haven't figured out how to apply the patch locally so I haven't looked at it on mobile yet.

I did a quick survey of other documentation sites to see how they handle this. Most of them put the table of contents for the current page in the sidebar, as well as having a "fixed sidebar" which is always visible as you scroll through the page. A couple of them (MDN, Kotlin) have a separate right sidebar with the TOC for the current page.

This made me think that the sidebar approach is actually pretty normal, but I think for it to really make sense the sidebar would need to be always visible while scrolling the page. Keeping the sidebar always visible is a pretty big design change to how this site works though, so if you don't think that's realistic Option 2 seems fine.

Here are the sites I looked at in my quick docs design survey:

I'll rerun the workflows in CI.

@dscho
Copy link
Member

dscho commented Aug 9, 2025

@jvns FWIW I ran those workflows in my fork and deployed the site there: https://dscho.github.io/git-scm.com/docs/git-rebase

jvns and others added 2 commits August 9, 2025 15:26
Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.
@jvns
Copy link
Author

jvns commented Aug 9, 2025

Here's what it looks like to have the sidebar always visible (mimicking docs.python.org's approach)

image

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