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

kaxilnaik pushed a commit to branch staging
in repository https://gitbox.apache.org/repos/asf/airflow-site.git

commit 185c6799d7ec78715d25d413c0df1a1ad92ed5d0
Author: Kaxil Naik <[email protected]>
AuthorDate: Fri Nov 7 01:40:07 2025 +0000

    Add dark mode support to Airflow documentation site
    
    Upgrade Docsy theme from v0.2.0 to v0.12.0 and implement Bootstrap 5 dark 
mode.
    
    Core Changes:
    - Enable showLightDarkModeMenu in config.toml
    - Add theme toggle component to navbar (light/dark/auto modes)
    - Store user preference in localStorage
    - Respect prefers-color-scheme media query
    
    Styling Implementation:
    - Use Bootstrap 5 CSS variables (--bs-body-color, --bs-secondary-bg, etc.) 
for all UI elements
    - Style navbar, header, sidebar, tables, admonitions, and forms for dark 
mode
    - Add Pygments-generated GitHub Dark theme for code syntax highlighting in 
Sphinx docs
    - Adapt confetti animation to dynamically set background based on theme
    - Ensure proper semantic colors for all admonition types (note, warning, 
error, hint, etc.)
    
    Technical Details:
    - Added $td-sidebar-tree-root-color variable (required by Docsy v0.2.0+)
    - Added $font-awesome-font-name variable (required by Docsy v0.6.0+)
    - Added .-bg-orange utility class (required by Docsy v0.6.0+)
    - Created pygments/_dark.scss (generated via: pygmentize -S github-dark)
    - Use hooks/body-end.html for webpack scripts (proper Docsy pattern)
    - Add dark mode support to Sphinx theme (header.html, layout.html)
    - Update .gitignore to exclude site/public/ directory
    
    Fixed Issues:
    - Confetti animation now visible with transparent header background
    - Sidebar links readable in dark mode (increased contrast)
    - All admonition types (including seealso) properly colored
    - Logo text white in dark mode
    - Navbar and header backgrounds adapt to theme
    
    Architecture:
    - Removed baseof.html override, use Docsy hooks instead
    - Follow Docsy best practices for customization
    - Use CSS variables for maintainability
    
    Testing:
    - Verified dark mode toggle works on all pages
    - Confirmed confetti animation adapts to theme changes
    - Tested all admonition types in light and dark modes
    - Verified code syntax highlighting in Sphinx documentation
    
    Fix CSS lint errors and add dark mode support for blog page
    
    - Replace hardcoded colors in blog page with Bootstrap CSS variables
    - Fix CSS specificity linting errors in rating component
    - Add stylelint disable for dark mode block where linter doesn't understand 
theme context
---
 landing-pages/.gitignore                           |   1 +
 landing-pages/site/assets/scss/_blog-page.scss     |  33 ++
 landing-pages/site/assets/scss/_colors.scss        |   4 +-
 landing-pages/site/assets/scss/_header.scss        |  21 +
 landing-pages/site/assets/scss/_navbar.scss        |  67 ++++
 landing-pages/site/assets/scss/_rating.scss        |  28 ++
 landing-pages/site/assets/scss/_roadmap.scss       |   5 +
 landing-pages/site/assets/scss/_rst-content.scss   | 427 ++++++++++++++++-----
 landing-pages/site/assets/scss/_variables.scss     |   4 +-
 landing-pages/site/assets/scss/main-custom.scss    |   2 +-
 landing-pages/site/assets/scss/pygments/_dark.scss | 121 ++++++
 landing-pages/site/layouts/_default/baseof.html    |   4 +-
 .../layouts/{partials => _partials}/navbar.html    |  10 +
 .../baseof.html => partials/hooks/body-end.html}   |  30 +-
 landing-pages/site/layouts/partials/navbar.html    |  10 +
 .../site/layouts/partials/scripts.html.backup      |  14 -
 landing-pages/src/js/headerAnimation.js            |  10 +-
 .../sphinx_airflow_theme/__init__.py               |   2 +-
 .../sphinx_airflow_theme/globaltoc.html            |  20 +
 .../sphinx_airflow_theme/header.html               |  34 ++
 .../sphinx_airflow_theme/layout.html               | 103 ++++-
 21 files changed, 805 insertions(+), 145 deletions(-)

diff --git a/landing-pages/.gitignore b/landing-pages/.gitignore
index 4aa6c9e0db..7b46e9dd7f 100644
--- a/landing-pages/.gitignore
+++ b/landing-pages/.gitignore
@@ -3,3 +3,4 @@ dist/
 site/data/webpack.json
 resources/
 site/static/_gen/
+site/public/
diff --git a/landing-pages/site/assets/scss/_blog-page.scss 
b/landing-pages/site/assets/scss/_blog-page.scss
index baf27066e8..53296f0e27 100644
--- a/landing-pages/site/assets/scss/_blog-page.scss
+++ b/landing-pages/site/assets/scss/_blog-page.scss
@@ -163,3 +163,36 @@
     justify-content: center
   }
 }
+
+// Dark mode support using Bootstrap CSS variables
+[data-bs-theme="dark"] {
+  .blogpost-content {
+    &__metadata {
+      &--title {
+        color: var(--bs-emphasis-color);
+      }
+
+      &--description {
+        color: var(--bs-body-color);
+      }
+
+      &--date {
+        color: var(--bs-secondary-color);
+      }
+    }
+  }
+
+  .tag {
+    color: var(--bs-link-color);
+    background-color: var(--bs-link-color-rgb, 104, 210, 254, 0.15);
+
+    &.active, &:hover {
+      background-color: var(--bs-link-color);
+      color: var(--bs-body-bg);
+    }
+  }
+
+  .new-entry--link {
+    color: var(--bs-link-color);
+  }
+}
diff --git a/landing-pages/site/assets/scss/_colors.scss 
b/landing-pages/site/assets/scss/_colors.scss
index 4eee5b3f3e..948b34cc78 100644
--- a/landing-pages/site/assets/scss/_colors.scss
+++ b/landing-pages/site/assets/scss/_colors.scss
@@ -33,7 +33,7 @@ $colors: (
         greyish-brown: #51504f
 );
 
-// Background color utility class for Docsy theme compatibility
+// Background color utility class required by Docsy v0.6.0+ theme
 .-bg-orange {
-  background-color: #FFA630; // Using $secondary color
+  background-color: #FFA630; // Using $secondary color value
 }
diff --git a/landing-pages/site/assets/scss/_header.scss 
b/landing-pages/site/assets/scss/_header.scss
index 324623886d..7e0b7a37ac 100644
--- a/landing-pages/site/assets/scss/_header.scss
+++ b/landing-pages/site/assets/scss/_header.scss
@@ -22,6 +22,8 @@
   position: relative;
   margin: 123px -20px 0;
   min-height: calc(100vh - 123px);
