Skip to content

[clang-doc] Add button a toggle for light/dark theme#181587

Draft
evelez7 wants to merge 2 commits intollvm:mainfrom
evelez7:clang-doc-theme-toggle
Draft

[clang-doc] Add button a toggle for light/dark theme#181587
evelez7 wants to merge 2 commits intollvm:mainfrom
evelez7:clang-doc-theme-toggle

Conversation

@evelez7
Copy link
Member

@evelez7 evelez7 commented Feb 16, 2026

Also fixes a typo that caused some overflow issues even when there was no content to cause an overflow.

ALso fixes a typo that caused some overflow issues even when there was
no content to cause an overflow.
@llvmbot
Copy link
Member

llvmbot commented Feb 16, 2026

@llvm/pr-subscribers-clang-tools-extra

Author: Erick Velez (evelez7)

Changes

Also fixes a typo that caused some overflow issues even when there was no content to cause an overflow.


Full diff: https://github.com/llvm/llvm-project/pull/181587.diff

3 Files Affected:

  • (modified) clang-tools-extra/clang-doc/assets/clang-doc-mustache.css (+9-1)
  • (modified) clang-tools-extra/clang-doc/assets/head-template.mustache (+3-2)
  • (modified) clang-tools-extra/clang-doc/assets/navbar-template.mustache (+51)
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index 19fba2f9eae76..69d1dad6d4b67 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -1,7 +1,7 @@
 /* css for clang-doc mustache backend */
 @import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
 
