| @use "functions" as *; |
| @use "config" as *; |
| @use "mixins/border-radius" as *; |
| @use "mixins/focus-ring" as *; |
| @use "mixins/gradients" as *; |
| @use "mixins/tokens" as *; |
| @use "mixins/transition" as *; |
| |
| $nav-tokens: () !default; |
| |
| // scss-docs-start nav-tokens |
| // stylelint-disable-next-line scss/dollar-variable-default |
| $nav-tokens: defaults( |
| ( |
| --nav-gap: .125rem, |
| --nav-link-gap: .5rem, |
| --nav-link-align: center, |
| --nav-link-justify: center, |
| --nav-link-padding-x: .75rem, |
| --nav-link-padding-y: .375rem, |
| --nav-link-color: var(--fg-2), |
| --nav-link-hover-color: var(--fg-1), |
| --nav-link-hover-bg: var(--bg-1), |
| --nav-link-active-color: var(--fg-body), |
| --nav-link-active-bg: var(--bg-2), |
| --nav-link-disabled-color: var(--fg-4), |
| --nav-link-border-width: var(--border-width), |
| --nav-link-transition-property: "color, background-color, border-color", |
| --nav-link-transition-duration: .15s, |
| --nav-link-transition-timing: ease-in-out, |
| ), |
| $nav-tokens |
| ); |
| // scss-docs-end nav-tokens |
| |
| $nav-tabs-tokens: () !default; |
| |
| // scss-docs-start nav-tabs-tokens |
| // stylelint-disable-next-line scss/dollar-variable-default |
| $nav-tabs-tokens: defaults( |
| ( |
| --nav-tabs-border-width: var(--border-width), |
| --nav-tabs-border-color: var(--border-color), |
| --nav-tabs-border-radius: var(--radius-5), |
| --nav-tabs-link-hover-border-color: var(--border-subtle), |
| --nav-tabs-link-active-color: var(--fg-color), |
| --nav-tabs-link-active-bg: var(--bg-body), |
| --nav-tabs-link-active-border-color: var(--border-color) var(--border-color) var(--bg-body), |
| ), |
| $nav-tabs-tokens |
| ); |
| // scss-docs-end nav-tabs-tokens |
| |
| $nav-pills-tokens: () !default; |
| |
| // scss-docs-start nav-pills-tokens |
| // stylelint-disable-next-line scss/dollar-variable-default |
| $nav-pills-tokens: defaults( |
| ( |
| --nav-pills-bg: var(--bg-1), |
| --nav-pills-padding: .25rem, |
| --nav-pills-border-radius: var(--radius-9), |
| --nav-pills-link-active-color: var(--primary-contrast), |
| --nav-pills-link-active-bg: var(--primary-bg), |
| --nav-pills-link-border-radius: var(--radius-9), |
| ), |
| $nav-pills-tokens |
| ); |
| // scss-docs-end nav-pills-tokens |
| |
| $nav-underline-tokens: () !default; |
| |
| // scss-docs-start nav-underline-tokens |
| // stylelint-disable-next-line scss/dollar-variable-default |
| $nav-underline-tokens: defaults( |
| ( |
| --nav-gap: 1rem, |
| --nav-link-active-bg: transparent, |
| --nav-underline-border-width: .125rem, |
| --nav-underline-link-active-color: var(--fg-color), |
| ), |
| $nav-underline-tokens |
| ); |
| // scss-docs-end nav-underline-tokens |
| |
| $tab-pane-tokens: () !default; |
| |
| // scss-docs-start tab-pane-tokens |
| // stylelint-disable-next-line scss/dollar-variable-default |
| $tab-pane-tokens: defaults( |
| ( |
| --tab-pane-transition-property: "opacity", |
| --tab-pane-transition-duration: .15s, |
| --tab-pane-transition-timing: linear, |
| ), |
| $tab-pane-tokens |
| ); |
| // scss-docs-end tab-pane-tokens |
| |
| // Base class |
| // |
| // Kickstart any navigation component with a set of style resets. Works with |
| // `<nav>`s, `<ul>`s or `<ol>`s. |
| |
| @layer components { |
| .nav { |
| @include tokens($nav-tokens); |
| |
| display: flex; |
| flex-wrap: wrap; |
| gap: var(--nav-gap); |
| padding-inline-start: 0; |
| margin-bottom: 0; |
| list-style-type: ""; |
| } |
| |
| .nav-item { |
| display: flex; |
| } |
| |
| .nav-link { |
| display: flex; |
| gap: var(--nav-link-gap); |
| align-items: var(--nav-link-align); |
| justify-content: var(--nav-link-justify); |
| padding: var(--nav-link-padding-y) var(--nav-link-padding-x); |
| font-weight: var(--nav-link-font-weight); |
| color: var(--theme-fg, var(--nav-link-color)); |
| text-decoration: none; |
| white-space: nowrap; |
| background: none; |
| border: var(--nav-link-border-width) solid transparent; |
| @include border-radius(var(--radius-5)); |
| @include transition-props( |
| var(--nav-link-transition-property), |
| var(--nav-link-transition-duration), |
| var(--nav-link-transition-timing) |
| ); |
| |
| &:hover, |
| &:focus { |
| color: var(--theme-fg-emphasis, var(--nav-link-hover-color)); |
| background-color: var(--theme-bg-muted, var(--nav-link-hover-bg)); |
| } |
| |
| &:focus-visible { |
| --focus-ring-offset: 1px; |
| color: var(--theme-fg-emphasis, var(--nav-link-hover-color)); |
| @include focus-ring(true); |
| } |
| |
| &.active, |
| &:active { |
| color: var(--theme-fg, var(--nav-link-active-color)); |
| background-color: var(--theme-bg-subtle, var(--nav-link-active-bg)); |
| } |
| |
| // Disabled state lightens text |
| &.disabled, |
| &:disabled { |
| color: var(--nav-link-disabled-color); |
| pointer-events: none; |
| cursor: default; |
| } |
| } |
| |
| // |
| // Tabs |
| // |
| |
| .nav-tabs { |
| // scss-docs-start nav-tabs-css-vars |
| @include tokens($nav-tabs-tokens); |
| // scss-docs-end nav-tabs-css-vars |
| |
| box-shadow: inset 0 calc(-1 * var(--nav-tabs-border-width)) 0 var(--nav-tabs-border-color); |
| |
| .nav-link { |
| border: var(--nav-tabs-border-width) solid transparent; |
| border-bottom-color: var(--nav-tabs-border-color); |
| @include border-bottom-radius(0); |
| |
| &:hover { |
| // Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link |
| isolation: isolate; |
| border-color: var(--nav-tabs-link-hover-border-color); |
| border-bottom-color: var(--nav-tabs-border-color); |
| } |
| } |
| |
| .nav-link.active, |
| .nav-item.show .nav-link { |
| color: var(--nav-tabs-link-active-color); |
| background-color: var(--nav-tabs-link-active-bg); |
| border-color: var(--nav-tabs-link-active-border-color); |
| border-bottom-color: var(--nav-tabs-link-active-bg); |
| } |
| |
| .menu { |
| margin-top: calc(-1 * var(--nav-tabs-border-width)); |
| @include border-top-radius(0); |
| } |
| } |
| |
| // |
| // Pills |
| // |
| |
| .nav-pills { |
| @include tokens($nav-pills-tokens); |
| |
| display: inline-flex; |
| padding: var(--nav-pills-padding); |
| background-color: var(--nav-pills-bg); |
| @include border-radius(var(--nav-pills-border-radius)); |
| |
| .nav-link { |
| @include border-radius(var(--nav-pills-link-border-radius)); |
| } |
| |
| .nav-link.active, |
| .show > .nav-link { |
| color: var(--theme-contrast, var(--nav-pills-link-active-color)); |
| @include gradient-bg(var(--theme-bg, var(--nav-pills-link-active-bg))); |
| } |
| } |
| |
| .nav-pills-vertical { |
| flex-direction: column; |
| align-items: stretch; |
| |
| .nav-item, |
| .nav-link { |
| width: 100%; |
| } |
| } |
| |
| // |
| // Underline |
| // |
| |
| .nav-underline { |
| // scss-docs-start nav-underline-css-vars |
| @include tokens($nav-underline-tokens); |
| // scss-docs-end nav-underline-css-vars |
| |
| .nav-link { |
| padding-inline: 0; |
| border: 0; |
| border-block-end: var(--nav-underline-border-width) solid transparent; |
| @include border-radius(0); |
| |
| &:hover, |
| &:focus { |
| border-block-end-color: currentcolor; |
| } |
| } |
| |
| .nav-link.active, |
| .show > .nav-link { |
| font-weight: var(--font-weight-bold); |
| color: var(--nav-underline-link-active-color); |
| border-block-end-color: currentcolor; |
| } |
| } |
| |
| // |
| // Justified variants |
| // |
| |
| .nav-fill { |
| > .nav-link, |
| .nav-item { |
| flex: 1 1 auto; |
| text-align: center; |
| } |
| } |
| |
| .nav-justified { |
| > .nav-link, |
| .nav-item { |
| flex-grow: 1; |
| flex-basis: 0; |
| text-align: center; |
| } |
| } |
| |
| .nav-fill, |
| .nav-justified { |
| .nav-item .nav-link { |
| width: 100%; // Make sure button will grow |
| } |
| } |
| |
| // Tabbable tabs |
| // |
| // Hide tabbable panes to start, show them when `.active` |
| |
| .tab-content { |
| > .tab-pane { |
| @include tokens($tab-pane-tokens); |
| |
| display: none; |
| |
| // The pane fades in on its own, from the starting state below. Only the |
| // entry animates: two in-flow panes cannot cross-fade without stacking, |
| // so the outgoing pane leaves at once. Add .tab-pane-instant to skip it. |
| &:not(.tab-pane-instant) { |
| @include transition-props( |
| var(--tab-pane-transition-property), |
| var(--tab-pane-transition-duration), |
| var(--tab-pane-transition-timing) |
| ); |
| } |
| } |
| > .active { |
| display: block; |
| } |
| } |
| |
| // The pane is not rendered before .active lands, so the fade-in needs an |
| // explicit starting state. |
| @starting-style { |
| .tab-content > .tab-pane:not(.tab-pane-instant).active { |
| opacity: 0; |
| } |
| } |
| } |