+  // background-color is handled by the canvas animation which draws 
white/dark background
+  transition: background-color 0.3s ease;
 }
 
 #header-canvas {
@@ -119,3 +121,22 @@
     }
   }
 }
+
+// Dark mode styles
+[data-bs-theme="dark"] {
+  #header {
+    // background-color handled by canvas animation
+  }
+
+  #header-canvas {
+    .text-area {
+      &--header {
+        color: rgba(255, 255, 255, 0.95);
+      }
+
+      &--subheader {
+        color: rgba(255, 255, 255, 0.75);
+      }
+    }
+  }
+}
diff --git a/landing-pages/site/assets/scss/_navbar.scss 
b/landing-pages/site/assets/scss/_navbar.scss
index 64b88e2a90..96b8c664ac 100644
--- a/landing-pages/site/assets/scss/_navbar.scss
+++ b/landing-pages/site/assets/scss/_navbar.scss
@@ -28,6 +28,7 @@
   border-bottom: solid 1px map-get($colors, very-light-pink);
   z-index: 32;
   padding: 30px 60px;
+  transition: background-color 0.3s ease, border-color 0.3s ease;
 
   &__menu-container {
     flex-grow: 1;
@@ -50,6 +51,7 @@
     margin-right: 30px;
     position: relative;
     width: fit-content;
+    transition: color 0.3s ease;
 
     &::before, &::after {
       content: "";
@@ -74,6 +76,43 @@
   &--box-shadow {
     box-shadow: 0 2px 6px 0 rgba(0, 0, 0, .12);
   }
+
+  // Theme toggle styling
+  &__theme-toggle {
+    display: inline-flex;
+    align-items: center;
+    position: relative;
+
+    .btn {
+      padding: 0.25rem 0.5rem;
+      font-size: 0.875rem;
+      border: none;
+      background: none;
+
+      svg {
+        width: 1rem;
+        height: 1rem;
+      }
+    }
+
+    .dropdown-menu {
+      font-size: 0.8125rem;
+      min-width: 7rem;
+      padding: 0.25rem 0;
+      left: 0 !important;
+      right: auto !important;
+
+      .dropdown-item {
+        padding: 0.375rem 0.75rem;
+        font-size: 0.8125rem;
+
+        svg {
+          width: 0.875rem;
+          height: 0.875rem;
+        }
+      }
+    }
+  }
 }
 
 @media (max-width: $tablet) {
@@ -154,3 +193,31 @@
     }
   }
 }
+
+// Dark mode styles
+[data-bs-theme="dark"] {
+  .navbar {
+    background-color: #1a1a1a;
+    border-bottom-color: #333;
+
+    &__text-link {
+      color: rgba(255, 255, 255, 0.85);
+
+      &:hover, &.active {
+        color: rgba(255, 255, 255, 1);
+      }
+    }
+
+    // Make logo text (Apache Airflow) white in dark mode
+    &__icon-container {
+      svg path[fill="#51504f"] {
+        fill: rgba(255, 255, 255, 0.95);
+      }
+    }
+
+    // Also handle mobile drawer background
+    &__drawer {
+      background-color: #1a1a1a;
+    }
+  }
+}
diff --git a/landing-pages/site/assets/scss/_rating.scss 
b/landing-pages/site/assets/scss/_rating.scss
index afaf719fc9..83594416a4 100644
--- a/landing-pages/site/assets/scss/_rating.scss
+++ b/landing-pages/site/assets/scss/_rating.scss
@@ -47,3 +47,31 @@
     }
   }
 }
+
+// Dark mode styles
+// stylelint-disable no-descending-specificity
+[data-bs-theme="dark"] {
+  .rating-container {
+    p {
+      color: rgba(255, 255, 255, 0.85);
+    }
+  }
+
+  .rate-star {
+    svg {
+      path {
+        stroke: rgba(255, 255, 255, 0.75);
+      }
+    }
+
+    &:hover, &:hover ~ & {
+      svg {
+        path {
+          fill: #68d2fe;
+          stroke: none;
+        }
+      }
+    }
+  }
+}
+// stylelint-enable no-descending-specificity
diff --git a/landing-pages/site/assets/scss/_roadmap.scss 
b/landing-pages/site/assets/scss/_roadmap.scss
index 599523671a..3b5b4bf99b 100644
--- a/landing-pages/site/assets/scss/_roadmap.scss
+++ b/landing-pages/site/assets/scss/_roadmap.scss
@@ -288,3 +288,8 @@
     }
   }
 }
+
+// Dark mode styles for roadmap page
+[data-bs-theme="dark"] .roadmap .td-sidebar {
+  background-color: #2d2d2d !important;
+}
diff --git a/landing-pages/site/assets/scss/_rst-content.scss 
b/landing-pages/site/assets/scss/_rst-content.scss
index 404c584dc0..b5bb9cec21 100644
--- a/landing-pages/site/assets/scss/_rst-content.scss
+++ b/landing-pages/site/assets/scss/_rst-content.scss
@@ -17,21 +17,28 @@
  * under the License.
  */
 
+// Set Open Sans as the default body font to match production behavior
+// This ensures both Hugo pages and Sphinx docs use the same font stack
+body {
+  font-family: "Open Sans", -apple-system, system-ui, "Segoe UI", Roboto, 
"Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", 
"Segoe UI Symbol";
+}
+
 .rst-content {
-  color: #707070;
+  // Use Bootstrap CSS variables for automatic light/dark mode support
+  color: var(--bs-body-color);
 
   h1 {
     scroll-margin-top: 130px;
     margin-bottom: 30px;
     font-weight: 500;
     font-family: "Rubik", sans-serif;
-    color: #51504f;
+    color: var(--bs-emphasis-color);
     font-size: 225%;
   }
 
   h2, h3, h4, h5, h6, p {
     font-family: 'Roboto', sans-serif;
-    color: #707070;
+    color: var(--bs-body-color);
   }
 
   h2, h3, h4, h5, h6 {
@@ -69,17 +76,21 @@
 
   code {
     max-width: 100%;
-    color: #51504f;
+    color: var(--bs-code-color);
+    background-color: var(--bs-secondary-bg);
     padding: 0 5px;
     font-family: 'Roboto Mono', monospace;
     overflow-x: auto;
   }
 
-  .note, .attention, .caution, .danger, .error, .hint, .important, .tip, 
.warning, .admonition-todo, .admonition {
+  // Admonitions using Bootstrap contextual colors
+  .note, .seealso, .attention, .caution, .danger, .error, .hint, .important, 
.tip, .warning, .admonition-todo, .admonition {
     padding: 9px 10px;
     line-height: 24px;
     margin-bottom: 24px;
-    background: #e7f2fa;
+    background: var(--bs-info-bg-subtle);
+    border-left: 4px solid var(--bs-info-border-subtle);
+    color: var(--bs-body-color);
   }
 
   @media (max-width: 768px) {
@@ -91,110 +102,87 @@
 
   .admonition-title:before {
     content: "!";
-    background-color: white;
+    background-color: var(--bs-body-bg);
     border-radius: 50%;
     padding: 0 4px;
     margin-right: 5px;
   }
 
   .admonition-title {
-    color: #fff;
+    color: var(--bs-emphasis-color);
     font-weight: 500;
     font-size: 10px;
     line-height: 2.1;
     display: block;
-    background: #68d1ff;
+    background: var(--bs-info-bg-subtle);
     margin: -10px;
     padding: 0 12px;
     margin-bottom: 9px;
   }
 
+  // Danger/Error admonitions
   .danger, .error {
-    background: #fdece9;
-
-    &::before {
-      color: #fdece9;
-    }
+    background: var(--bs-danger-bg-subtle);
+    border-left-color: var(--bs-danger-border-subtle);
   }
 
   .danger .admonition-title, .error .admonition-title {
-    background: #ee8170;
-
-    &::before {
-      color: #ee8170;
-    }
+    background: var(--bs-danger-bg-subtle);
+    color: var(--bs-danger-text-emphasis);
   }
 
+  // Warning/Caution/Attention admonitions
   .attention, .caution {
-    background: #fff8f6;
+    background: var(--bs-warning-bg-subtle);
+    border-left-color: var(--bs-warning-border-subtle);
   }
 
   .warning {
-    background: #f8f8f8;
+    background: var(--bs-secondary-bg);
+    border-left-color: var(--bs-border-color);
   }
 
   .attention .admonition-title, .caution .admonition-title {
-    background: #ffa996;
-
-    &::before {
-      color: #ffa996;
-    }
+    background: var(--bs-warning-bg-subtle);
+    color: var(--bs-warning-text-emphasis);
   }
 
   .warning .admonition-title {
-    background: #a6a6a6;
-
-    &::before {
-      color: #a6a6a6;
-    }
+    background: var(--bs-secondary-bg);
+    color: var(--bs-emphasis-color);
   }
 
+  // Note/Seealso - info colored
   .note, .seealso {
-    background: #f3fbff;
+    background: var(--bs-info-bg-subtle);
+    border-left-color: var(--bs-info-border-subtle);
   }
 
   .note .admonition-title, .seealso .admonition-title {
-    background: #68d2fe;
-
-    &::before {
-      color: #68d2fe;
-    }
-  }
-
-  .hint {
-    background: #f2fef6;
+    background: var(--bs-info-bg-subtle);
+    color: var(--bs-info-text-emphasis);
   }
 
-  .important {
-    background: #e6f9fc;
+  // Hint/Tip - success colored
+  .hint, .tip {
+    background: var(--bs-success-bg-subtle);
+    border-left-color: var(--bs-success-border-subtle);
   }
 
-  .tip {
-    background: #e5f7ec;
+  .hint .admonition-title, .tip .admonition-title {
+    background: var(--bs-success-bg-subtle);
+    color: var(--bs-success-text-emphasis);
   }
 
-  .hint .admonition-title {
-    background: #63e598;
-
-    &::before {
-      color: #63e598;
-    }
+  // Important - info colored
+  .important {
+    background: var(--bs-info-bg-subtle);
+    border-left-color: var(--bs-info-border-subtle);
   }
 
   .important .admonition-title {
-    background: #5bdae3;
-
-    &::before {
-      color: #5bdae3;
-    }
-  }
-
-  .tip .admonition-title {
-    background: #5bcb88;
-
-    &::before {
-      color: #5bcb88;
-    }
+    background: var(--bs-info-bg-subtle);
+    color: var(--bs-info-text-emphasis);
   }
 
   .note p:last-child, .attention p:last-child, .caution p:last-child, .danger 
p:last-child, .error p:last-child, .hint p:last-child, .important p:last-child, 
.tip p:last-child, .warning p:last-child, .seealso p:last-child, .admonition 
p:last-child {
@@ -237,7 +225,7 @@
   }
 
   pre {
-    background-color: #f2f8fe;
+    background-color: var(--bs-secondary-bg);
   }
 
   pre.literal-block, .linenodiv pre {
@@ -345,14 +333,14 @@
   }
 
   table.docutils thead th, table.field-list thead th {
-    border-bottom: solid 1px rgba(81, 80, 79, 0.3);
-    border-left: solid 1px rgba(81, 80, 79, 0.3);
+    border-bottom: solid 1px var(--bs-border-color);
+    border-left: solid 1px var(--bs-border-color);
   }
 
   table.docutils thead th p, table.field-list thead th p {
     font-weight: bold;
     font-size: 18px;
-    color: #51504f;
+    color: var(--bs-emphasis-color);
     line-height: 1.33;
     margin-bottom: 0;
   }
@@ -367,16 +355,16 @@
   }
 
   table.docutils:not(.field-list) tr:nth-child(2n-1) td {
-    background-color: rgba(112, 112, 112, 0.05);
+    background-color: var(--bs-secondary-bg);
   }
 
   table.docutils {
-    border: 1px solid rgba(81, 80, 79, 0.3);
+    border: 1px solid var(--bs-border-color);
   }
 
   table.docutils td {
-    border-bottom: 1px solid rgba(81, 80, 79, 0.3);
-    border-left: 1px solid rgba(81, 80, 79, 0.3);
+    border-bottom: 1px solid var(--bs-border-color);
+    border-left: 1px solid var(--bs-border-color);
   }
 
   table.docutils tbody > tr:last-child td {
@@ -403,12 +391,12 @@
   }
 
   code.literal {
-    color: #E74C3C
+    color: var(--bs-danger-text-emphasis);
   }
 
   code.xref, a code {
     font-weight: bold;
-    color: #707070;
+    color: var(--bs-body-color);
   }
 
   pre, kbd {
@@ -424,7 +412,7 @@
   }
 
   a code {
-    color: #2980B9
+    color: var(--bs-link-color);
   }
 
   dl {
@@ -456,9 +444,9 @@
     margin: 6px 0;
     font-size: 100%;
     line-height: 1.63;
-    background: #f3fbff;
-    color: #51504f;
-    border-top: solid 4px #68d1ff;
+    background: var(--bs-info-bg-subtle);
+    color: var(--bs-emphasis-color);
+    border-top: solid 4px var(--bs-info-border-subtle);
     padding: 8px 10px;
     position: relative;
 
@@ -468,28 +456,28 @@
   }
 
   dl:not(.docutils) dt:before {
-    color: #68d1ff
+    color: var(--bs-info-text-emphasis);
   }
 
   dl:not(.docutils) dt .headerlink {
-    color: #707070;
+    color: var(--bs-body-color);
     font-size: 100% !important
   }
 
   dl:not(.docutils) dt .fn-backref {
-    color: #0cb6ff;
+    color: var(--bs-link-color);
   }
 
   dl:not(.docutils) dl dt {
     margin-bottom: 6px;
     border: none;
-    border-left: solid 8px #a6a6a6;
-    background: #f8f8f8;
-    color: #707070
+    border-left: solid 8px var(--bs-border-color);
+    background: var(--bs-secondary-bg);
+    color: var(--bs-body-color);
   }
 
   dl:not(.docutils) dl dt .headerlink {
-    color: #707070;
+    color: var(--bs-body-color);
     font-size: 100% !important
   }
 
@@ -515,7 +503,7 @@
   dl:not(.docutils) .optional {
     display: inline-block;
     padding: 0 4px;
-    color: #51504f;
+    color: var(--bs-emphasis-color);
     font-weight: bold
   }
 
@@ -535,7 +523,7 @@
 
   .example-header {
     position: relative;
-    background: #017cee;
+    background: var(--bs-primary);
     padding: 8px 16px;
     margin-bottom: 0;
   }
@@ -605,11 +593,11 @@
   }
 
   .viewcode-button:visited {
-    color: #404040;
+    color: var(--bs-body-color);
   }
 
   .viewcode-button:hover, .viewcode-button:focus {
-    color: #404040;
+    color: var(--bs-emphasis-color);
   }
 
   .section {
@@ -624,3 +612,264 @@
     }
   }
 }
+
+// Dark mode styles using Bootstrap 5's CSS variables
+// This ensures consistency with Bootstrap's design system
+[data-bs-theme="dark"] {
+  .rst-content {
+    color: var(--bs-body-color);
+
+    h1, h2, h3, h4, h5, h6 {
+      color: var(--bs-emphasis-color);
+    }
+
+    p {
+      color: var(--bs-body-color);
+    }
+
+    // Make rubric headings more visible
+    p.rubric {
+      color: var(--bs-emphasis-color);
+      font-weight: bold;
+    }
+
+    a {
+      color: var(--bs-link-color);
+
+      &:hover {
+        color: var(--bs-link-hover-color);
+      }
+    }
+
+    code {
+      background-color: var(--bs-secondary-bg);
+      color: var(--bs-code-color);
+    }
+
+            // Syntax highlighting for code blocks in dark mode
+            // Generated via: cd sphinx_airflow_theme/demo && uv run 
pygmentize -S github-dark -f html -a ".highlight" > 
../../landing-pages/site/assets/scss/pygments/_dark.scss
+            // To change theme: replace "github-dark" with another Pygments 
style (run: uv run pygmentize -L styles)
+            @import "pygments/dark";
+
+    // Override background to use Bootstrap variables for consistency
+    .highlight {
+      background: var(--bs-secondary-bg) !important;
+
+      pre {
+        background: var(--bs-secondary-bg) !important;
+      }
+    }
+
+    pre {
+      background: var(--bs-secondary-bg) !important;
+      border-color: var(--bs-border-color);
+      color: var(--bs-body-color);
+    }
+
+    // Doc test blocks
+    .doctest {
+      background: var(--bs-secondary-bg) !important;
+
+      .gp, .go {
+        color: var(--bs-body-color) !important;
+      }
+    }
+
+    table {
+      border-color: var(--bs-border-color);
+
+      th {
+        background: var(--bs-tertiary-bg) !important;
+        color: var(--bs-emphasis-color) !important;
+        border-color: var(--bs-border-color) !important;
+      }
+
+      td {
+        border-color: var(--bs-border-color);
+        color: var(--bs-body-color);
+        background-color: var(--bs-body-bg);
+      }
+
+      // Alternating row colors for better readability
+      &.docutils:not(.field-list) tr:nth-child(2n-1) td {
+        background-color: var(--bs-secondary-bg) !important;
+      }
+    }
+
+    .admonition {
+      background-color: var(--bs-secondary-bg);
+      border-color: var(--bs-border-color);
+
+      .admonition-title {
+        background-color: var(--bs-tertiary-bg);
+        color: var(--bs-emphasis-color);
+      }
+    }
+
+    // Info-type admonitions (blue/teal)
+    .note, .seealso, .important {
+      background: var(--bs-info-bg-subtle);
+      border-color: var(--bs-info-border-subtle);
+    }
+
+    // Success-type admonitions (green/blue)
+    .hint, .tip {
+      background: var(--bs-success-bg-subtle);
+      border-color: var(--bs-success-border-subtle);
+    }
+
+    // Danger-type admonitions (red)
+    .error, .danger {
+      background: var(--bs-danger-bg-subtle);
+      border-color: var(--bs-danger-border-subtle);
+    }
+
+    // Warning-type admonitions (orange/yellow)
+    .warning, .attention, .caution {
+      background: var(--bs-warning-bg-subtle);
+      border-color: var(--bs-warning-border-subtle);
+    }
+
+    .viewcode-button {
+      color: var(--bs-body-color);
+
+      &:visited, &:hover, &:focus {
+        color: var(--bs-emphasis-color);
+      }
+    }
+
+    dt {
+      color: var(--bs-emphasis-color);
+    }
+
+    dd {
+      color: var(--bs-secondary-color);
+    }
+  }
+
+  // Main layout
+  .base-layout {
+    background-color: var(--bs-body-bg);
+  }
+
+  .roadmap {
+    background-color: var(--bs-body-bg);
+  }
+
+  // Sidebar navigation
+  .wy-nav-side-toc {
+    background-color: var(--bs-secondary-bg) !important;
+    border-color: var(--bs-border-color);
+
+    a {
+      color: var(--bs-emphasis-color) !important; // Changed from 
secondary-color for better readability, !important to override .roadmap 
specificity
+
+      &:hover {
+        color: var(--bs-link-color) !important; // Use link color for 
interactivity
+      }
+    }
+
+    .caption {
+      color: var(--bs-emphasis-color);
+      font-weight: 600;
+    }
+  }
+
+  // Also handle roadmap-specific selectors
+  .roadmap .wy-nav-side-toc .wy-menu-vertical a {
+    color: var(--bs-emphasis-color) !important;
+
+    &:hover {
+      color: var(--bs-link-color) !important;
+    }
+  }
+
+  .td-sidebar {
+    background-color: var(--bs-secondary-bg) !important;
+    border-color: var(--bs-border-color);
+
+    a {
+      color: var(--bs-emphasis-color); // Changed from body-color for better 
visibility
+
+      &:hover, &.active {
+        color: var(--bs-link-color); // Use link color for interactivity
+      }
+    }
+
+    h2, h3, h4 {
+      color: var(--bs-emphasis-color);
+    }
+
+    // Make section headings like "REFERENCES" more visible
+    .td-sidebar-nav__section-title,
+    .caption {
+      color: var(--bs-emphasis-color) !important; // Changed from 
secondary-color
+      font-weight: 700; // Increased from 600
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+      font-size: 0.75rem;
+    }
+
+    .form-control {
+      background-color: var(--bs-tertiary-bg);
+      border-color: var(--bs-border-color);
+      color: var(--bs-body-color);
+
+      &::placeholder {
+        color: var(--bs-secondary-color);
+      }
+
+      &:focus {
+        background-color: var(--bs-body-bg);
+        border-color: var(--bs-link-color);
+        color: var(--bs-emphasis-color);
+      }
+    }
+
+    button {
+      background-color: var(--bs-secondary-bg);
+      border-color: var(--bs-border-color);
+      color: var(--bs-body-color);
+
+      &:hover {
+        background-color: var(--bs-tertiary-bg);
+        color: var(--bs-emphasis-color);
+      }
+    }
+  }
+
+  // Version selector and dropdowns
+  .docs-version-selector,
+  .sidebar__version-selector {
+    background-color: var(--bs-secondary-bg);
+    border-color: var(--bs-border-color);
+    color: var(--bs-body-color);
+
+    button {
+      background-color: var(--bs-secondary-bg);
+      border-color: var(--bs-border-color);
+      color: var(--bs-body-color);
+
+      &:hover {
+        background-color: var(--bs-tertiary-bg);
+      }
+    }
+  }
+
+  // Breadcrumbs
+  .breadcrumb {
+    background-color: transparent;
+
+    a {
+      color: var(--bs-link-color);
+    }
+
+    .breadcrumb-item {
+      color: var(--bs-secondary-color);
+
+      &::before {
+        color: var(--bs-border-color);
+      }
+    }
+  }
+}
diff --git a/landing-pages/site/assets/scss/_variables.scss 
b/landing-pages/site/assets/scss/_variables.scss
index 1ba7fe4335..93e12503ca 100644
--- a/landing-pages/site/assets/scss/_variables.scss
+++ b/landing-pages/site/assets/scss/_variables.scss
@@ -59,7 +59,7 @@ $code-color: darken($secondary, 20%) !default;
 // UI element colors
 
 $border-color: $gray-300 !default;
-$td-sidebar-tree-root-color: $primary !default;
+$td-sidebar-tree-root-color: $primary !default; // Required by Docsy v0.2.0+ 
for sidebar tree styling
 $td-sidebar-bg-color: rgba($primary, 0.03) !default;
 $td-sidebar-border-color: $border-color !default;
 
@@ -78,7 +78,7 @@ $link-hover-decoration: none !default;
 $google_font_name: "Open Sans" !default;
 $google_font_family: "Open+Sans:300,300i,400,400i,700,700i" !default;
 $web-font-path: "/external/css/OpenSans.css";
-$font-awesome-font-name: "Font Awesome 6 Free" !default;
+$font-awesome-font-name: "Font Awesome 6 Free" !default; // Required by Docsy 
v0.6.0+ for Font Awesome 6 compatibility
 
 $td-fonts-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 
"Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", 
"Segoe UI Symbol";
 
diff --git a/landing-pages/site/assets/scss/main-custom.scss 
b/landing-pages/site/assets/scss/main-custom.scss
index 60cb3e2ef3..c486fa2f8d 100644
--- a/landing-pages/site/assets/scss/main-custom.scss
+++ b/landing-pages/site/assets/scss/main-custom.scss
@@ -17,10 +17,10 @@
  * under the License.
  */
 
+@import url('/external/css/OpenSans.css');
 @import url('/external/css/Rubik.css');
 @import url('/external/css/Roboto.css');
 @import url('/external/css/RobotoMono.css');
-
 @import "typography";
 @import "accordion";
 @import "buttons";
diff --git a/landing-pages/site/assets/scss/pygments/_dark.scss 
b/landing-pages/site/assets/scss/pygments/_dark.scss
new file mode 100644
index 0000000000..8c42469020
--- /dev/null
+++ b/landing-pages/site/assets/scss/pygments/_dark.scss
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Generated using: pygmentize -S github-dark -f html -a '.highlight'
+
+/*
+ * Pygments Syntax Highlighting - GitHub Dark Theme
+ *
+ * DO NOT EDIT THIS FILE MANUALLY - IT IS GENERATED
+ *
+ * To regenerate:
+ *   cd sphinx_airflow_theme/demo
+ *   uv run pygmentize -S github-dark -f html -a ".highlight" > 
../../landing-pages/site/assets/scss/pygments/_dark.scss
+ *
+ * To list available themes: uv run pygmentize -L styles
+ *
+ * Generated: 2025-11-07
+ */
+
+pre { line-height: 125%; }
+td.linenos .normal { color: #6e7681; background-color: #0d1117; padding-left: 
5px; padding-right: 5px; }
+span.linenos { color: #6e7681; background-color: #0d1117; padding-left: 5px; 
padding-right: 5px; }
+td.linenos .special { color: #e6edf3; background-color: #6e7681; padding-left: 
5px; padding-right: 5px; }
+span.linenos.special { color: #e6edf3; background-color: #6e7681; 
padding-left: 5px; padding-right: 5px; }
+.highlight .hll { background-color: #6e7681 }
+.highlight { background: #0d1117; color: #E6EDF3 }
+.highlight .c { color: #8B949E; font-style: italic } /* Comment */
+.highlight .err { color: #F85149 } /* Error */
+.highlight .esc { color: #E6EDF3 } /* Escape */
+.highlight .g { color: #E6EDF3 } /* Generic */
+.highlight .k { color: #FF7B72 } /* Keyword */
+.highlight .l { color: #A5D6FF } /* Literal */
+.highlight .n { color: #E6EDF3 } /* Name */
+.highlight .o { color: #FF7B72; font-weight: bold } /* Operator */
+.highlight .x { color: #E6EDF3 } /* Other */
+.highlight .p { color: #E6EDF3 } /* Punctuation */
+.highlight .ch { color: #8B949E; font-style: italic } /* Comment.Hashbang */
+.highlight .cm { color: #8B949E; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #8B949E; font-weight: bold; font-style: italic } /* 
Comment.Preproc */
+.highlight .cpf { color: #8B949E; font-style: italic } /* Comment.PreprocFile 
*/
+.highlight .c1 { color: #8B949E; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #8B949E; font-weight: bold; font-style: italic } /* 
Comment.Special */
+.highlight .gd { color: #FFA198; background-color: #490202 } /* 
Generic.Deleted */
+.highlight .ge { color: #E6EDF3; font-style: italic } /* Generic.Emph */
+.highlight .ges { color: #E6EDF3; font-weight: bold; font-style: italic } /* 
Generic.EmphStrong */
+.highlight .gr { color: #FFA198 } /* Generic.Error */
+.highlight .gh { color: #79C0FF; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #56D364; background-color: #0F5323 } /* 
Generic.Inserted */
+.highlight .go { color: #8B949E } /* Generic.Output */
+.highlight .gp { color: #8B949E } /* Generic.Prompt */
+.highlight .gs { color: #E6EDF3; font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #79C0FF } /* Generic.Subheading */
+.highlight .gt { color: #FF7B72 } /* Generic.Traceback */
+.highlight .g-Underline { color: #E6EDF3; text-decoration: underline } /* 
Generic.Underline */
+.highlight .kc { color: #79C0FF } /* Keyword.Constant */
+.highlight .kd { color: #FF7B72 } /* Keyword.Declaration */
+.highlight .kn { color: #FF7B72 } /* Keyword.Namespace */
+.highlight .kp { color: #79C0FF } /* Keyword.Pseudo */
+.highlight .kr { color: #FF7B72 } /* Keyword.Reserved */
+.highlight .kt { color: #FF7B72 } /* Keyword.Type */
+.highlight .ld { color: #79C0FF } /* Literal.Date */
+.highlight .m { color: #A5D6FF } /* Literal.Number */
+.highlight .s { color: #A5D6FF } /* Literal.String */
+.highlight .na { color: #E6EDF3 } /* Name.Attribute */
+.highlight .nb { color: #E6EDF3 } /* Name.Builtin */
+.highlight .nc { color: #F0883E; font-weight: bold } /* Name.Class */
+.highlight .no { color: #79C0FF; font-weight: bold } /* Name.Constant */
+.highlight .nd { color: #D2A8FF; font-weight: bold } /* Name.Decorator */
+.highlight .ni { color: #FFA657 } /* Name.Entity */
+.highlight .ne { color: #F0883E; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #D2A8FF; font-weight: bold } /* Name.Function */
+.highlight .nl { color: #79C0FF; font-weight: bold } /* Name.Label */
+.highlight .nn { color: #FF7B72 } /* Name.Namespace */
+.highlight .nx { color: #E6EDF3 } /* Name.Other */
+.highlight .py { color: #79C0FF } /* Name.Property */
+.highlight .nt { color: #7EE787 } /* Name.Tag */
+.highlight .nv { color: #79C0FF } /* Name.Variable */
+.highlight .ow { color: #FF7B72; font-weight: bold } /* Operator.Word */
+.highlight .pm { color: #E6EDF3 } /* Punctuation.Marker */
+.highlight .w { color: #6E7681 } /* Text.Whitespace */
+.highlight .mb { color: #A5D6FF } /* Literal.Number.Bin */
+.highlight .mf { color: #A5D6FF } /* Literal.Number.Float */
+.highlight .mh { color: #A5D6FF } /* Literal.Number.Hex */
+.highlight .mi { color: #A5D6FF } /* Literal.Number.Integer */
+.highlight .mo { color: #A5D6FF } /* Literal.Number.Oct */
+.highlight .sa { color: #79C0FF } /* Literal.String.Affix */
+.highlight .sb { color: #A5D6FF } /* Literal.String.Backtick */
+.highlight .sc { color: #A5D6FF } /* Literal.String.Char */
+.highlight .dl { color: #79C0FF } /* Literal.String.Delimiter */
+.highlight .sd { color: #A5D6FF } /* Literal.String.Doc */
+.highlight .s2 { color: #A5D6FF } /* Literal.String.Double */
+.highlight .se { color: #79C0FF } /* Literal.String.Escape */
+.highlight .sh { color: #79C0FF } /* Literal.String.Heredoc */
+.highlight .si { color: #A5D6FF } /* Literal.String.Interpol */
+.highlight .sx { color: #A5D6FF } /* Literal.String.Other */
+.highlight .sr { color: #79C0FF } /* Literal.String.Regex */
+.highlight .s1 { color: #A5D6FF } /* Literal.String.Single */
+.highlight .ss { color: #A5D6FF } /* Literal.String.Symbol */
+.highlight .bp { color: #E6EDF3 } /* Name.Builtin.Pseudo */
+.highlight .fm { color: #D2A8FF; font-weight: bold } /* Name.Function.Magic */
+.highlight .vc { color: #79C0FF } /* Name.Variable.Class */
+.highlight .vg { color: #79C0FF } /* Name.Variable.Global */
+.highlight .vi { color: #79C0FF } /* Name.Variable.Instance */
+.highlight .vm { color: #79C0FF } /* Name.Variable.Magic */
+.highlight .il { color: #A5D6FF } /* Literal.Number.Integer.Long */
diff --git a/landing-pages/site/layouts/_default/baseof.html 
b/landing-pages/site/layouts/_default/baseof.html
index afbac71e21..0ab0ab5673 100644
--- a/landing-pages/site/layouts/_default/baseof.html
+++ b/landing-pages/site/layouts/_default/baseof.html
@@ -44,8 +44,6 @@
 </div>
 {{ partialCached "footer.html" . }}
 {{ partial "scripts.html" . }}
+{{ partial "hooks/body-end.html" . }}
 </body>
-{{ with .Site.Data.webpack }}
-    <script src="{{ relURL .main.js }}"></script>
-{{ end }}
 </html>
diff --git a/landing-pages/site/layouts/partials/navbar.html 
b/landing-pages/site/layouts/_partials/navbar.html
similarity index 88%
copy from landing-pages/site/layouts/partials/navbar.html
copy to landing-pages/site/layouts/_partials/navbar.html
index 858d9dcbac..882374cb8c 100644
--- a/landing-pages/site/layouts/partials/navbar.html
+++ b/landing-pages/site/layouts/_partials/navbar.html
@@ -29,6 +29,11 @@
         {{ template "menu-content" . }}
     </div>
     <div class="no-desktop navbar__drawer-container">
+        {{ if .Site.Params.ui.showLightDarkModeMenu -}}
+        <div class="navbar__theme-toggle">
+            {{ partial "theme-toggler" . }}
+        </div>
+        {{ end -}}
         <button class="navbar__toggle-button" id="navbar-toggle-button">
             {{ with resources.Get "icons/hamburger-icon.svg" }}
                 <div id="hamburger-icon" class="navbar__toggle-button--icon 
visible">
@@ -63,6 +68,11 @@
                     {{ $el.Name }}
                 </a>
             {{ end }}
+            {{ if .Site.Params.ui.showLightDarkModeMenu -}}
+            <div class="navbar__theme-toggle">
+                {{ partial "theme-toggler" . }}
+            </div>
+            {{ end -}}
         </div>
     </div>
 {{ end }}
diff --git a/landing-pages/site/layouts/_default/baseof.html 
b/landing-pages/site/layouts/partials/hooks/body-end.html
similarity index 50%
copy from landing-pages/site/layouts/_default/baseof.html
copy to landing-pages/site/layouts/partials/hooks/body-end.html
index afbac71e21..e783767480 100644
--- a/landing-pages/site/layouts/_default/baseof.html
+++ b/landing-pages/site/layouts/partials/hooks/body-end.html
@@ -17,35 +17,7 @@
  under the License.
 */}}
 
-<!doctype html>
-<html lang="{{ .Site.Language.Lang }}" class="no-js">
-<head>
-    {{ partial "head.html" . }}
-</head>
-<body class="td-{{ .Kind }}">
-<header>
-    {{ partial "navbar.html" . }}
-</header>
-<div class="container-fluid td-default">
-    {{ block "animation-header" . }}{{ end }}
-    <div class="home-page-layout base-layout">
-        <main role="main" class="td-main container">
-            {{ block "main" . }}{{ end }}
-        </main>
-        <div class="base-layout--button">
-            <div class="base-layout--scrollButton">
-                {{ partial "scroll-to-top" . }}
-            </div>
-            <div class="base-layout--suggestButton">
-                {{ partial "suggest-change" . }}
-            </div>
-        </div>
-    </div>
-</div>
-{{ partialCached "footer.html" . }}
-{{ partial "scripts.html" . }}
-</body>
+{{/* Include webpack-generated main.js for confetti animation and other 
features */}}
 {{ with .Site.Data.webpack }}
     <script src="{{ relURL .main.js }}"></script>
 {{ end }}
-</html>
diff --git a/landing-pages/site/layouts/partials/navbar.html 
b/landing-pages/site/layouts/partials/navbar.html
index 858d9dcbac..84e037a310 100644
--- a/landing-pages/site/layouts/partials/navbar.html
+++ b/landing-pages/site/layouts/partials/navbar.html
@@ -27,8 +27,18 @@
     </div>
     <div class="desktop-only navbar__menu-container">
         {{ template "menu-content" . }}
+        {{ if .Site.Params.ui.showLightDarkModeMenu -}}
+        <div class="navbar__theme-toggle">
+            {{ partial "theme-toggler" . }}
+        </div>
+        {{ end -}}
     </div>
     <div class="no-desktop navbar__drawer-container">
+        {{ if .Site.Params.ui.showLightDarkModeMenu -}}
+        <div class="navbar__theme-toggle">
+            {{ partial "theme-toggler" . }}
+        </div>
+        {{ end -}}
         <button class="navbar__toggle-button" id="navbar-toggle-button">
             {{ with resources.Get "icons/hamburger-icon.svg" }}
                 <div id="hamburger-icon" class="navbar__toggle-button--icon 
visible">
diff --git a/landing-pages/site/layouts/partials/scripts.html.backup 
b/landing-pages/site/layouts/partials/scripts.html.backup
deleted file mode 100644
index b6be1b26d4..0000000000
--- a/landing-pages/site/layouts/partials/scripts.html.backup
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<script src="/external/js/cdnjs.cloudflare.com-1.14.3-popper.min.js" 
integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
 crossorigin="anonymous"></script>
-<script src="/external/js/stackpath.bootstrapcdn.com-4.1.3-bootstrap.min.js" 
integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
 crossorigin="anonymous"></script>
-{{ $jsBase := resources.Get "js/base.js" }}
-{{ $jsAnchor := resources.Get "js/anchor.js" }}
-{{ $jsSearch := resources.Get "js/search.js" | resources.ExecuteAsTemplate 
"js/search.js" .Site.Home }}
-{{ $js := (slice $jsBase $jsAnchor $jsSearch) | resources.Concat "js/main.js" 
}}
-{{ if hugo.IsServer }}
-<script src="{{ $js.RelPermalink }}"></script>
-{{ else }}
-{{ $js := $js | minify | fingerprint }}
-<script src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}" 
crossorigin="anonymous"></script>
-{{ end }}
-{{ partial "hooks/body-end.html" . }}
diff --git a/landing-pages/src/js/headerAnimation.js 
b/landing-pages/src/js/headerAnimation.js
index 55c11f65c1..9b77f3a49d 100644
--- a/landing-pages/src/js/headerAnimation.js
+++ b/landing-pages/src/js/headerAnimation.js
@@ -210,7 +210,6 @@ export function initHeaderAnimation() {
   const button = document.querySelector("#header-button");
 
   documentReady(function() {
-
     new p5((sketch) => {
 
       let colors = [];
@@ -237,7 +236,14 @@ export function initHeaderAnimation() {
       };
 
       sketch.draw = () => {
-        sketch.background(255, 255, 255);
+        // Dark mode support: dynamically set canvas background based on theme
+        const isDarkMode = 
document.documentElement.getAttribute("data-bs-theme") === "dark";
+        if (isDarkMode) {
+          sketch.background(26, 26, 26); // #1a1a1a to match dark navbar 
background
+        } else {
+          sketch.background(255, 255, 255);
+        }
+
         if (DRAW_SAFE_AREA) {
           logoArea.draw(sketch);
         }
diff --git a/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py 
b/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
index 3ca55c2761..b9a320e5d3 100644
--- a/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
+++ b/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
@@ -18,7 +18,7 @@
 from os import path
 from sphinx.application import Sphinx
 
-__version__ = '0.2.3'
+__version__ = '0.3.0'
 __version_full__ = __version__
 
 
diff --git a/sphinx_airflow_theme/sphinx_airflow_theme/globaltoc.html 
b/sphinx_airflow_theme/sphinx_airflow_theme/globaltoc.html
index c5d5951628..11c277fb67 100644
--- a/sphinx_airflow_theme/sphinx_airflow_theme/globaltoc.html
+++ b/sphinx_airflow_theme/sphinx_airflow_theme/globaltoc.html
@@ -87,4 +87,24 @@
         color: #707070;
     }
 
+    /* Dark mode support */
+    [data-bs-theme="dark"] .toctree .caption {
+        color: rgba(255, 255, 255, 0.95);
+    }
+    [data-bs-theme="dark"] .toctree li {
+        color: rgba(255, 255, 255, 0.85);
+    }
+    [data-bs-theme="dark"] .toctree a {
+        color: rgba(255, 255, 255, 0.85);
+    }
+    [data-bs-theme="dark"] .toctree a:hover {
+        color: rgba(255, 255, 255, 1);
+    }
+    [data-bs-theme="dark"] .toctree .current > a:not([href="#"]) {
+        color: #68d2fe;
+    }
+    [data-bs-theme="dark"] .toctree .current {
+        color: #68d2fe;
+    }
+
 </style>
diff --git a/sphinx_airflow_theme/sphinx_airflow_theme/header.html 
b/sphinx_airflow_theme/sphinx_airflow_theme/header.html
index f8f1d64554..9bdc02163f 100644
--- a/sphinx_airflow_theme/sphinx_airflow_theme/header.html
+++ b/sphinx_airflow_theme/sphinx_airflow_theme/header.html
@@ -59,6 +59,40 @@
                             {{ link.text }}
                         </a>
                     {% endfor %}
+                    <div class="navbar__theme-toggle">
+                        <button class="btn btn-link nav-link dropdown-toggle 
d-flex align-items-center"
+                                id="bd-theme"
+                                type="button"
+                                aria-expanded="false"
+                                data-bs-toggle="dropdown"
+                                data-bs-display="static"
+                                aria-label="Toggle theme (auto)">
+                            <svg class="bi my-1 theme-icon-active"><use 
href="#circle-half"></use></svg>
+                        </button>
+                        <ul class="dropdown-menu dropdown-menu-end" 
aria-labelledby="bd-theme-text">
+                            <li>
+                                <button type="button" class="dropdown-item 
d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
+                                    <svg class="bi me-2 opacity-50"><use 
href="#sun-fill"></use></svg>
+                                    Light
+                                    <svg class="bi ms-auto d-none"><use 
href="#check2"></use></svg>
+                                </button>
+                            </li>
+                            <li>
+                                <button type="button" class="dropdown-item 
d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
+                                    <svg class="bi me-2 opacity-50"><use 
href="#moon-stars-fill"></use></svg>
+                                    Dark
+                                    <svg class="bi ms-auto d-none"><use 
href="#check2"></use></svg>
+                                </button>
+                            </li>
+                            <li>
+                                <button type="button" class="dropdown-item 
d-flex align-items-center active" data-bs-theme-value="auto" 
aria-pressed="true">
+                                    <svg class="bi me-2 opacity-50"><use 
href="#circle-half"></use></svg>
+                                    Auto
+                                    <svg class="bi ms-auto d-none"><use 
href="#check2"></use></svg>
+                                </button>
+                            </li>
+                        </ul>
+                    </div>
                 </div>
 
                 {% if not theme_hide_website_buttons %}
diff --git a/sphinx_airflow_theme/sphinx_airflow_theme/layout.html 
b/sphinx_airflow_theme/sphinx_airflow_theme/layout.html
index 09a7566fe4..c518458ff9 100644
--- a/sphinx_airflow_theme/sphinx_airflow_theme/layout.html
+++ b/sphinx_airflow_theme/sphinx_airflow_theme/layout.html
@@ -215,8 +215,8 @@
     <script type="text/javascript" src="{{ pathto('_static/_gen/js/docs.js', 
1) }}"></script>
     <script type="text/javascript" id="documentation_options" 
data-url_root="{{ pathto('', 1) }}" src="{{ 
pathto('_static/documentation_options.js', 1) }}"></script>
     <script src="/external/js/jquery-3.4.1.min.js" 
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" 
crossorigin="anonymous"></script>
-    <script src="/external/js/cdnjs.cloudflare.com-1.14.3-popper.min.js" 
integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
 crossorigin="anonymous"></script>
-    <script 
src="/external/js/stackpath.bootstrapcdn.com-4.1.3-bootstrap.min.js" 
integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
 crossorigin="anonymous"></script>
+    <script 
src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"; 
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
 crossorigin="anonymous"></script>
+    <script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"; 
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
 crossorigin="anonymous"></script>
     {%- for js in script_files %}
         {{ js_tag(js) }}
     {%- endfor %}
@@ -324,6 +324,105 @@
 
 {%- block body_tag %}<body class="td-section">{% endblock %}
 
+<svg xmlns="http://www.w3.org/2000/svg"; class="d-none">
+  <symbol id="check2" viewBox="0 0 16 16">
+    <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 
0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
+  </symbol>
+  <symbol id="circle-half" viewBox="0 0 16 16">
+    <path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
+  </symbol>
+  <symbol id="moon-stars-fill" viewBox="0 0 16 16">
+    <path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 
4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 
.81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 
7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
+    <path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 
1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 
1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 
6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 
1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 
0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 
.274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l- 
[...]
+  </symbol>
+  <symbol id="sun-fill" viewBox="0 0 16 16">
+    <path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 
1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 
13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 
1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 
.707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 
9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 
.707 0zm9.193 2.121a.5.5 0 0 1-.70 [...]
+  </symbol>
+</svg>
+
+<script>
+  /*!
+   * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
+   * Copyright 2011-2024 The Bootstrap Authors
+   * Licensed under Creative Commons Attribution 3.0 Unported License.
+   */
+
+  (() => {
+    'use strict'
+
+    const getStoredTheme = () => localStorage.getItem('theme')
+    const setStoredTheme = theme => localStorage.setItem('theme', theme)
+
+    const getPreferredTheme = () => {
+      const storedTheme = getStoredTheme()
+      if (storedTheme) {
+        return storedTheme
+      }
+
+      return window.matchMedia('(prefers-color-scheme: dark)').matches ? 
'dark' : 'light'
+    }
+
+    const setTheme = theme => {
+      if (theme === 'auto') {
+        document.documentElement.setAttribute('data-bs-theme', 
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
+      } else {
+        document.documentElement.setAttribute('data-bs-theme', theme)
+      }
+    }
+
+    setTheme(getPreferredTheme())
+
+    const showActiveTheme = (theme, focus = false) => {
+      const themeSwitcher = document.querySelector('#bd-theme')
+
+      if (!themeSwitcher) {
+        return
+      }
+
+      const themeSwitcherText = document.querySelector('#bd-theme-text')
+      const activeThemeIcon = document.querySelector('.theme-icon-active use')
+      const btnToActive = 
document.querySelector(`[data-bs-theme-value="${theme}"]`)
+      const svgOfActiveBtn = btnToActive.querySelector('svg 
use').getAttribute('href')
+
+      document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
+        element.classList.remove('active')
+        element.setAttribute('aria-pressed', 'false')
+      })
+
+      btnToActive.classList.add('active')
+      btnToActive.setAttribute('aria-pressed', 'true')
+      activeThemeIcon.setAttribute('href', svgOfActiveBtn)
+      const themeSwitcherLabel = `${themeSwitcherText ? 
themeSwitcherText.textContent : ''} (${btnToActive.dataset.bsThemeValue})`
+      themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
+
+      if (focus) {
+        themeSwitcher.focus()
+      }
+    }
+
+    window.matchMedia('(prefers-color-scheme: 
dark)').addEventListener('change', () => {
+      const storedTheme = getStoredTheme()
+      if (storedTheme !== 'light' && storedTheme !== 'dark') {
+        setTheme(getPreferredTheme())
+      }
+    })
+
+    window.addEventListener('DOMContentLoaded', () => {
+      showActiveTheme(getPreferredTheme())
+
+      document.querySelectorAll('[data-bs-theme-value]')
+        .forEach(toggle => {
+          toggle.addEventListener('click', () => {
+            const theme = toggle.getAttribute('data-bs-theme-value')
+            setStoredTheme(theme)
+            setTheme(theme)
+            showActiveTheme(theme, true)
+          })
+        })
+    })
+  })()
+</script>
+
 {%- block header %}
     {% include "header.html" %}
 {% endblock %}

Reply via email to