blob: 236bd53a0503b01eb1e20fe3390fe680c71db31c [file] [edit]
@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;
}
}
}