@use "config" as *; @use "colors" as *; @use "variables" as *; @use "functions" as *; @use "vendor/rfs" as *; @use "mixins/border-radius" as *; @use "mixins/box-shadow" as *; @use "mixins/transition" as *; @use "mixins/gradients" as *; // Full color palette // Semantic colors assigned globally, used for components // Semantic theme helpers—`.bs-theme-{color}`??? // `${component}-variants()` maps for alternate component appearances // - not to be confused with theme colors // Bootstrap 6 uses Sass modules, and is still heavily Sass-based, but is now more than ever CSS-first. // This includes leading with CSS variables whenever possible, including using them within Sass maps, loops, mixins, and variables. // Button variants // // Easily pump out default styles, as well as :hover, :focus, :active, // and disabled options for all buttons $button-variants: ( "solid": ( "base": ( "bg": "bg", "color": "contrast", "border-color": "bg" ), "hover": ( "bg": "bg", "border-color": "bg", "color": "contrast" ), "active": ( "bg": "bg", "border-color": "bg", "color": "contrast" ) ), "outline": ( "base": ( "bg": "transparent", "color": "text", "border-color": "border" ), "hover": ( "bg": "bg", "color": "contrast", "border-color": "bg" ), "active": ( "bg": "bg", "color": "contrast", "border-color": "bg" ) ), "subtle": ( "base": ( "bg": "bg-subtle", "color": "text", "border-color": "transparent" ), "hover": ( "bg": "bg-subtle", "color": "text" ), "active": ( "bg": "bg-subtle", "color": "text" ) ), "text": ( "base": ( "color": "text", "bg": "transparent", "border-color": "transparent" ), "hover": ( "color": "text", "bg": "bg-subtle" ), "active": ( "color": "text", "bg": "bg-subtle" ) ) ) !default; // Helper function to get nested map values using dot notation @function get-nested-value($map, $keys) { $value: $map; @each $key in $keys { @if type-of($value) == "map" { $value: map-get($value, $key); } @else { @return null; } } @return $value; } // Helper function to split dot notation string into list @function split-keys($key) { $keys: (); $parts: str-slice($key, 1); @each $part in $parts { $keys: append($keys, $part); } @return $keys; } // Main button style generator mixin @mixin button-variant($color, $variant) { $variant-styles: map-get($button-variants, $variant); @if $variant-styles { // Base properties @each $property, $value in map-get($variant-styles, "base") { @if $value == "transparent" { --#{$prefix}btn-#{$property}: transparent; // #{$property}: transparent; } @else { --#{$prefix}btn-#{$property}: var(--#{$prefix}#{$color}-#{$value}); // #{$property}: var(#{$prefix}#{$color}-#{$value}); } } // Hover state &:hover { @each $property, $value in map-get($variant-styles, "hover") { @if $value == "transparent" { --#{$prefix}btn-hover-#{$property}: transparent; // #{$property}: transparent; } @else if $value == "bg-subtle" { --#{$prefix}btn-hover-#{$property}: var(--#{$prefix}#{$color}-#{$value}); // #{$property}: var(#{$prefix}#{$color}-#{$value}); } @else { --#{$prefix}btn-hover-#{$property}: oklch(from var(--#{$prefix}#{$color}-#{$value}) calc(l * .95) calc(c * 1.1) h); // #{$property}: oklch( // from var(#{$prefix}#{$color}-#{$value}) calc(l * .98) calc(c * 1.1) h // ); } } } // Active state &:active, &.active { @each $property, $value in map-get($variant-styles, "active") { @if $value == "transparent" { --#{$prefix}btn-active-#{$property}: transparent; // #{$property}: transparent; } @else if $value == "bg-subtle" { --#{$prefix}btn-active-#{$property}: var(--#{$prefix}#{$color}-#{$value}); // #{$property}: var(#{$prefix}#{$color}-#{$value}); } @else { --#{$prefix}btn-active-#{$property}: oklch(from var(--#{$prefix}#{$color}-#{$value}) calc(l * .9) calc(c * 1.15) h); // #{$property}: oklch( // from var(#{$prefix}#{$color}-#{$value}) calc(l * .9) calc(c * 1.15) h // ); } } } } } // Generate all button variants @each $color, $_ in $theme-colors { @each $variant, $_ in $button-variants { .btn-#{$color}-#{$variant} { @include button-variant($color, $variant); } } } @layer components { // scss-docs-start btn-variant-mixin // @mixin button-variant( // $background, // $border, // $color: color-contrast($background), // $hover-background: if($color == $color-contrast-light, shade-color($background, $btn-hover-bg-shade-amount), tint-color($background, $btn-hover-bg-tint-amount)), // $hover-border: if($color == $color-contrast-light, shade-color($border, $btn-hover-border-shade-amount), tint-color($border, $btn-hover-border-tint-amount)), // $hover-color: color-contrast($hover-background), // $active-background: if($color == $color-contrast-light, shade-color($background, $btn-active-bg-shade-amount), tint-color($background, $btn-active-bg-tint-amount)), // $active-border: if($color == $color-contrast-light, shade-color($border, $btn-active-border-shade-amount), tint-color($border, $btn-active-border-tint-amount)), // $active-color: color-contrast($active-background), // $disabled-background: $background, // $disabled-border: $border, // $disabled-color: color-contrast($disabled-background) // ) { // --#{$prefix}btn-color: #{$color}; // --#{$prefix}btn-bg: #{$background}; // --#{$prefix}btn-border-color: #{$border}; // --#{$prefix}btn-hover-color: #{$hover-color}; // --#{$prefix}btn-hover-bg: #{$hover-background}; // --#{$prefix}btn-hover-border-color: #{$hover-border}; // --#{$prefix}btn-focus-shadow-rgb: #{to-rgb(mix($color, $border, 15%))}; // --#{$prefix}btn-active-color: #{$active-color}; // --#{$prefix}btn-active-bg: #{$active-background}; // --#{$prefix}btn-active-border-color: #{$active-border}; // --#{$prefix}btn-active-shadow: #{$btn-active-box-shadow}; // --#{$prefix}btn-disabled-color: #{$disabled-color}; // --#{$prefix}btn-disabled-bg: #{$disabled-background}; // --#{$prefix}btn-disabled-border-color: #{$disabled-border}; // } // scss-docs-end btn-variant-mixin // scss-docs-start btn-outline-variant-mixin // @mixin button-outline-variant( // $color, // $color-hover: color-contrast($color), // $active-background: $color, // $active-border: $color, // $active-color: color-contrast($active-background) // ) { // --#{$prefix}btn-color: #{$color}; // --#{$prefix}btn-border-color: #{$color}; // --#{$prefix}btn-hover-color: #{$color-hover}; // --#{$prefix}btn-hover-bg: #{$active-background}; // --#{$prefix}btn-hover-border-color: #{$active-border}; // --#{$prefix}btn-focus-shadow-rgb: #{to-rgb($color)}; // --#{$prefix}btn-active-color: #{$active-color}; // --#{$prefix}btn-active-bg: #{$active-background}; // --#{$prefix}btn-active-border-color: #{$active-border}; // --#{$prefix}btn-active-shadow: #{$btn-active-box-shadow}; // --#{$prefix}btn-disabled-color: #{$color}; // --#{$prefix}btn-disabled-bg: transparent; // --#{$prefix}btn-disabled-border-color: #{$color}; // --#{$prefix}gradient: none; // } // scss-docs-end btn-outline-variant-mixin // scss-docs-start btn-size-mixin @mixin button-size($padding-y, $padding-x, $font-size, $border-radius) { --#{$prefix}btn-padding-y: #{$padding-y}; --#{$prefix}btn-padding-x: #{$padding-x}; @include rfs($font-size, --#{$prefix}btn-font-size); --#{$prefix}btn-border-radius: #{$border-radius}; } // scss-docs-end btn-size-mixin // // Base styles // .btn { // scss-docs-start btn-css-vars --#{$prefix}btn-padding-x: #{$btn-padding-x}; --#{$prefix}btn-padding-y: #{$btn-padding-y}; --#{$prefix}btn-font-family: #{$btn-font-family}; @include rfs($btn-font-size, --#{$prefix}btn-font-size); --#{$prefix}btn-font-weight: #{$btn-font-weight}; --#{$prefix}btn-line-height: #{$btn-line-height}; --#{$prefix}btn-color: #{$btn-color}; --#{$prefix}btn-bg: transparent; --#{$prefix}btn-border-width: #{$btn-border-width}; --#{$prefix}btn-border-color: transparent; --#{$prefix}btn-border-radius: #{$btn-border-radius}; --#{$prefix}btn-hover-color: var(--#{$prefix}btn-color); --#{$prefix}btn-hover-border-color: transparent; --#{$prefix}btn-box-shadow: #{$btn-box-shadow}; --#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity}; --#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5); // scss-docs-end btn-css-vars display: inline-block; padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x); font-family: var(--#{$prefix}btn-font-family); @include font-size(var(--#{$prefix}btn-font-size)); font-weight: var(--#{$prefix}btn-font-weight); line-height: var(--#{$prefix}btn-line-height); color: var(--#{$prefix}btn-color); text-align: center; text-decoration: if($link-decoration == none, null, none); white-space: $btn-white-space; vertical-align: middle; cursor: if($enable-button-pointers, pointer, null); user-select: none; border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color); @include border-radius(var(--#{$prefix}btn-border-radius)); @include gradient-bg(var(--#{$prefix}btn-bg)); @include box-shadow(var(--#{$prefix}btn-box-shadow)); @include transition($btn-transition); &:hover { color: var(--#{$prefix}btn-hover-color); text-decoration: if($link-hover-decoration == underline, none, null); background-color: var(--#{$prefix}btn-hover-bg); border-color: var(--#{$prefix}btn-hover-border-color); } .btn-check + &:hover { // override for the checkbox/radio buttons color: var(--#{$prefix}btn-color); background-color: var(--#{$prefix}btn-bg); border-color: var(--#{$prefix}btn-border-color); } &:focus-visible { color: var(--#{$prefix}btn-hover-color); @include gradient-bg(var(--#{$prefix}btn-hover-bg)); border-color: var(--#{$prefix}btn-hover-border-color); outline: 0; // Avoid using mixin so we can pass custom focus shadow properly @if $enable-shadows { box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow); } @else { box-shadow: var(--#{$prefix}btn-focus-box-shadow); } } .btn-check:focus-visible + & { border-color: var(--#{$prefix}btn-hover-border-color); outline: 0; // Avoid using mixin so we can pass custom focus shadow properly @if $enable-shadows { box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow); } @else { box-shadow: var(--#{$prefix}btn-focus-box-shadow); } } .btn-check:checked + &, :not(.btn-check) + &:active, &:first-child:active, &.active, &.show { color: var(--#{$prefix}btn-active-color); background-color: var(--#{$prefix}btn-active-bg); // Remove CSS gradients if they"re enabled background-image: if($enable-gradients, none, null); border-color: var(--#{$prefix}btn-active-border-color); @include box-shadow(var(--#{$prefix}btn-active-shadow)); &:focus-visible { // Avoid using mixin so we can pass custom focus shadow properly @if $enable-shadows { box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow); } @else { box-shadow: var(--#{$prefix}btn-focus-box-shadow); } } } .btn-check:checked:focus-visible + & { // Avoid using mixin so we can pass custom focus shadow properly @if $enable-shadows { box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow); } @else { box-shadow: var(--#{$prefix}btn-focus-box-shadow); } } &:disabled, &.disabled, fieldset:disabled & { color: var(--#{$prefix}btn-disabled-color); pointer-events: none; background-color: var(--#{$prefix}btn-disabled-bg); background-image: if($enable-gradients, none, null); border-color: var(--#{$prefix}btn-disabled-border-color); opacity: var(--#{$prefix}btn-disabled-opacity); @include box-shadow(none); } } // // Alternate buttons // // scss-docs-start btn-variant-loops // @each $color, $value in $theme-colors { // .btn-#{$color} { // @if $color == "light" { // @include button-variant( // $value, // $value, // $hover-background: shade-color($value, $btn-hover-bg-shade-amount), // $hover-border: shade-color($value, $btn-hover-border-shade-amount), // $active-background: shade-color($value, $btn-active-bg-shade-amount), // $active-border: shade-color($value, $btn-active-border-shade-amount) // ); // } @else if $color == "dark" { // @include button-variant( // $value, // $value, // $hover-background: tint-color($value, $btn-hover-bg-tint-amount), // $hover-border: tint-color($value, $btn-hover-border-tint-amount), // $active-background: tint-color($value, $btn-active-bg-tint-amount), // $active-border: tint-color($value, $btn-active-border-tint-amount) // ); // } @else { // @include button-variant($value, $value); // } // } // } // @each $color, $value in $theme-colors { // .btn-outline-#{$color} { // @include button-outline-variant($value); // } // } // scss-docs-end btn-variant-loops // // Link buttons // // Make a button look and behave like a link .btn-link { --#{$prefix}btn-font-weight: #{$font-weight-normal}; --#{$prefix}btn-color: #{$btn-link-color}; --#{$prefix}btn-bg: transparent; --#{$prefix}btn-border-color: transparent; --#{$prefix}btn-hover-color: #{$btn-link-hover-color}; --#{$prefix}btn-hover-border-color: transparent; --#{$prefix}btn-active-color: #{$btn-link-hover-color}; --#{$prefix}btn-active-border-color: transparent; --#{$prefix}btn-disabled-color: #{$btn-link-disabled-color}; --#{$prefix}btn-disabled-border-color: transparent; --#{$prefix}btn-box-shadow: 0 0 0 #000; // Can"t use `none` as keyword negates all values when used with multiple shadows // mdo-do // --#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb}; text-decoration: $link-decoration; @if $enable-gradients { background-image: none; } &:hover, &:focus-visible { text-decoration: $link-hover-decoration; } &:focus-visible { color: var(--#{$prefix}btn-color); } &:hover { color: var(--#{$prefix}btn-hover-color); } // No need for an active state here } // // Button Sizes // .btn-lg { @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg); } .btn-sm { @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm); } }