]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Refactor nav and navbar components
authorMark Otto <markdotto@gmail.com>
Thu, 11 Dec 2025 17:46:57 +0000 (09:46 -0800)
committerMark Otto <markdotto@gmail.com>
Thu, 11 Dec 2025 18:26:46 +0000 (10:26 -0800)
- Add $nav-gap and $nav-link-gap variables for flexbox gap layout
- Rename nav link color variables to use --bs-fg-* tokens
- Add active state variables ($nav-link-active-color, $nav-link-active-bg)
- Add hover background variable ($nav-link-hover-bg)
- Update .nav and .nav-link to use flexbox with gap
- Update related documentation

scss/_nav.scss
scss/_navbar.scss
site/src/content/docs/components/navbar.mdx
site/src/content/docs/components/navs-tabs.mdx

index aa7fab749987813b46c3fe043b3e3078e6c59773..8e57bb0450d80c28cc039b89e6ce46a3ab1ed83e 100644 (file)
@@ -1,30 +1,32 @@
 @use "config" as *;
 @use "variables" as *;
 @use "mixins/border-radius" as *;
-@use "mixins/transition" as *;
 @use "mixins/gradients" as *;
+@use "mixins/focus-ring" as *;
+@use "mixins/transition" as *;
 @use "vendor/rfs" as *;
 
 // scss-docs-start nav-variables
+$nav-gap:                           .125rem !default;
+$nav-link-gap:                      .5rem !default;
 $nav-link-padding-y:                .5rem !default;
 $nav-link-padding-x:                1rem !default;