-*,*::before *::after {
+*,*::before, *::after {
     box-sizing:border-box
 }
 * {
@@ -88,6 +88,7 @@ html, body {
     margin: 0;
     padding: 0;
     width: 100%;
+    overflow-x: scroll;
 }
 
 .container {
@@ -179,6 +180,13 @@ header {
     justify-self:center
 }
 
+#theme-toggle {
+    grid-column: 3;
+    justify-self: end;
+    color: var(--text1);
+    border: none;
+}
+
 @media(max-width:768px) {
     .navbar__menu {
         flex-direction:column;
diff --git a/clang-tools-extra/clang-doc/assets/head-template.mustache b/clang-tools-extra/clang-doc/assets/head-template.mustache
index 2ee4823fb77c1..67fcfc565f2da 100644
--- a/clang-tools-extra/clang-doc/assets/head-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/head-template.mustache
@@ -8,8 +8,9 @@
         <script src="{{.}}"></script>
     {{/Scripts}}
     {{! Highlight.js dependency for syntax highlighting }}
-    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" media="(prefers-color-scheme: light)">
-    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css" media="(prefers-color-scheme: dark)">
+    <link id="hljs-light-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" />
+    <link id="hljs-dark-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css" />
+    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0&icon_names=routine" />
     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
 </head>
diff --git a/clang-tools-extra/clang-doc/assets/navbar-template.mustache b/clang-tools-extra/clang-doc/assets/navbar-template.mustache
index 666a4c3e93739..810fccf919942 100644
--- a/clang-tools-extra/clang-doc/assets/navbar-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/navbar-template.mustache
@@ -12,6 +12,7 @@
                 </li>
             </ul>
         </div>
+        <button id="theme-toggle"><span class="material-symbols-rounded">routine</span></button>
     </div>
     {{#HasContexts}}
     <div class="navbar-breadcrumb-container">
@@ -21,3 +22,53 @@
     </div>
     {{/HasContexts}}
 </nav>
+<script>
+    const root = document.documentElement;
+    const toggle = document.getElementById("theme-toggle");
+    const hlLight = document.getElementById("hljs-light-theme");
+    const hlDark = document.getElementById("hljs-dark-theme");
+
+    function changeHighlightJS(theme) {
+      if (theme === "dark") {
+        hlDark.disabled = false;
+        hlLight.disabled = true;
+      } else {
+        hlDark.disabled = true;
+        hlLight.disabled = false;
+      }
+    }
+
+    // Listen to system theme changes.
+    // If the user manually toggles the theme, the theme wont change according
+    // to system changes.
+    const themeQuery = window.matchMedia("(prefers-color-scheme: dark)");
+    themeQuery.addEventListener("change", (event) => {
+      if (savedTheme === null) {
+        return;
+      } else if (event.matches) {
+        changeHighlightJS("dark");
+      } else {
+        changeHighlightJS("light");
+      }
+    });
+
+    toggle.onclick = () => {
+      const currentTheme = root.getAttribute("color-scheme");
+      const next = currentTheme === "dark" ? "light" : "dark";
+      changeHighlightJS(next);
+      root.setAttribute("color-scheme", next);
+      localStorage.setItem("theme", next);
+    };
+
+    document.addEventListener("DOMContentLoaded", () => {
+      const savedTheme = localStorage.getItem("theme");
+      const currentTheme = root.getAttribute("color-scheme");
+      if (savedTheme !== null) {
+        root.setAttribute("color-scheme", savedTheme);
+        changeHighlightJS(savedTheme);
+      } else {
+        root.setAttribute("color-scheme", currentTheme);
+        changeHighlightJS(currentTheme);
+      }
+    });
+</script>

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

🪟 Windows x64 Test Results

  • 3051 tests passed
  • 29 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

Clang Tools

Clang Tools.clang-doc/basic-project.mustache.test
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
rm -rf C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp && mkdir -p C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/build
# executed command: rm -rf 'C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp'
# note: command had no output on stdout or stderr
# executed command: mkdir -p 'C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs' 'C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/build'
# note: command had no output on stdout or stderr
# RUN: at line 2
sed 's|$test_dir|C:/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc|g' C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc/Inputs/basic-project/database_template.json > C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/build/compile_commands.json
# executed command: sed 's|$test_dir|C:/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc|g' 'C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc/Inputs/basic-project/database_template.json'
# note: command had no output on stdout or stderr
# RUN: at line 4
clang-doc --format=html --output=C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs --executor=all-TUs C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/build/compile_commands.json
# executed command: clang-doc --format=html '--output=C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs' --executor=all-TUs 'C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/build/compile_commands.json'
# .---command stdout------------
# | Emiting docs in html format.
# | Mapping decls...
# | Collecting infos...
# | Reducing 5 infos...
# | Generating docs...
# | Generating assets for docs...
# `-----------------------------
# .---command stderr------------
# | [1/3] Processing file C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\Inputs\basic-project\src\Calculator.cpp
# | [2/3] Processing file C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\Inputs\basic-project\src\Circle.cpp
# | [3/3] Processing file C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\Inputs\basic-project\src\Rectangle.cpp
# `-----------------------------
# RUN: at line 5
FileCheck C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\basic-project.mustache.test -input-file=C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html -check-prefix=HTML-SHAPE
# executed command: FileCheck 'C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\basic-project.mustache.test' '-input-file=C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html' -check-prefix=HTML-SHAPE
# .---command stderr------------
# | C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\basic-project.mustache.test:16:13: error: HTML-SHAPE: expected string not found in input
# | HTML-SHAPE: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" media="(prefers-color-scheme: light)">
# |             ^
# | C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html:7:46: note: scanning from here
# |  <script src="../mustache-index.js"></script>
# |                                              ^
# | C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html:8:24: note: possible intended match here
# |  <link id="hljs-light-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" />
# |                        ^
# | 
# | Input file: C:\_work\llvm-project\llvm-project\build\tools\clang\tools\extra\test\clang-doc\Output\basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html
# | Check file: C:\_work\llvm-project\llvm-project\clang-tools-extra\test\clang-doc\basic-project.mustache.test
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             1: <!DOCTYPE html> 
# |             2: <html lang="en-US"> 
# |             3: <head> 
# |             4:  <meta charset="utf-8"/> 
# |             5:  <title>Shape</title> 
# |             6:  <link rel="stylesheet" type="text/css" href="../clang-doc-mustache.css"/> 
# |             7:  <script src="../mustache-index.js"></script> 
# | check:16'0                                                  X error: no match found
# |             8:  <link id="hljs-light-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" /> 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:16'1                            ?                                                                                                                   possible intended match
# |             9:  <link id="hljs-dark-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css" /> 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            10:  <script> 
# | check:16'0     ~~~~~~~~~~
# |            11:  const root = document.documentElement; 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            12:  const toggle = document.getElementById("theme-toggle"); 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            13:  const hlLight = document.getElementById("hljs-light-theme"); 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |             .
# |             .
# |             .
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

🐧 Linux x64 Test Results

  • 3112 tests passed
  • 7 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

Clang Tools

Clang Tools.clang-doc/basic-project.mustache.test
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
rm -rf /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp && mkdir -p /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/build
# executed command: rm -rf /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp
# note: command had no output on stdout or stderr
# executed command: mkdir -p /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/build
# note: command had no output on stdout or stderr
# RUN: at line 2
sed 's|$test_dir|/home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc|g' /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/Inputs/basic-project/database_template.json > /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/build/compile_commands.json
# executed command: sed 's|$test_dir|/home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc|g' /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/Inputs/basic-project/database_template.json
# note: command had no output on stdout or stderr
# RUN: at line 4
clang-doc --format=html --output=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs --executor=all-TUs /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/build/compile_commands.json
# executed command: clang-doc --format=html --output=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs --executor=all-TUs /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/build/compile_commands.json
# .---command stdout------------
# | Emiting docs in html format.
# | Mapping decls...
# | Collecting infos...
# | Reducing 5 infos...
# | Generating docs...
# | Generating assets for docs...
# `-----------------------------
# .---command stderr------------
# | [1/3] Processing file /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Rectangle.cpp
# | [2/3] Processing file /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Circle.cpp
# | [3/3] Processing file /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Calculator.cpp
# `-----------------------------
# RUN: at line 5
FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/basic-project.mustache.test -input-file=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html -check-prefix=HTML-SHAPE
# executed command: FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/basic-project.mustache.test -input-file=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html -check-prefix=HTML-SHAPE
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/basic-project.mustache.test:16:13: error: HTML-SHAPE: expected string not found in input
# | HTML-SHAPE: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" media="(prefers-color-scheme: light)">
# |             ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html:7:46: note: scanning from here
# |  <script src="../mustache-index.js"></script>
# |                                              ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html:8:24: note: possible intended match here
# |  <link id="hljs-light-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" />
# |                        ^
# | 
# | Input file: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/tools/extra/test/clang-doc/Output/basic-project.mustache.test.tmp/docs/html/GlobalNamespace/_ZTV5Shape.html
# | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/clang-tools-extra/test/clang-doc/basic-project.mustache.test
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             1: <!DOCTYPE html> 
# |             2: <html lang="en-US"> 
# |             3: <head> 
# |             4:  <meta charset="utf-8"/> 
# |             5:  <title>Shape</title> 
# |             6:  <link rel="stylesheet" type="text/css" href="../clang-doc-mustache.css"/> 
# |             7:  <script src="../mustache-index.js"></script> 
# | check:16'0                                                  X error: no match found
# |             8:  <link id="hljs-light-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" /> 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:16'1                            ?                                                                                                                   possible intended match
# |             9:  <link id="hljs-dark-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css" /> 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            10:  <script> 
# | check:16'0     ~~~~~~~~~~
# |            11:  const root = document.documentElement; 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            12:  const toggle = document.getElementById("theme-toggle"); 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            13:  const hlLight = document.getElementById("hljs-light-theme"); 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |             .
# |             .
# |             .
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@evelez7 evelez7 marked this pull request as draft February 16, 2026 06:40
@evelez7
Copy link
Member Author

evelez7 commented Feb 16, 2026

This currently works, but I'm trying to prevent a flash of the unstyled content if the user toggles a manual theme change.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants