This is an automated email from the ASF dual-hosted git repository.

lprimak pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shiro-site.git


The following commit(s) were added to refs/heads/main by this push:
     new 410eb2593 Add dark mode support to documentation website (#277)
410eb2593 is described below

commit 410eb2593fb7c68ef2e87fa25167727a877a9fbe
Author: Ganesh Patil <[email protected]>
AuthorDate: Sat Jan 31 22:33:47 2026 +0530

    Add dark mode support to documentation website (#277)
    
    * Add troubleshooting and FAQ documentation page - Closes #267
    
    * Fix: Spell out RIA abbreviation on first use for accessibility
    
    * Add dark mode support to documentation site
    
    * Update src/site/assets/css/base.css
    
    Co-authored-by: Copilot <[email protected]>
    
    * Fix localStorage error handling and add button type attribute
    
    * Fix dark mode contrast for blog/announcements text
    
    * Fix code sample contrast in dark mode
    
    - Add Highlight.js syntax token colors for dark mode
    - Style keywords (purple), strings (green), comments (gray), numbers 
(orange)
    - Add class names (yellow), attributes (blue), tags (cyan) styling
    - Fix Asciidoctor listing blocks for dark mode
    
    Addresses review feedback about code samples having bad contrast.
    
    * Improve dark mode code syntax highlighting with high-contrast Dracula 
theme colors
    
    * made better contrast for notes
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
    Co-authored-by: lprimak <[email protected]>
---
 src/site/assets/css/base.css  | 436 +++++++++++++++++++++++++++++++++++++++++-
 src/site/assets/js/theme.js   |  39 ++++
 src/site/templates/header.ftl |   3 +
 src/site/templates/menu.ftl   |   6 +
 4 files changed, 477 insertions(+), 7 deletions(-)

diff --git a/src/site/assets/css/base.css b/src/site/assets/css/base.css
index 1c93c31fd..8f9a4ffb7 100644
--- a/src/site/assets/css/base.css
+++ b/src/site/assets/css/base.css
@@ -1,10 +1,44 @@
 /* Sticky footer styles
 -------------------------------------------------- */
 
+/* Theme color variables */
+:root {
+  --bg-color: #ffffff;
+  --bg-secondary: #f8f9fa;
+  --text-color: #212529;
+  --text-muted: #6c757d;
+  --link-color: #0d6efd;
+  --link-hover: #0a58ca;
+  --border-color: #dee2e6;
+  --code-bg: #f5f5f5;
+  --code-text: #1f2937;
+  --navbar-bg: #f8f9fa;
+  --footer-bg: #e8e8e8;
+  --card-bg: #f2f2f2;
+}
+
+[data-theme="dark"] {
+  --bg-color: #0f172a;
+  --bg-secondary: #1e293b;
+  --text-color: #e2e8f0;
+  --text-muted: #94a3b8;
+  --link-color: #60a5fa;
+  --link-hover: #93c5fd;
+  --border-color: #334155;
+  --code-bg: #1e293b;
+  --code-text: #e2e8f0;
+  --navbar-bg: #1e293b;
+  --footer-bg: #1e293b;
+  --card-bg: #1e293b;
+}
+
 html,
 body {
   height: 100%;
   /* The html and body elements cannot have any padding or margin. */
+  background-color: var(--bg-color);
+  color: var(--text-color);
+  transition: background-color 0.3s ease, color 0.3s ease;
 }
 
 /* Wrapper for page content to push down footer */
@@ -21,7 +55,8 @@ body {
 
 #custom-footer {
   min-height: 60px;
-  background-color: #e8e8e8;
+  background-color: var(--footer-bg);
+  transition: background-color 0.3s ease;
 }
 
 /* Lastly, apply responsive CSS fixes as necessary */
@@ -38,10 +73,16 @@ body {
 /* -------------------------------------------------- */
 a:link {
   text-decoration: none;
+  color: var(--link-color);
 }
 
 a:visited {
   text-decoration: none;
+  color: var(--link-color);
+}
+
+a:hover {
+  color: var(--link-hover);
 }
 
 /* Custom page CSS
@@ -154,22 +195,22 @@ a:visited {
 /** ======================================================= **/
 
 .authentication {
-  background:url(../images/authentication.png) no-repeat #f2f2f2 right 10px 
top 5px;
+  background:url(../images/authentication.png) no-repeat var(--card-bg) right 
10px top 5px;
 }
 .authorization {
-  background:url(../images/authorization.png) no-repeat #f2f2f2 right 10px top 
5px;
+  background:url(../images/authorization.png) no-repeat var(--card-bg) right 
10px top 5px;
 }
 .cryptography {
-  background:url(../images/crypt.png) no-repeat #f2f2f2 right 10px top 5px;
+  background:url(../images/crypt.png) no-repeat var(--card-bg) right 10px top 
5px;
 }
 .session-management {
-  background:url(../images/session.png) no-repeat #f2f2f2 right 10px top 5px;
+  background:url(../images/session.png) no-repeat var(--card-bg) right 10px 
top 5px;
 }
 .web-integration {
-  background:url(../images/web-integration.png) no-repeat #f2f2f2 right 10px 
top 5px;
+  background:url(../images/web-integration.png) no-repeat var(--card-bg) right 
10px top 5px;
 }
 .integrations {
-  background:url(../images/integration.png) no-repeat #f2f2f2 right 10px top 
5px;
+  background:url(../images/integration.png) no-repeat var(--card-bg) right 
10px top 5px;
 }
 
 h2.panel-title {
@@ -213,3 +254,384 @@ div.related-content {
 .related-content .read-more {
   font-size: 11px;
 }
+
+/* Dark mode overrides for Bootstrap components */
+[data-theme="dark"] .navbar-light {
+  background-color: var(--navbar-bg) !important;
+}
+
+[data-theme="dark"] .navbar-light .navbar-nav .nav-link {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .navbar-light .navbar-nav .nav-link:hover {
+  color: var(--link-color);
+}
+
+[data-theme="dark"] .dropdown-menu {
+  background-color: var(--bg-secondary);
+  border-color: var(--border-color);
+}
+
+[data-theme="dark"] .dropdown-item {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .dropdown-item:hover {
+  background-color: var(--border-color);
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .dropdown-divider {
+  border-color: var(--border-color);
+}
+
+[data-theme="dark"] .bg-light {
+  background-color: var(--navbar-bg) !important;
+}
+
+[data-theme="dark"] .border-top {
+  border-color: var(--border-color) !important;
+}
+
+[data-theme="dark"] .text-muted {
+  color: var(--text-muted) !important;
+}
+
+[data-theme="dark"] .shadow-sm {
+  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.4) !important;
+}
+
+/* Code blocks dark mode */
+[data-theme="dark"] pre,
+[data-theme="dark"] code {
+  background-color: var(--code-bg);
+  color: var(--code-text);
+}
+
+[data-theme="dark"] .hljs {
+  background: var(--code-bg);
+  color: var(--code-text);
+}
+
+/* Highlight.js syntax token colors for dark mode - High contrast */
+[data-theme="dark"] .hljs-keyword,
+[data-theme="dark"] .hljs-selector-tag,
+[data-theme="dark"] .hljs-section {
+  color: #ff79c6;
+  font-weight: 500;
+}
+
+[data-theme="dark"] .hljs-string,
+[data-theme="dark"] .hljs-symbol,
+[data-theme="dark"] .hljs-bullet,
+[data-theme="dark"] .hljs-addition {
+  color: #50fa7b;
+}
+
+[data-theme="dark"] .hljs-title,
+[data-theme="dark"] .hljs-name {
+  color: #8be9fd;
+  font-weight: 500;
+}
+
+[data-theme="dark"] .hljs-type,
+[data-theme="dark"] .hljs-attribute,
+[data-theme="dark"] .hljs-variable,
+[data-theme="dark"] .hljs-template-tag,
+[data-theme="dark"] .hljs-template-variable {
+  color: #ffb86c;
+}
+
+[data-theme="dark"] .hljs-comment,
+[data-theme="dark"] .hljs-quote,
+[data-theme="dark"] .hljs-deletion,
+[data-theme="dark"] .hljs-meta {
+  color: #6272a4;
+  font-style: italic;
+}
+
+[data-theme="dark"] .hljs-number,
+[data-theme="dark"] .hljs-regexp,
+[data-theme="dark"] .hljs-literal,
+[data-theme="dark"] .hljs-link {
+  color: #bd93f9;
+}
+
+[data-theme="dark"] .hljs-class .hljs-title,
+[data-theme="dark"] .hljs-title.class_,
+[data-theme="dark"] .hljs-built_in {
+  color: #f1fa8c;
+}
+
+[data-theme="dark"] .hljs-attr {
+  color: #50fa7b;
+}
+
+[data-theme="dark"] .hljs-selector-class,
+[data-theme="dark"] .hljs-selector-attr,
+[data-theme="dark"] .hljs-selector-pseudo {
+  color: #8be9fd;
+}
+
+[data-theme="dark"] .hljs-tag {
+  color: #ff79c6;
+}
+
+[data-theme="dark"] .hljs-doctag,
+[data-theme="dark"] .hljs-strong {
+  font-weight: bold;
+}
+
+[data-theme="dark"] .hljs-emphasis {
+  font-style: italic;
+}
+
+/* XML/HTML specific - brighter tag colors */
+[data-theme="dark"] .language-xml .hljs-tag,
+[data-theme="dark"] .language-html .hljs-tag {
+  color: #ff79c6;
+}
+
+[data-theme="dark"] .language-xml .hljs-name,
+[data-theme="dark"] .language-html .hljs-name {
+  color: #8be9fd;
+}
+
+[data-theme="dark"] .language-xml .hljs-attr,
+[data-theme="dark"] .language-html .hljs-attr {
+  color: #50fa7b;
+}
+
+[data-theme="dark"] .language-xml .hljs-string,
+[data-theme="dark"] .language-html .hljs-string {
+  color: #f1fa8c;
+}
+
+/* Asciidoctor listing block code in dark mode */
+[data-theme="dark"] .listingblock pre.highlight code,
+[data-theme="dark"] .listingblock pre.highlight,
+[data-theme="dark"] .literalblock pre,
+[data-theme="dark"] pre.content {
+  background-color: var(--code-bg);
+  color: var(--code-text);
+}
+
+/* Theme toggle button */
+#theme-toggle {
+  background: none;
+  border: 1px solid var(--border-color);
+  border-radius: 4px;
+  padding: 4px 8px;
+  cursor: pointer;
+  font-size: 16px;
+  margin-left: 10px;
+  transition: background-color 0.3s ease;
+}
+
+#theme-toggle:focus,
+#theme-toggle:focus-visible {
+  outline: 2px solid var(--link-color);
+  outline-offset: 2px;
+  background-color: var(--bg-secondary);
+}
+#theme-toggle:hover {
+  background-color: var(--bg-secondary);
+}
+
+[data-theme="dark"] #theme-toggle {
+  border-color: var(--border-color);
+}
+
+/* Asciidoctor dark mode overrides */
+[data-theme="dark"] .subheader,
+[data-theme="dark"] #content #toctitle,
+[data-theme="dark"] .admonitionblock td.content > .title,
+[data-theme="dark"] .exampleblock > .title,
+[data-theme="dark"] .imageblock > .title,
+[data-theme="dark"] .listingblock > .title,
+[data-theme="dark"] .literalblock > .title,
+[data-theme="dark"] .paragraph > .title,
+[data-theme="dark"] .tableblock > .title,
+[data-theme="dark"] .dlist > .title,
+[data-theme="dark"] .olist > .title,
+[data-theme="dark"] .ulist > .title {
+  color: #f59e0b;
+}
+
+[data-theme="dark"] table {
+  background: var(--bg-secondary);
+  border-color: var(--border-color);
+}
+
+[data-theme="dark"] table thead tr th,
+[data-theme="dark"] table thead tr td,
+[data-theme="dark"] table tr th,
+[data-theme="dark"] table tr td {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] table tr.even,
+[data-theme="dark"] table tr.alt,
+[data-theme="dark"] table tr:nth-of-type(even) {
+  background: var(--bg-color);
+}
+
+[data-theme="dark"] *:not(pre) > code {
+  background-color: var(--code-bg);
+  border-color: var(--border-color);
+  color: var(--code-text);
+}
+
+[data-theme="dark"] blockquote {
+  border-left-color: var(--border-color);
+}
+
+[data-theme="dark"] blockquote,
+[data-theme="dark"] blockquote p,
+[data-theme="dark"] blockquote cite {
+  color: var(--text-muted);
+}
+
+[data-theme="dark"] abbr,
+[data-theme="dark"] acronym {
+  color: var(--text-color);
+  border-bottom-color: var(--border-color);
+}
+
+[data-theme="dark"] kbd:not(.keyseq) {
+  color: var(--text-color);
+  background-color: var(--bg-secondary);
+  border-color: var(--border-color);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), 0 0 0 2px var(--bg-color) 
inset;
+}
+
+[data-theme="dark"] .admonitionblock > table {
+  background: var(--bg-secondary);
+}
+
+[data-theme="dark"] .admonitionblock > table td.content {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .admonitionblock td.icon .icon-note:before {
+  color: var(--link-color);
+}
+
+[data-theme="dark"] .listingblock pre {
+  background: var(--code-bg);
+}
+
+/* Blog/News/Announcements dark mode fixes */
+[data-theme="dark"] .card {
+  background-color: var(--bg-secondary);
+  border-color: var(--border-color);
+}
+
+[data-theme="dark"] .card-body {
+  background-color: var(--bg-secondary);
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .card-body p,
+[data-theme="dark"] .card-body small,
+[data-theme="dark"] .card-body h4,
+[data-theme="dark"] .card-body a {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .card-body a:hover {
+  color: var(--link-hover);
+}
+
+[data-theme="dark"] .card-body small,
+[data-theme="dark"] small {
+  color: var(--text-muted);
+}
+
+[data-theme="dark"] .card-header {
+  border-bottom-color: var(--border-color);
+}
+
+[data-theme="dark"] .news-title,
+[data-theme="dark"] .popular-guides {
+  color: var(--link-color);
+}
+
+[data-theme="dark"] .news-title:hover,
+[data-theme="dark"] .popular-guides:hover {
+  color: var(--link-hover);
+}
+
+/* Page header and content */
+[data-theme="dark"] .page-header,
+[data-theme="dark"] .page-header h1 {
+  color: var(--text-color);
+}
+
+/* Blog post text styling */
+[data-theme="dark"] em,
+[data-theme="dark"] time {
+  color: var(--text-muted);
+}
+
+[data-theme="dark"] p {
+  color: var(--text-color);
+}
+
+/* Pagination dark mode */
+[data-theme="dark"] .pagination .page-link {
+  background-color: var(--bg-secondary);
+  border-color: var(--border-color);
+  color: var(--link-color);
+}
+
+[data-theme="dark"] .pagination .page-item.disabled .page-link {
+  background-color: var(--bg-color);
+  color: var(--text-muted);
+}
+
+[data-theme="dark"] .pagination .page-link:hover {
+  background-color: var(--border-color);
+  color: var(--link-hover);
+}
+
+/* Panel headings */
+[data-theme="dark"] .panel-heading {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] .panel-title {
+  color: var(--text-color);
+}
+
+/* Hero section / intro box */
+[data-theme="dark"] .rounded-3.bg-light {
+  background-color: var(--bg-secondary) !important;
+  color: var(--text-color);
+}
+
+/* Archive page */
+[data-theme="dark"] h4 {
+  color: var(--text-color);
+}
+
+[data-theme="dark"] ul li {
+  color: var(--text-color);
+}
+
+/* Download button - ensure visibility */
+[data-theme="dark"] .btn-success {
+  color: #ffffff;
+}
+
+/* Border styling for cards */
+[data-theme="dark"] .border-primary {
+  border-color: var(--link-color) !important;
+}
+
+/* Horizontal rule */
+[data-theme="dark"] hr {
+  border-color: var(--border-color);
+  opacity: 0.5;
+}
diff --git a/src/site/assets/js/theme.js b/src/site/assets/js/theme.js
new file mode 100644
index 000000000..bc8735aee
--- /dev/null
+++ b/src/site/assets/js/theme.js
@@ -0,0 +1,39 @@
+(function () {
+  var storedTheme;
+  try {
+    storedTheme = localStorage.getItem("theme");
+  } catch (e) {
+    storedTheme = null;
+  }
+
+  var theme = storedTheme || "light";
+  document.documentElement.setAttribute("data-theme", theme);
+
+  function updateIcon(currentTheme) {
+    var icon = document.getElementById("theme-icon");
+    if (icon) {
+      icon.textContent = currentTheme === "dark" ? "☀️" : "🌙";
+    }
+  }
+
+  document.addEventListener("DOMContentLoaded", function () {
+    var toggle = document.getElementById("theme-toggle");
+    var currentTheme = document.documentElement.getAttribute("data-theme");
+
+    updateIcon(currentTheme);
+
+    if (toggle) {
+      toggle.addEventListener("click", function () {
+        var current = document.documentElement.getAttribute("data-theme");
+        var next = current === "dark" ? "light" : "dark";
+        document.documentElement.setAttribute("data-theme", next);
+        try {
+          localStorage.setItem("theme", next);
+        } catch (e) {
+          // localStorage not available
+        }
+        updateIcon(next);
+      });
+    }
+  });
+})();
diff --git a/src/site/templates/header.ftl b/src/site/templates/header.ftl
index 7e78715ef..079bed060 100644
--- a/src/site/templates/header.ftl
+++ b/src/site/templates/header.ftl
@@ -138,6 +138,9 @@
     <link href="<#if 
(content.rootpath)??>${content.rootpath}<#else></#if>highlight.js-11.11.1/styles/default.min.css"
 rel="stylesheet">
     <link href="<#if 
(content.rootpath)??>${content.rootpath}<#else></#if>css/gh-pages/gh-fork-ribbon.css"
 rel="stylesheet"/>
 
+    <!-- Theme toggle script (loaded early to prevent flash) -->
+    <script src="<#if 
(content.rootpath)??>${content.rootpath}<#else></#if>js/theme.js"></script>
+
     <!-- Fav and touch icons -->
     <!--<link rel="apple-touch-icon-precomposed" sizes="144x144" 
href="../assets/ico/apple-touch-icon-144-precomposed.png">
     <link rel="apple-touch-icon-precomposed" sizes="114x114" 
href="../assets/ico/apple-touch-icon-114-precomposed.png">
diff --git a/src/site/templates/menu.ftl b/src/site/templates/menu.ftl
index 345194907..cf36e81cc 100644
--- a/src/site/templates/menu.ftl
+++ b/src/site/templates/menu.ftl
@@ -90,6 +90,12 @@
                 <li><a class="dropdown-item" 
href="https://www.apache.org/security/";>Security</a></li>
               </ul>
             </li>
+            <!-- Theme toggle -->
+            <li class="nav-item d-flex align-items-center">
+              <button id="theme-toggle" type="button" aria-label="Toggle dark 
mode" title="Toggle dark mode">
+                <span id="theme-icon">🌙</span>
+              </button>
+            </li>
           </ul>
           <#--
           <form class="d-flex">

Reply via email to