-$nav-link-font-size:                null !default;
-$nav-link-font-weight:              null !default;
-$nav-link-color:                    var(--#{$prefix}link-color) !default;
-$nav-link-hover-color:              var(--#{$prefix}link-hover-color) !default;
+$nav-link-color:                    var(--#{$prefix}fg-2) !default;
+$nav-link-hover-color:              var(--#{$prefix}fg-1) !default;
+$nav-link-hover-bg:                 var(--#{$prefix}bg-1) !default;
 $nav-link-transition:               color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default;
-$nav-link-disabled-color:           var(--#{$prefix}secondary-color) !default;
-$nav-link-focus-box-shadow:         $focus-ring-box-shadow !default;
+$nav-link-disabled-color:           var(--#{$prefix}fg-4) !default;
+$nav-link-active-color:             var(--#{$prefix}fg-body) !default;
+$nav-link-active-bg:                var(--#{$prefix}bg-2) !default;
 
 $nav-tabs-border-color:             var(--#{$prefix}border-color) !default;
 $nav-tabs-border-width:             var(--#{$prefix}border-width) !default;
 $nav-tabs-border-radius:            var(--#{$prefix}border-radius) !default;
-$nav-tabs-link-hover-border-color:  var(--#{$prefix}secondary-bg) var(--#{$prefix}secondary-bg) $nav-tabs-border-color !default;
+$nav-tabs-link-hover-border-color:  var(--#{$prefix}border-subtle) !default;
 $nav-tabs-link-active-color:        var(--#{$prefix}emphasis-color) !default;
 $nav-tabs-link-active-bg:           var(--#{$prefix}bg-body) !default;
 $nav-tabs-link-active-border-color: var(--#{$prefix}border-color) var(--#{$prefix}border-color) $nav-tabs-link-active-bg !default;
 
-$nav-pills-border-radius:           var(--#{$prefix}border-radius) !default;
 $nav-pills-link-active-color:       $component-active-color !default;
 $nav-pills-link-active-bg:          $component-active-bg !default;
 
@@ -41,41 +43,62 @@ $nav-underline-link-active-color:   var(--#{$prefix}emphasis-color) !default;
 @layer components {
   .nav {
     // scss-docs-start nav-css-vars
+    --#{$prefix}nav-gap: #{$nav-gap};
+    --#{$prefix}nav-link-gap: #{$nav-link-gap};
     --#{$prefix}nav-link-padding-x: #{$nav-link-padding-x};
     --#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
-    @include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
-    --#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
     --#{$prefix}nav-link-color: #{$nav-link-color};
     --#{$prefix}nav-link-hover-color: #{$nav-link-hover-color};
+    --#{$prefix}nav-link-hover-bg: #{$nav-link-hover-bg};
+    --#{$prefix}nav-link-active-color: #{$nav-link-active-color};
+    --#{$prefix}nav-link-active-bg: #{$nav-link-active-bg};
     --#{$prefix}nav-link-disabled-color: #{$nav-link-disabled-color};
     // scss-docs-end nav-css-vars
 
     display: flex;
     flex-wrap: wrap;
+    gap: var(--#{$prefix}nav-gap);
     padding-inline-start: 0;
     margin-bottom: 0;
     list-style: none;
   }
 
+  .nav-item {
+    display: flex;
+
+  }
+
   .nav-link {
-    display: block;
+    display: flex;
+    gap: var(--#{$prefix}nav-link-gap);
+    align-items: center;
+    justify-content: center;
     padding: var(--#{$prefix}nav-link-padding-y) var(--#{$prefix}nav-link-padding-x);
-    @include font-size(var(--#{$prefix}nav-link-font-size));
     font-weight: var(--#{$prefix}nav-link-font-weight);
     color: var(--#{$prefix}nav-link-color);
     text-decoration: none;
     background: none;
     border: 0;
+    @include border-radius(var(--#{$prefix}border-radius));
+    // @include font-size(var(--#{$prefix}nav-link-font-size));
     @include transition($nav-link-transition);
 
     &:hover,
     &:focus {
       color: var(--#{$prefix}nav-link-hover-color);
+      background-color: var(--#{$prefix}nav-link-hover-bg);
     }
 
     &:focus-visible {
-      outline: 0;
-      box-shadow: $nav-link-focus-box-shadow;
+      color: var(--#{$prefix}nav-link-hover-color);
+      @include focus-ring(true);
+      --#{$prefix}focus-ring-offset: 1px;
+    }
+
+    &.active,
+    &:active {
+      color: var(--#{$prefix}nav-link-active-color);
+      background-color: var(--#{$prefix}nav-link-active-bg);
     }
 
     // Disabled state lightens text
@@ -107,13 +130,13 @@ $nav-underline-link-active-color:   var(--#{$prefix}emphasis-color) !default;
     .nav-link {
       margin-bottom: calc(-1 * var(--#{$prefix}nav-tabs-border-width));
       border: var(--#{$prefix}nav-tabs-border-width) solid transparent;
-      @include border-top-radius(var(--#{$prefix}nav-tabs-border-radius));
+      @include border-bottom-radius(0);
 
-      &:hover,
-      &:focus {
+      &:hover {
         // Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
         isolation: isolate;
         border-color: var(--#{$prefix}nav-tabs-link-hover-border-color);
+        border-bottom-color: var(--#{$prefix}nav-tabs-border-color);
       }
     }
 
@@ -122,6 +145,7 @@ $nav-underline-link-active-color:   var(--#{$prefix}emphasis-color) !default;
       color: var(--#{$prefix}nav-tabs-link-active-color);
       background-color: var(--#{$prefix}nav-tabs-link-active-bg);
       border-color: var(--#{$prefix}nav-tabs-link-active-border-color);
+      border-bottom-color: var(--#{$prefix}nav-tabs-link-active-bg);
     }
 
     .dropdown-menu {
@@ -139,15 +163,10 @@ $nav-underline-link-active-color:   var(--#{$prefix}emphasis-color) !default;
 
   .nav-pills {
     // scss-docs-start nav-pills-css-vars
-    --#{$prefix}nav-pills-border-radius: #{$nav-pills-border-radius};
     --#{$prefix}nav-pills-link-active-color: #{$nav-pills-link-active-color};
     --#{$prefix}nav-pills-link-active-bg: #{$nav-pills-link-active-bg};
     // scss-docs-end nav-pills-css-vars
 
-    .nav-link {
-      @include border-radius(var(--#{$prefix}nav-pills-border-radius));
-    }
-
     .nav-link.active,
     .show > .nav-link {
       color: var(--#{$prefix}nav-pills-link-active-color);
@@ -162,16 +181,17 @@ $nav-underline-link-active-color:   var(--#{$prefix}emphasis-color) !default;
 
   .nav-underline {
     // scss-docs-start nav-underline-css-vars
-    --#{$prefix}nav-underline-gap: #{$nav-underline-gap};
+    --#{$prefix}nav-gap: #{$nav-underline-gap};
+    --#{$prefix}nav-link-active-bg: transparent;
+    // --#{$prefix}nav-underline-gap: #{$nav-underline-gap};
     --#{$prefix}nav-underline-border-width: #{$nav-underline-border-width};
     --#{$prefix}nav-underline-link-active-color: #{$nav-underline-link-active-color};
     // scss-docs-end nav-underline-css-vars
 
-    gap: var(--#{$prefix}nav-underline-gap);
-
     .nav-link {
       padding-inline: 0;
       border-block-end: var(--#{$prefix}nav-underline-border-width) solid transparent;
+      @include border-radius(0);
 
       &:hover,
       &:focus {
index ed2fb9cb6122ca81545c37fa3faa061b61fdd41f..0be6d9b88474f2981f695134c2c91987254e6702 100644 (file)
@@ -15,7 +15,7 @@
 $navbar-padding-y:                  $spacer * .5 !default;
 $navbar-padding-x:                  null !default;
 
-$navbar-nav-link-padding-x:         .5rem !default;
+$navbar-nav-link-padding-x:         .75rem !default;
 
 $navbar-brand-font-size:            $font-size-lg !default;
 // Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link
@@ -34,10 +34,10 @@ $navbar-toggler-border-radius:      $btn-border-radius !default;
 $navbar-toggler-focus-width:        $btn-focus-width !default;
 $navbar-toggler-transition:         box-shadow .15s ease-in-out !default;
 
-$navbar-light-color:                color-mix(in srgb, var(--#{$prefix}fg-black) 65%, transparent) !default;
-$navbar-light-hover-color:          color-mix(in srgb, var(--#{$prefix}fg-black) 80%, transparent) !default;
-$navbar-light-active-color:         color-mix(in srgb, var(--#{$prefix}fg-black) 100%, transparent) !default;
-$navbar-light-disabled-color:       color-mix(in srgb, var(--#{$prefix}fg-black) 30%, transparent) !default;
+$navbar-light-color:                var(--#{$prefix}fg-2) !default;
+$navbar-light-hover-color:          var(--#{$prefix}fg-1) !default;
+$navbar-light-active-color:         var(--#{$prefix}fg) !default;
+$navbar-light-disabled-color:       var(--#{$prefix}fg-3) !default;
 $navbar-light-icon-color:           rgba($body-color, .75) !default;
 $navbar-light-toggler-icon-bg:      url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-light-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default;
 $navbar-light-toggler-border-color: rgba(var(--#{$prefix}emphasis-color-rgb), .15) !default;
@@ -139,7 +139,7 @@ $navbar-dark-brand-hover-color:     $navbar-dark-active-color !default;
 
   .navbar-nav {
     // scss-docs-start navbar-nav-css-vars
-    --#{$prefix}nav-link-padding-x: 0;
+    // --#{$prefix}nav-link-padding-x: 0;
     // @mdo-do: fix this, navbar shouldn't need to reuse nav link variables mb? or we need to bring them in…
     // --#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
     // @include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
@@ -162,9 +162,9 @@ $navbar-dark-brand-hover-color:     $navbar-dark-active-color !default;
       }
     }
 
-    .dropdown-menu {
-      position: static;
-    }
+    // .dropdown-menu {
+    //   position: static;
+    // }
   }
 
 
@@ -257,15 +257,16 @@ $navbar-dark-brand-hover-color:     $navbar-dark-active-color !default;
           justify-content: flex-start;
 
           .navbar-nav {
+            --#{$prefix}nav-link-padding-x: var(--#{$prefix}navbar-nav-link-padding-x);
             flex-direction: row;
 
-            .dropdown-menu {
-              position: absolute;
-            }
+            // .dropdown-menu {
+            //   position: absolute;
+            // }
 
-            .nav-link {
-              padding-inline: var(--#{$prefix}navbar-nav-link-padding-x);
-            }
+            // .nav-link {
+            //   padding-inline: var(--#{$prefix}navbar-nav-link-padding-x);
+            // }
           }
 
           .navbar-nav-scroll {
index eafbcbfce545dc9b415cf9d71b23e6f33ec45cbe..f9347633218049fb02562d4e404b532d6442a6b1 100644 (file)
@@ -33,14 +33,14 @@ Navbars come with built-in support for a handful of sub-components. Choose from
 
 Here’s an example of all the sub-components included in a responsive light-themed navbar that automatically collapses at the `lg` (large) breakpoint.
 
-<Example code={`<nav class="navbar navbar-expand-lg bg-body-tertiary">
+<Example code={`<nav class="navbar navbar-expand-lg">
     <div class="container-fluid">
       <a class="navbar-brand" href="#">Navbar</a>
       <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
         <span class="navbar-toggler-icon"></span>
       </button>
       <div class="collapse navbar-collapse" id="navbarSupportedContent">
-        <ul class="navbar-nav me-auto mb-2 mb-lg-0">
+        <ul class="nav navbar-nav me-auto mb-2 mb-lg-0">
           <li class="nav-item">
             <a class="nav-link active" aria-current="page" href="#">Home</a>
           </li>
@@ -64,7 +64,7 @@ Here’s an example of all the sub-components included in a responsive light-the
         </ul>
         <form class="d-flex" role="search">
           <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"/>
-          <button class="btn btn-outline-success" type="submit">Search</button>
+          <button class="btn btn-subtle theme-primary" type="submit">Search</button>
         </form>
       </div>
     </div>
index 429ff106dd161b5cb6e00a7fb8caac403e7c15f0..3755ca9cac2ad42cfb4bde260dea15795985057f 100644 (file)
@@ -1,6 +1,6 @@
 ---
 title: Navs and tabs
-description: Documentation and examples for how to use Bootstrap’s included navigation components.
+description: Add lists of links to your pages with Bootstrap’s nav components and JavaScript-powered tabs.
 aliases: "/docs/[[config:docs_version]]/components/navs/"
 toc: true
 ---
@@ -12,8 +12,6 @@ Navigation available in Bootstrap share general markup and styles, from the base
 The base `.nav` component is built with flexbox and provide a strong foundation for building all types of navigation components. It includes some style overrides (for working with lists), some link padding for larger hit areas, and basic disabled styling.
 
 <Callout>
-The base `.nav` component does not include any `.active` state. The following examples include the class, mainly to demonstrate that this particular class does not trigger any special styling.
-
 To convey the active state to assistive technologies, use the `aria-current` attribute — using the `page` value for current page, or `true` for the current item in a set.
 </Callout>
 
@@ -45,13 +43,11 @@ Classes are used throughout, so your markup can be super flexible. Use `<ul>`s l
 
 Change the style of `.nav`s component with modifiers and utilities. Mix and match as needed, or build your own.
 
-### Horizontal alignment
-
-Change the horizontal alignment of your nav with [flexbox utilities]([[docsref:/utilities/flex#justify-content]]). By default, navs are left-aligned, but you can easily change them to center or right-aligned.
+### Tabs
 
-Centered with `.justify-content-center`:
+Takes the basic nav from above and adds the `.nav-tabs` class to generate a tabbed interface. Use them to create tabbable regions with our [tab JavaScript plugin](#javascript-behavior).
 
-<Example code={`<ul class="nav justify-content-center">
+<Example code={`<ul class="nav nav-tabs">
     <li class="nav-item">
       <a class="nav-link active" aria-current="page" href="#">Active</a>
     </li>
@@ -66,9 +62,11 @@ Centered with `.justify-content-center`:
     </li>
   </ul>`} />
 
-Right-aligned with `.justify-content-end`:
+### Pills
 
-<Example code={`<ul class="nav justify-content-end">
+Take that same HTML, but use `.nav-pills` instead for a more themed appearance.
+
+<Example code={`<ul class="nav nav-pills">
     <li class="nav-item">
       <a class="nav-link active" aria-current="page" href="#">Active</a>
     </li>
@@ -83,11 +81,11 @@ Right-aligned with `.justify-content-end`:
     </li>
   </ul>`} />
 
-### Vertical
+### Underline
 
-Stack your navigation by changing the flex item direction with the `.flex-column` utility. Need to stack them on some viewports but not others? Use the responsive versions (e.g., `.flex-sm-column`).
+Take that same HTML, but use `.nav-underline` instead:
 
-<Example code={`<ul class="nav flex-column">
+<Example code={`<ul class="nav nav-underline">
     <li class="nav-item">
       <a class="nav-link active" aria-current="page" href="#">Active</a>
     </li>
@@ -102,20 +100,15 @@ Stack your navigation by changing the flex item direction with the `.flex-column
     </li>
   </ul>`} />
 
-As always, vertical navigation is possible without `<ul>`s, too.
+## Alignment
 
-<Example code={`<nav class="nav flex-column">
-    <a class="nav-link active" aria-current="page" href="#">Active</a>
-    <a class="nav-link" href="#">Link</a>
-    <a class="nav-link" href="#">Link</a>
-    <a class="nav-link disabled" aria-disabled="true">Disabled</a>
-  </nav>`} />
+Change the alignment of your nav with [flexbox utilities]([[docsref:/utilities/flex]]). By default, navs are horizontal and left-aligned, but you can easily modify them to center or right-aligned, and even stacked vertically.
 
-### Tabs
+### Horizontal
 
-Takes the basic nav from above and adds the `.nav-tabs` class to generate a tabbed interface. Use them to create tabbable regions with our [tab JavaScript plugin](#javascript-behavior).
+Centered with `.justify-content-center`:
 
-<Example code={`<ul class="nav nav-tabs">
+<Example code={`<ul class="nav justify-content-center">
     <li class="nav-item">
       <a class="nav-link active" aria-current="page" href="#">Active</a>
     </li>
@@ -130,11 +123,9 @@ Takes the basic nav from above and adds the `.nav-tabs` class to generate a tabb
     </li>
   </ul>`} />
 
-### Pills
-
-Take that same HTML, but use `.nav-pills` instead:
+Right-aligned with `.justify-content-end`:
 
-<Example code={`<ul class="nav nav-pills">
+<Example code={`<ul class="nav justify-content-end">
     <li class="nav-item">
       <a class="nav-link active" aria-current="page" href="#">Active</a>
     </li>
@@ -149,11 +140,11 @@ Take that same HTML, but use `.nav-pills` instead:
     </li>
   </ul>`} />
 
-### Underline
+### Vertical
 
-Take that same HTML, but use `.nav-underline` instead:
+Stack your navigation by changing the flex item direction with the `.flex-column` utility. Need to stack them on some viewports but not others? Use the responsive versions (e.g., `.flex-sm-column`).
 
-<Example code={`<ul class="nav nav-underline">
+<Example code={`<ul class="nav flex-column">
     <li class="nav-item">
       <a class="nav-link active" aria-current="page" href="#">Active</a>
     </li>
@@ -168,6 +159,15 @@ Take that same HTML, but use `.nav-underline` instead:
     </li>
   </ul>`} />
 
+As always, vertical navigation is possible without `<ul>`s, too.
+
+<Example code={`<nav class="nav flex-column">
+    <a class="nav-link active" aria-current="page" href="#">Active</a>
+    <a class="nav-link" href="#">Link</a>
+    <a class="nav-link" href="#">Link</a>
+    <a class="nav-link disabled" aria-disabled="true">Disabled</a>
+  </nav>`} />
+
 ### Fill and justify
 
 Force your `.nav`’s contents to extend the full available width with one of two modifier classes. To proportionately fill all available space with your `.nav-item`s, use `.nav-fill`. Notice that all horizontal space is occupied, but not every nav item has the same width.