diff options
Diffstat (limited to '')
26 files changed, 1046 insertions, 0 deletions
diff --git a/scss/mixins/_alert.scss b/scss/mixins/_alert.scss new file mode 100644 index 0000000..231f068 --- /dev/null +++ b/scss/mixins/_alert.scss @@ -0,0 +1,15 @@ +// scss-docs-start alert-variant-mixin +@mixin alert-variant($background, $border, $color) { + --#{$prefix}alert-color: #{$color}; + --#{$prefix}alert-bg: #{$background}; + --#{$prefix}alert-border-color: #{$border}; + + @if $enable-gradients { + background-image: var(--#{$prefix}gradient); + } + + .alert-link { + color: shade-color($color, 20%); + } +} +// scss-docs-end alert-variant-mixin diff --git a/scss/mixins/_backdrop.scss b/scss/mixins/_backdrop.scss new file mode 100644 index 0000000..9705ae9 --- /dev/null +++ b/scss/mixins/_backdrop.scss @@ -0,0 +1,14 @@ +// Shared between modals and offcanvases +@mixin overlay-backdrop($zindex, $backdrop-bg, $backdrop-opacity) { + position: fixed; + top: 0; + left: 0; + z-index: $zindex; + width: 100vw; + height: 100vh; + background-color: $backdrop-bg; + + // Fade for backdrop + &.fade { opacity: 0; } + &.show { opacity: $backdrop-opacity; } +} diff --git a/scss/mixins/_banner.scss b/scss/mixins/_banner.scss new file mode 100644 index 0000000..97ec9d1 --- /dev/null +++ b/scss/mixins/_banner.scss @@ -0,0 +1,9 @@ +@mixin bsBanner($file) { + /*! + * Bootstrap #{$file} v5.2.3 (https://getbootstrap.com/) + * Copyright 2011-2022 The Bootstrap Authors + * Copyright 2011-2022 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +} + diff --git a/scss/mixins/_border-radius.scss b/scss/mixins/_border-radius.scss new file mode 100644 index 0000000..616decb --- /dev/null +++ b/scss/mixins/_border-radius.scss @@ -0,0 +1,78 @@ +// stylelint-disable property-disallowed-list +// Single side border-radius + +// Helper function to replace negative values with 0 +@function valid-radius($radius) { + $return: (); + @each $value in $radius { + @if type-of($value) == number { + $return: append($return, max($value, 0)); + } @else { + $return: append($return, $value); + } + } + @return $return; +} + +// scss-docs-start border-radius-mixins +@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) { + @if $enable-rounded { + border-radius: valid-radius($radius); + } + @else if $fallback-border-radius != false { + border-radius: $fallback-border-radius; + } +} + +@mixin border-top-radius($radius: $border-radius) { + @if $enable-rounded { + border-top-left-radius: valid-radius($radius); + border-top-right-radius: valid-radius($radius); + } +} + +@mixin border-end-radius($radius: $border-radius) { + @if $enable-rounded { + border-top-right-radius: valid-radius($radius); + border-bottom-right-radius: valid-radius($radius); + } +} + +@mixin border-bottom-radius($radius: $border-radius) { + @if $enable-rounded { + border-bottom-right-radius: valid-radius($radius); + border-bottom-left-radius: valid-radius($radius); + } +} + +@mixin border-start-radius($radius: $border-radius) { + @if $enable-rounded { + border-top-left-radius: valid-radius($radius); + border-bottom-left-radius: valid-radius($radius); + } +} + +@mixin border-top-start-radius($radius: $border-radius) { + @if $enable-rounded { + border-top-left-radius: valid-radius($radius); + } +} + +@mixin border-top-end-radius($radius: $border-radius) { + @if $enable-rounded { + border-top-right-radius: valid-radius($radius); + } +} + +@mixin border-bottom-end-radius($radius: $border-radius) { + @if $enable-rounded { + border-bottom-right-radius: valid-radius($radius); + } +} + +@mixin border-bottom-start-radius($radius: $border-radius) { + @if $enable-rounded { + border-bottom-left-radius: valid-radius($radius); + } +} +// scss-docs-end border-radius-mixins diff --git a/scss/mixins/_box-shadow.scss b/scss/mixins/_box-shadow.scss new file mode 100644 index 0000000..4172541 --- /dev/null +++ b/scss/mixins/_box-shadow.scss @@ -0,0 +1,18 @@ +@mixin box-shadow($shadow...) { + @if $enable-shadows { + $result: (); + + @each $value in $shadow { + @if $value != null { + $result: append($result, $value, "comma"); + } + @if $value == none and length($shadow) > 1 { + @warn "The keyword 'none' must be used as a single argument."; + } + } + + @if (length($result) > 0) { + box-shadow: $result; + } + } +} diff --git a/scss/mixins/_breakpoints.scss b/scss/mixins/_breakpoints.scss new file mode 100644 index 0000000..286be89 --- /dev/null +++ b/scss/mixins/_breakpoints.scss @@ -0,0 +1,127 @@ +// Breakpoint viewport sizes and media queries. +// +// Breakpoints are defined as a map of (name: minimum width), order from small to large: +// +// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px) +// +// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default. + +// Name of the next breakpoint, or null for the last breakpoint. +// +// >> breakpoint-next(sm) +// md +// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) +// md +// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl)) +// md +@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) { + $n: index($breakpoint-names, $name); + @if not $n { + @error "breakpoint `#{$name}` not found in `#{$breakpoints}`"; + } + @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); +} + +// Minimum breakpoint width. Null for the smallest (first) breakpoint. +// +// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) +// 576px +@function breakpoint-min($name, $breakpoints: $grid-breakpoints) { + $min: map-get($breakpoints, $name); + @return if($min != 0, $min, null); +} + +// Maximum breakpoint width. +// The maximum value is reduced by 0.02px to work around the limitations of +// `min-` and `max-` prefixes and viewports with fractional widths. +// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max +// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. +// See https://bugs.webkit.org/show_bug.cgi?id=178261 +// +// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) +// 767.98px +@function breakpoint-max($name, $breakpoints: $grid-breakpoints) { + $max: map-get($breakpoints, $name); + @return if($max and $max > 0, $max - .02, null); +} + +// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front. +// Useful for making responsive utilities. +// +// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) +// "" (Returns a blank string) +// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) +// "-sm" +@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) { + @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); +} + +// Media of at least the minimum breakpoint width. No query for the smallest breakpoint. +// Makes the @content apply to the given breakpoint and wider. +@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) { + $min: breakpoint-min($name, $breakpoints); + @if $min { + @media (min-width: $min) { + @content; + } + } @else { + @content; + } +} + +// Media of at most the maximum breakpoint width. No query for the largest breakpoint. +// Makes the @content apply to the given breakpoint and narrower. +@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) { + $max: breakpoint-max($name, $breakpoints); + @if $max { + @media (max-width: $max) { + @content; + } + } @else { + @content; + } +} + +// Media that spans multiple breakpoint widths. +// Makes the @content apply between the min and max breakpoints +@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) { + $min: breakpoint-min($lower, $breakpoints); + $max: breakpoint-max($upper, $breakpoints); + + @if $min != null and $max != null { + @media (min-width: $min) and (max-width: $max) { + @content; + } + } @else if $max == null { + @include media-breakpoint-up($lower, $breakpoints) { + @content; + } + } @else if $min == null { + @include media-breakpoint-down($upper, $breakpoints) { + @content; + } + } +} + +// Media between the breakpoint's minimum and maximum widths. +// No minimum for the smallest breakpoint, and no maximum for the largest one. +// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower. +@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) { + $min: breakpoint-min($name, $breakpoints); + $next: breakpoint-next($name, $breakpoints); + $max: breakpoint-max($next, $breakpoints); + + @if $min != null and $max != null { + @media (min-width: $min) and (max-width: $max) { + @content; + } + } @else if $max == null { + @include media-breakpoint-up($name, $breakpoints) { + @content; + } + } @else if $min == null { + @include media-breakpoint-down($next, $breakpoints) { + @content; + } + } +} diff --git a/scss/mixins/_buttons.scss b/scss/mixins/_buttons.scss new file mode 100644 index 0000000..cf087fd --- /dev/null +++ b/scss/mixins/_buttons.scss @@ -0,0 +1,70 @@ +// Button variants +// +// Easily pump out default styles, as well as :hover, :focus, :active, +// and disabled options for all buttons + +// 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 diff --git a/scss/mixins/_caret.scss b/scss/mixins/_caret.scss new file mode 100644 index 0000000..4b0f036 --- /dev/null +++ b/scss/mixins/_caret.scss @@ -0,0 +1,64 @@ +// scss-docs-start caret-mixins +@mixin caret-down { + border-top: $caret-width solid; + border-right: $caret-width solid transparent; + border-bottom: 0; + border-left: $caret-width solid transparent; +} + +@mixin caret-up { + border-top: 0; + border-right: $caret-width solid transparent; + border-bottom: $caret-width solid; + border-left: $caret-width solid transparent; +} + +@mixin caret-end { + border-top: $caret-width solid transparent; + border-right: 0; + border-bottom: $caret-width solid transparent; + border-left: $caret-width solid; +} + +@mixin caret-start { + border-top: $caret-width solid transparent; + border-right: $caret-width solid; + border-bottom: $caret-width solid transparent; +} + +@mixin caret($direction: down) { + @if $enable-caret { + &::after { + display: inline-block; + margin-left: $caret-spacing; + vertical-align: $caret-vertical-align; + content: ""; + @if $direction == down { + @include caret-down(); + } @else if $direction == up { + @include caret-up(); + } @else if $direction == end { + @include caret-end(); + } + } + + @if $direction == start { + &::after { + display: none; + } + + &::before { + display: inline-block; + margin-right: $caret-spacing; + vertical-align: $caret-vertical-align; + content: ""; + @include caret-start(); + } + } + + &:empty::after { + margin-left: 0; + } + } +} +// scss-docs-end caret-mixins diff --git a/scss/mixins/_clearfix.scss b/scss/mixins/_clearfix.scss new file mode 100644 index 0000000..ffc62bb --- /dev/null +++ b/scss/mixins/_clearfix.scss @@ -0,0 +1,9 @@ +// scss-docs-start clearfix +@mixin clearfix() { + &::after { + display: block; + clear: both; + content: ""; + } +} +// scss-docs-end clearfix diff --git a/scss/mixins/_color-scheme.scss b/scss/mixins/_color-scheme.scss new file mode 100644 index 0000000..90497aa --- /dev/null +++ b/scss/mixins/_color-scheme.scss @@ -0,0 +1,7 @@ +// scss-docs-start mixin-color-scheme +@mixin color-scheme($name) { + @media (prefers-color-scheme: #{$name}) { + @content; + } +} +// scss-docs-end mixin-color-scheme diff --git a/scss/mixins/_container.scss b/scss/mixins/_container.scss new file mode 100644 index 0000000..b9f3351 --- /dev/null +++ b/scss/mixins/_container.scss @@ -0,0 +1,11 @@ +// Container mixins + +@mixin make-container($gutter: $container-padding-x) { + --#{$prefix}gutter-x: #{$gutter}; + --#{$prefix}gutter-y: 0; + width: 100%; + padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list + padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list + margin-right: auto; + margin-left: auto; +} diff --git a/scss/mixins/_deprecate.scss b/scss/mixins/_deprecate.scss new file mode 100644 index 0000000..df070bc --- /dev/null +++ b/scss/mixins/_deprecate.scss @@ -0,0 +1,10 @@ +// Deprecate mixin +// +// This mixin can be used to deprecate mixins or functions. +// `$enable-deprecation-messages` is a global variable, `$ignore-warning` is a variable that can be passed to +// some deprecated mixins to suppress the warning (for example if the mixin is still be used in the current version of Bootstrap) +@mixin deprecate($name, $deprecate-version, $remove-version, $ignore-warning: false) { + @if ($enable-deprecation-messages != false and $ignore-warning != true) { + @warn "#{$name} has been deprecated as of #{$deprecate-version}. It will be removed entirely in #{$remove-version}."; + } +} diff --git a/scss/mixins/_forms.scss b/scss/mixins/_forms.scss new file mode 100644 index 0000000..2a853a7 --- /dev/null +++ b/scss/mixins/_forms.scss @@ -0,0 +1,152 @@ +// This mixin uses an `if()` technique to be compatible with Dart Sass +// See https://github.com/sass/sass/issues/1873#issuecomment-152293725 for more details + +// scss-docs-start form-validation-mixins +@mixin form-validation-state-selector($state) { + @if ($state == "valid" or $state == "invalid") { + .was-validated #{if(&, "&", "")}:#{$state}, + #{if(&, "&", "")}.is-#{$state} { + @content; + } + } @else { + #{if(&, "&", "")}.is-#{$state} { + @content; + } + } +} + +@mixin form-validation-state( + $state, + $color, + $icon, + $tooltip-color: color-contrast($color), + $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity), + $focus-box-shadow: 0 0 $input-btn-focus-blur $input-focus-width rgba($color, $input-btn-focus-color-opacity) +) { + .#{$state}-feedback { + display: none; + width: 100%; + margin-top: $form-feedback-margin-top; + @include font-size($form-feedback-font-size); + font-style: $form-feedback-font-style; + color: $color; + } + + .#{$state}-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; // Contain to parent when possible + padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x; + margin-top: .1rem; + @include font-size($form-feedback-tooltip-font-size); + line-height: $form-feedback-tooltip-line-height; + color: $tooltip-color; + background-color: $tooltip-bg-color; + @include border-radius($form-feedback-tooltip-border-radius); + } + + @include form-validation-state-selector($state) { + ~ .#{$state}-feedback, + ~ .#{$state}-tooltip { + display: block; + } + } + + .form-control { + @include form-validation-state-selector($state) { + border-color: $color; + + @if $enable-validation-icons { + padding-right: $input-height-inner; + background-image: escape-svg($icon); + background-repeat: no-repeat; + background-position: right $input-height-inner-quarter center; + background-size: $input-height-inner-half $input-height-inner-half; + } + + &:focus { + border-color: $color; + box-shadow: $focus-box-shadow; + } + } + } + + // stylelint-disable-next-line selector-no-qualifying-type + textarea.form-control { + @include form-validation-state-selector($state) { + @if $enable-validation-icons { + padding-right: $input-height-inner; + background-position: top $input-height-inner-quarter right $input-height-inner-quarter; + } + } + } + + .form-select { + @include form-validation-state-selector($state) { + border-color: $color; + + @if $enable-validation-icons { + &:not([multiple]):not([size]), + &:not([multiple])[size="1"] { + padding-right: $form-select-feedback-icon-padding-end; + background-image: escape-svg($form-select-indicator), escape-svg($icon); + background-position: $form-select-bg-position, $form-select-feedback-icon-position; + background-size: $form-select-bg-size, $form-select-feedback-icon-size; + } + } + + &:focus { + border-color: $color; + box-shadow: $focus-box-shadow; + } + } + } + + .form-control-color { + @include form-validation-state-selector($state) { + @if $enable-validation-icons { + width: add($form-color-width, $input-height-inner); + } + } + } + + .form-check-input { + @include form-validation-state-selector($state) { + border-color: $color; + + &:checked { + background-color: $color; + } + + &:focus { + box-shadow: $focus-box-shadow; + } + + ~ .form-check-label { + color: $color; + } + } + } + .form-check-inline .form-check-input { + ~ .#{$state}-feedback { + margin-left: .5em; + } + } + + .input-group { + > .form-control:not(:focus), + > .form-select:not(:focus), + > .form-floating:not(:focus-within) { + @include form-validation-state-selector($state) { + @if $state == "valid" { + z-index: 3; + } @else if $state == "invalid" { + z-index: 4; + } + } + } + } +} +// scss-docs-end form-validation-mixins diff --git a/scss/mixins/_gradients.scss b/scss/mixins/_gradients.scss new file mode 100644 index 0000000..608e18d --- /dev/null +++ b/scss/mixins/_gradients.scss @@ -0,0 +1,47 @@ +// Gradients + +// scss-docs-start gradient-bg-mixin +@mixin gradient-bg($color: null) { + background-color: $color; + + @if $enable-gradients { + background-image: var(--#{$prefix}gradient); + } +} +// scss-docs-end gradient-bg-mixin + +// scss-docs-start gradient-mixins +// Horizontal gradient, from left to right +// +// Creates two color stops, start and end, by specifying a color and position for each color stop. +@mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) { + background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent); +} + +// Vertical gradient, from top to bottom +// +// Creates two color stops, start and end, by specifying a color and position for each color stop. +@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: null, $end-percent: null) { + background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent); +} + +@mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) { + background-image: linear-gradient($deg, $start-color, $end-color); +} + +@mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) { + background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color); +} + +@mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) { + background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color); +} + +@mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) { + background-image: radial-gradient(circle, $inner-color, $outer-color); +} + +@mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) { + background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent); +} +// scss-docs-end gradient-mixins diff --git a/scss/mixins/_grid.scss b/scss/mixins/_grid.scss new file mode 100644 index 0000000..38e2239 --- /dev/null +++ b/scss/mixins/_grid.scss @@ -0,0 +1,151 @@ +// Grid system +// +// Generate semantic grid columns with these mixins. + +@mixin make-row($gutter: $grid-gutter-width) { + --#{$prefix}gutter-x: #{$gutter}; + --#{$prefix}gutter-y: 0; + display: flex; + flex-wrap: wrap; + // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed + margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list + margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list + margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list +} + +@mixin make-col-ready() { + // Add box sizing if only the grid is loaded + box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null); + // Prevent columns from becoming too narrow when at smaller grid tiers by + // always setting `width: 100%;`. This works because we set the width + // later on to override this initial width. + flex-shrink: 0; + width: 100%; + max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid + padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list + padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list + margin-top: var(--#{$prefix}gutter-y); +} + +@mixin make-col($size: false, $columns: $grid-columns) { + @if $size { + flex: 0 0 auto; + width: percentage(divide($size, $columns)); + + } @else { + flex: 1 1 0; + max-width: 100%; + } +} + +@mixin make-col-auto() { + flex: 0 0 auto; + width: auto; +} + +@mixin make-col-offset($size, $columns: $grid-columns) { + $num: divide($size, $columns); + margin-left: if($num == 0, 0, percentage($num)); +} + +// Row columns +// +// Specify on a parent element(e.g., .row) to force immediate children into NN +// number of columns. Supports wrapping to new lines, but does not do a Masonry +// style grid. +@mixin row-cols($count) { + > * { + flex: 0 0 auto; + width: divide(100%, $count); + } +} + +// Framework grid generation +// +// Used only by Bootstrap to generate the correct number of grid classes given +// any value of `$grid-columns`. + +@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) { + @each $breakpoint in map-keys($breakpoints) { + $infix: breakpoint-infix($breakpoint, $breakpoints); + + @include media-breakpoint-up($breakpoint, $breakpoints) { + // Provide basic `.col-{bp}` classes for equal-width flexbox columns + .col#{$infix} { + flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4 + } + + .row-cols#{$infix}-auto > * { + @include make-col-auto(); + } + + @if $grid-row-columns > 0 { + @for $i from 1 through $grid-row-columns { + .row-cols#{$infix}-#{$i} { + @include row-cols($i); + } + } + } + + .col#{$infix}-auto { + @include make-col-auto(); + } + + @if $columns > 0 { + @for $i from 1 through $columns { + .col#{$infix}-#{$i} { + @include make-col($i, $columns); + } + } + + // `$columns - 1` because offsetting by the width of an entire row isn't possible + @for $i from 0 through ($columns - 1) { + @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0 + .offset#{$infix}-#{$i} { + @include make-col-offset($i, $columns); + } + } + } + } + + // Gutters + // + // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns. + @each $key, $value in $gutters { + .g#{$infix}-#{$key}, + .gx#{$infix}-#{$key} { + --#{$prefix}gutter-x: #{$value}; + } + + .g#{$infix}-#{$key}, + .gy#{$infix}-#{$key} { + --#{$prefix}gutter-y: #{$value}; + } + } + } + } +} + +@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) { + @each $breakpoint in map-keys($breakpoints) { + $infix: breakpoint-infix($breakpoint, $breakpoints); + + @include media-breakpoint-up($breakpoint, $breakpoints) { + @if $columns > 0 { + @for $i from 1 through $columns { + .g-col#{$infix}-#{$i} { + grid-column: auto / span $i; + } + } + + // Start with `1` because `0` is and invalid value. + // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible. + @for $i from 1 through ($columns - 1) { + .g-start#{$infix}-#{$i} { + grid-column-start: $i; + } + } + } + } + } +} diff --git a/scss/mixins/_image.scss b/scss/mixins/_image.scss new file mode 100644 index 0000000..e1df779 --- /dev/null +++ b/scss/mixins/_image.scss @@ -0,0 +1,16 @@ +// Image Mixins +// - Responsive image +// - Retina image + + +// Responsive image +// +// Keep images from scaling beyond the width of their parents. + +@mixin img-fluid { + // Part 1: Set a maximum relative to the parent + max-width: 100%; + // Part 2: Override the height to auto, otherwise images will be stretched + // when setting a width and height attribute on the img element. + height: auto; +} diff --git a/scss/mixins/_list-group.scss b/scss/mixins/_list-group.scss new file mode 100644 index 0000000..e55415f --- /dev/null +++ b/scss/mixins/_list-group.scss @@ -0,0 +1,24 @@ +// List Groups + +// scss-docs-start list-group-mixin +@mixin list-group-item-variant($state, $background, $color) { + .list-group-item-#{$state} { + color: $color; + background-color: $background; + + &.list-group-item-action { + &:hover, + &:focus { + color: $color; + background-color: shade-color($background, 10%); + } + + &.active { + color: $white; + background-color: $color; + border-color: $color; + } + } + } +} +// scss-docs-end list-group-mixin diff --git a/scss/mixins/_lists.scss b/scss/mixins/_lists.scss new file mode 100644 index 0000000..2518562 --- /dev/null +++ b/scss/mixins/_lists.scss @@ -0,0 +1,7 @@ +// Lists + +// Unstyled keeps list items block level, just removes default browser padding and list-style +@mixin list-unstyled { + padding-left: 0; + list-style: none; +} diff --git a/scss/mixins/_pagination.scss b/scss/mixins/_pagination.scss new file mode 100644 index 0000000..0d65796 --- /dev/null +++ b/scss/mixins/_pagination.scss @@ -0,0 +1,10 @@ +// Pagination + +// scss-docs-start pagination-mixin +@mixin pagination-size($padding-y, $padding-x, $font-size, $border-radius) { + --#{$prefix}pagination-padding-x: #{$padding-x}; + --#{$prefix}pagination-padding-y: #{$padding-y}; + @include rfs($font-size, --#{$prefix}pagination-font-size); + --#{$prefix}pagination-border-radius: #{$border-radius}; +} +// scss-docs-end pagination-mixin diff --git a/scss/mixins/_reset-text.scss b/scss/mixins/_reset-text.scss new file mode 100644 index 0000000..f5bd1af --- /dev/null +++ b/scss/mixins/_reset-text.scss @@ -0,0 +1,17 @@ +@mixin reset-text { + font-family: $font-family-base; + // We deliberately do NOT reset font-size or overflow-wrap / word-wrap. + font-style: normal; + font-weight: $font-weight-normal; + line-height: $line-height-base; + text-align: left; // Fallback for where `start` is not supported + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; +} diff --git a/scss/mixins/_resize.scss b/scss/mixins/_resize.scss new file mode 100644 index 0000000..66f233a --- /dev/null +++ b/scss/mixins/_resize.scss @@ -0,0 +1,6 @@ +// Resize anything + +@mixin resizable($direction) { + overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` + resize: $direction; // Options: horizontal, vertical, both +} diff --git a/scss/mixins/_table-variants.scss b/scss/mixins/_table-variants.scss new file mode 100644 index 0000000..5fe1b9b --- /dev/null +++ b/scss/mixins/_table-variants.scss @@ -0,0 +1,24 @@ +// scss-docs-start table-variant +@mixin table-variant($state, $background) { + .table-#{$state} { + $color: color-contrast(opaque($body-bg, $background)); + $hover-bg: mix($color, $background, percentage($table-hover-bg-factor)); + $striped-bg: mix($color, $background, percentage($table-striped-bg-factor)); + $active-bg: mix($color, $background, percentage($table-active-bg-factor)); + $table-border-color: mix($color, $background, percentage($table-border-factor)); + + --#{$prefix}table-color: #{$color}; + --#{$prefix}table-bg: #{$background}; + --#{$prefix}table-border-color: #{$table-border-color}; + --#{$prefix}table-striped-bg: #{$striped-bg}; + --#{$prefix}table-striped-color: #{color-contrast($striped-bg)}; + --#{$prefix}table-active-bg: #{$active-bg}; + --#{$prefix}table-active-color: #{color-contrast($active-bg)}; + --#{$prefix}table-hover-bg: #{$hover-bg}; + --#{$prefix}table-hover-color: #{color-contrast($hover-bg)}; + + color: var(--#{$prefix}table-color); + border-color: var(--#{$prefix}table-border-color); + } +} +// scss-docs-end table-variant diff --git a/scss/mixins/_text-truncate.scss b/scss/mixins/_text-truncate.scss new file mode 100644 index 0000000..3504bb1 --- /dev/null +++ b/scss/mixins/_text-truncate.scss @@ -0,0 +1,8 @@ +// Text truncate +// Requires inline-block or block for proper styling + +@mixin text-truncate() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/scss/mixins/_transition.scss b/scss/mixins/_transition.scss new file mode 100644 index 0000000..d437f6d --- /dev/null +++ b/scss/mixins/_transition.scss @@ -0,0 +1,26 @@ +// stylelint-disable property-disallowed-list +@mixin transition($transition...) { + @if length($transition) == 0 { + $transition: $transition-base; + } + + @if length($transition) > 1 { + @each $value in $transition { + @if $value == null or $value == none { + @warn "The keyword 'none' or 'null' must be used as a single argument."; + } + } + } + + @if $enable-transitions { + @if nth($transition, 1) != null { + transition: $transition; + } + + @if $enable-reduced-motion and nth($transition, 1) != null and nth($transition, 1) != none { + @media (prefers-reduced-motion: reduce) { + transition: none; + } + } + } +} diff --git a/scss/mixins/_utilities.scss b/scss/mixins/_utilities.scss new file mode 100644 index 0000000..59a2374 --- /dev/null +++ b/scss/mixins/_utilities.scss @@ -0,0 +1,97 @@ +// Utility generator +// Used to generate utilities & print utilities +@mixin generate-utility($utility, $infix, $is-rfs-media-query: false) { + $values: map-get($utility, values); + + // If the values are a list or string, convert it into a map + @if type-of($values) == "string" or type-of(nth($values, 1)) != "list" { + $values: zip($values, $values); + } + + @each $key, $value in $values { + $properties: map-get($utility, property); + + // Multiple properties are possible, for example with vertical or horizontal margins or paddings + @if type-of($properties) == "string" { + $properties: append((), $properties); + } + + // Use custom class if present + $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1)); + $property-class: if($property-class == null, "", $property-class); + + // Use custom CSS variable name if present, otherwise default to `class` + $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class)); + + // State params to generate pseudo-classes + $state: if(map-has-key($utility, state), map-get($utility, state), ()); + + $infix: if($property-class == "" and str-slice($infix, 1, 1) == "-", str-slice($infix, 2), $infix); + + // Don't prefix if value key is null (e.g. with shadow class) + $property-class-modifier: if($key, if($property-class == "" and $infix == "", "", "-") + $key, ""); + + @if map-get($utility, rfs) { + // Inside the media query + @if $is-rfs-media-query { + $val: rfs-value($value); + + // Do not render anything if fluid and non fluid values are the same + $value: if($val == rfs-fluid-value($value), null, $val); + } + @else { + $value: rfs-fluid-value($value); + } + } + + $is-css-var: map-get($utility, css-var); + $is-local-vars: map-get($utility, local-vars); + $is-rtl: map-get($utility, rtl); + + @if $value != null { + @if $is-rtl == false { + /* rtl:begin:remove */ + } + + @if $is-css-var { + .#{$property-class + $infix + $property-class-modifier} { + --#{$prefix}#{$css-variable-name}: #{$value}; + } + + @each $pseudo in $state { + .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} { + --#{$prefix}#{$css-variable-name}: #{$value}; + } + } + } @else { + .#{$property-class + $infix + $property-class-modifier} { + @each $property in $properties { + @if $is-local-vars { + @each $local-var, $variable in $is-local-vars { + --#{$prefix}#{$local-var}: #{$variable}; + } + } + #{$property}: $value if($enable-important-utilities, !important, null); + } + } + + @each $pseudo in $state { + .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} { + @each $property in $properties { + @if $is-local-vars { + @each $local-var, $variable in $is-local-vars { + --#{$prefix}#{$local-var}: #{$variable}; + } + } + #{$property}: $value if($enable-important-utilities, !important, null); + } + } + } + } + + @if $is-rtl == false { + /* rtl:end:remove */ + } + } + } +} diff --git a/scss/mixins/_visually-hidden.scss b/scss/mixins/_visually-hidden.scss new file mode 100644 index 0000000..4fc7f49 --- /dev/null +++ b/scss/mixins/_visually-hidden.scss @@ -0,0 +1,29 @@ +// stylelint-disable declaration-no-important + +// Hide content visually while keeping it accessible to assistive technologies +// +// See: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ +// See: https://kittygiraudel.com/2016/10/13/css-hide-and-seek/ + +@mixin visually-hidden() { + position: absolute !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; // Fix for https://github.com/twbs/bootstrap/issues/25686 + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; +} + +// Use to only display content when it's focused, or one of its child elements is focused +// (i.e. when focus is within the element/container that the class was applied to) +// +// Useful for "Skip to main content" links; see https://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 + +@mixin visually-hidden-focusable() { + &:not(:focus):not(:focus-within) { + @include visually-hidden(); + } +} |