From 54808a918745c7f9ea4e1a7a1631dcd4b47f9c05 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Thu, 11 Dec 2025 09:46:57 -0800 Subject: [PATCH] Refactor nav and navbar components - 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 | 72 ++++++++++++------- scss/_navbar.scss | 31 ++++---- site/src/content/docs/components/navbar.mdx | 6 +- .../src/content/docs/components/navs-tabs.mdx | 60 ++++++++-------- 4 files changed, 95 insertions(+), 74 deletions(-) diff --git a/scss/_nav.scss b/scss/_nav.scss index aa7fab7499..8e57bb0450 100644 --- a/scss/_nav.scss +++ b/scss/_nav.scss @@ -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 { diff --git a/scss/_navbar.scss b/scss/_navbar.scss index ed2fb9cb61..0be6d9b884 100644 --- a/scss/_navbar.scss +++ b/scss/_navbar.scss @@ -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,") !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 { diff --git a/site/src/content/docs/components/navbar.mdx b/site/src/content/docs/components/navbar.mdx index eafbcbfce5..f934763321 100644 --- a/site/src/content/docs/components/navbar.mdx +++ b/site/src/content/docs/components/navbar.mdx @@ -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. - +
Navbar
diff --git a/site/src/content/docs/components/navs-tabs.mdx b/site/src/content/docs/components/navs-tabs.mdx index 429ff106dd..3755ca9cac 100644 --- a/site/src/content/docs/components/navs-tabs.mdx +++ b/site/src/content/docs/components/navs-tabs.mdx @@ -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. -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. @@ -45,13 +43,11 @@ Classes are used throughout, so your markup can be super flexible. Use `
    `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). - + @@ -66,9 +62,11 @@ Centered with `.justify-content-center`:
`} /> -Right-aligned with `.justify-content-end`: +### Pills - +Take that same HTML, but use `.nav-pills` instead for a more themed appearance. + + @@ -83,11 +81,11 @@ Right-aligned with `.justify-content-end`: `} /> -### 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: - + @@ -102,20 +100,15 @@ Stack your navigation by changing the flex item direction with the `.flex-column `} /> -As always, vertical navigation is possible without `
    `s, too. +## Alignment - - Active - Link - Link - Disabled - `} /> +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`: - + @@ -130,11 +123,9 @@ Takes the basic nav from above and adds the `.nav-tabs` class to generate a tabb
`} /> -### Pills - -Take that same HTML, but use `.nav-pills` instead: +Right-aligned with `.justify-content-end`: - + @@ -149,11 +140,11 @@ Take that same HTML, but use `.nav-pills` instead: `} /> -### 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`). - + @@ -168,6 +159,15 @@ Take that same HTML, but use `.nav-underline` instead: `} /> +As always, vertical navigation is possible without `
    `s, too. + + + Active + Link + Link + Disabled + `} /> + ### 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. -- 2.47.3