WIP: Añade componente para la gestión de menús #8

Draft
manuelcillero wants to merge 54 commits from add-menu-component into main
109 changed files with 536 additions and 173 deletions
Showing only changes of commit 674d92033c - Show all commits

View file

@ -4,12 +4,9 @@ use std::env;
use std::path::Path;
fn main() -> std::io::Result<()> {
StaticFilesBundle::from_scss(
"./static/bootstrap-5.3.3/bootstrap.scss",
"bootstrap.min.css",
)
.with_name("bootsier")
.build()?;
StaticFilesBundle::from_scss("./static/bootstrap/bootstrap.scss", "bootstrap.min.css")
.with_name("bootsier")
.build()?;
StaticFilesBundle::from_dir("./static/js", Some(bootstrap_js_files))
.with_name("bootsier_js")
.build()

View file

@ -86,7 +86,7 @@ use pagetop::prelude::*;
pub type BootsierRegion = ThemeRegion;
// Versión de la librería Bootstrap.
const BOOTSTRAP_VERSION: &str = "5.3.3";
const BOOTSTRAP_VERSION: &str = "5.3.8";
/// Tema basado en [Bootstrap](https://getbootstrap.com/) para los componentes base de PageTop.
///

View file

@ -1,18 +0,0 @@
@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;
}
}
}

View file

@ -134,17 +134,12 @@
&:last-child { border-bottom: 0; }
// stylelint-disable selector-max-class
> .accordion-header .accordion-button {
&,
&.collapsed {
@include border-radius(0);
}
}
// stylelint-enable selector-max-class
> .accordion-collapse {
> .accordion-collapse,
> .accordion-header .accordion-button,
> .accordion-header .accordion-button.collapsed {
@include border-radius(0);
}
// stylelint-enable selector-max-class
}
}

View file

@ -39,7 +39,7 @@
// Prevent double borders when buttons are next to each other
> :not(.btn-check:first-child) + .btn,
> .btn-group:not(:first-child) {
margin-left: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list
margin-left: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list
}
// Reset rounded corners
@ -126,7 +126,7 @@
> .btn:not(:first-child),
> .btn-group:not(:first-child) {
margin-top: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list
margin-top: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list
}
// Reset rounded corners
@ -135,7 +135,12 @@
@include border-bottom-radius(0);
}
> .btn ~ .btn,
// The top radius should be 0 if the button is:
// - the "third or more" child
// - the second child and the previous element isn't `.btn-check` (making it the first child visually)
// - part of a btn-group which isn't the first child
> .btn:nth-child(n + 3),
> :not(.btn-check) + .btn,
> .btn-group:not(:first-child) > .btn {
@include border-top-radius(0);
}

View file

@ -193,8 +193,7 @@
// The child selector allows nested `.card` within `.card-group`
// to display properly.
> .card {
// Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4
flex: 1 0 0%;
flex: 1 0 0;
margin-bottom: 0;
+ .card {
@ -207,13 +206,13 @@
&:not(:last-child) {
@include border-end-radius(0);
.card-img-top,
.card-header {
> .card-img-top,
> .card-header {
// stylelint-disable-next-line property-disallowed-list
border-top-right-radius: 0;
}
.card-img-bottom,
.card-footer {
> .card-img-bottom,
> .card-footer {
// stylelint-disable-next-line property-disallowed-list
border-bottom-right-radius: 0;
}
@ -222,13 +221,13 @@
&:not(:first-child) {
@include border-start-radius(0);
.card-img-top,
.card-header {
> .card-img-top,
> .card-header {
// stylelint-disable-next-line property-disallowed-list
border-top-left-radius: 0;
}
.card-img-bottom,
.card-footer {
> .card-img-bottom,
> .card-footer {
// stylelint-disable-next-line property-disallowed-list
border-bottom-left-radius: 0;
}

View file

@ -99,6 +99,7 @@
color: $carousel-control-color;
text-align: center;
background: none;
filter: var(--#{$prefix}carousel-control-icon-filter);
border: 0;
opacity: $carousel-control-opacity;
@include transition($carousel-control-transition);
@ -168,7 +169,7 @@
margin-left: $carousel-indicator-spacer;
text-indent: -999px;
cursor: pointer;
background-color: $carousel-indicator-active-bg;
background-color: var(--#{$prefix}carousel-indicator-active-bg);
background-clip: padding-box;
border: 0;
// Use transparent borders to increase the hit area by 10px on top and bottom.
@ -195,42 +196,31 @@
left: (100% - $carousel-caption-width) * .5;
padding-top: $carousel-caption-padding-y;
padding-bottom: $carousel-caption-padding-y;
color: $carousel-caption-color;
color: var(--#{$prefix}carousel-caption-color);
text-align: center;
}
// Dark mode carousel
@mixin carousel-dark() {
.carousel-control-prev-icon,
.carousel-control-next-icon {
filter: $carousel-dark-control-icon-filter;
}
.carousel-indicators [data-bs-target] {
background-color: $carousel-dark-indicator-active-bg;
}
.carousel-caption {
color: $carousel-dark-caption-color;
}
--#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark};
--#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark};
--#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark};
}
.carousel-dark {
@include carousel-dark();
}
:root,
[data-bs-theme="light"] {
--#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg};
--#{$prefix}carousel-caption-color: #{$carousel-caption-color};
--#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter};
}
@if $enable-dark-mode {
@include color-mode(dark) {
@if $color-mode-type == "media-query" {
.carousel {
@include carousel-dark();
}
} @else {
.carousel,
&.carousel {
@include carousel-dark();
}
}
@include color-mode(dark, true) {
@include carousel-dark();
}
}

View file

@ -12,7 +12,6 @@
--#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow};
--#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity};
--#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity};
--#{$prefix}btn-close-white-filter: #{$btn-close-white-filter};
// scss-docs-end close-css-vars
box-sizing: content-box;
@ -21,6 +20,7 @@
padding: $btn-close-padding-y $btn-close-padding-x;
color: var(--#{$prefix}btn-close-color);
background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements
filter: var(--#{$prefix}btn-close-filter);
border: 0; // for button elements
@include border-radius();
opacity: var(--#{$prefix}btn-close-opacity);
@ -47,17 +47,20 @@
}
@mixin btn-close-white() {
filter: var(--#{$prefix}btn-close-white-filter);
--#{$prefix}btn-close-filter: #{$btn-close-filter-dark};
}
.btn-close-white {
@include btn-close-white();
}
:root,
[data-bs-theme="light"] {
--#{$prefix}btn-close-filter: #{$btn-close-filter};
}
@if $enable-dark-mode {
@include color-mode(dark) {
.btn-close {
@include btn-close-white();
}
@include color-mode(dark, true) {
@include btn-close-white();
}
}

View file

@ -157,7 +157,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
@each $color in $foregrounds {
$contrast-ratio: contrast-ratio($background, $color);
@if $contrast-ratio > $min-contrast-ratio {
@if $contrast-ratio >= $min-contrast-ratio {
@return $color;
} @else if $contrast-ratio > $max-ratio {
$max-ratio: $contrast-ratio;
@ -177,7 +177,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
@return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
}
// Return WCAG2.1 relative luminance
// Return WCAG2.2 relative luminance
// See https://www.w3.org/TR/WCAG/#dfn-relative-luminance
// See https://www.w3.org/TR/WCAG/#dfn-contrast-ratio
@function luminance($color) {

View file

@ -43,31 +43,6 @@
}
}
// Interactive list items
//
// Use anchor or button elements instead of `li`s or `div`s to create interactive
// list items. Includes an extra `.active` modifier class for selected items.
.list-group-item-action {
width: 100%; // For `<button>`s (anchors become 100% by default though)
color: var(--#{$prefix}list-group-action-color);
text-align: inherit; // For `<button>`s (anchors inherit)
// Hover state
&:hover,
&:focus {
z-index: 1; // Place hover/focus items above their siblings for proper border styling
color: var(--#{$prefix}list-group-action-hover-color);
text-decoration: none;
background-color: var(--#{$prefix}list-group-action-hover-bg);
}
&:active {
color: var(--#{$prefix}list-group-action-active-color);
background-color: var(--#{$prefix}list-group-action-active-bg);
}
}
// Individual list items
//
// Use on `li`s or `div`s within the `.list-group` parent.
@ -115,6 +90,33 @@
}
}
// Interactive list items
//
// Use anchor or button elements instead of `li`s or `div`s to create interactive
// list items. Includes an extra `.active` modifier class for selected items.
.list-group-item-action {
width: 100%; // For `<button>`s (anchors become 100% by default though)
color: var(--#{$prefix}list-group-action-color);
text-align: inherit; // For `<button>`s (anchors inherit)
&:not(.active) {
// Hover state
&:hover,
&:focus {
z-index: 1; // Place hover/focus items above their siblings for proper border styling
color: var(--#{$prefix}list-group-action-hover-color);
text-decoration: none;
background-color: var(--#{$prefix}list-group-action-hover-bg);
}
&:active {
color: var(--#{$prefix}list-group-action-active-color);
background-color: var(--#{$prefix}list-group-action-active-bg);
}
}
}
// Horizontal
//
// Change the layout of list group items from vertical (default) to horizontal.

View file

@ -59,8 +59,8 @@
// When fading in the modal, animate it to slide down
.modal.fade & {
@include transition($modal-transition);
transform: $modal-fade-transform;
@include transition($modal-transition);
}
.modal.show & {
transform: $modal-show-transform;
@ -132,7 +132,11 @@
.btn-close {
padding: calc(var(--#{$prefix}modal-header-padding-y) * .5) calc(var(--#{$prefix}modal-header-padding-x) * .5);
margin: calc(-.5 * var(--#{$prefix}modal-header-padding-y)) calc(-.5 * var(--#{$prefix}modal-header-padding-x)) calc(-.5 * var(--#{$prefix}modal-header-padding-y)) auto;
// Split properties to avoid invalid calc() function if value is 0
margin-top: calc(-.5 * var(--#{$prefix}modal-header-padding-y));
margin-right: calc(-.5 * var(--#{$prefix}modal-header-padding-x));
margin-bottom: calc(-.5 * var(--#{$prefix}modal-header-padding-y));
margin-left: auto;
}
}

View file

@ -169,8 +169,8 @@
.nav-justified {
> .nav-link,
.nav-item {
flex-basis: 0;
flex-grow: 1;
flex-basis: 0;
text-align: center;
}
}

View file

@ -139,8 +139,8 @@
// the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
// on the `.navbar` parent.
.navbar-collapse {
flex-basis: 100%;
flex-grow: 1;
flex-basis: 100%;
// For always expanded or extra full navbars, ensure content aligns itself
// properly vertically. Can be easily overridden with flex utilities.
align-items: center;

View file

@ -127,7 +127,11 @@
.btn-close {
padding: calc(var(--#{$prefix}offcanvas-padding-y) * .5) calc(var(--#{$prefix}offcanvas-padding-x) * .5);
margin: calc(-.5 * var(--#{$prefix}offcanvas-padding-y)) calc(-.5 * var(--#{$prefix}offcanvas-padding-x)) calc(-.5 * var(--#{$prefix}offcanvas-padding-y)) auto;
// Split properties to avoid invalid calc() function if value is 0
margin-top: calc(-.5 * var(--#{$prefix}offcanvas-padding-y));
margin-right: calc(-.5 * var(--#{$prefix}offcanvas-padding-x));
margin-bottom: calc(-.5 * var(--#{$prefix}offcanvas-padding-y));
margin-left: auto;
}
}

View file

@ -75,7 +75,7 @@
margin-left: $pagination-margin-start;
}
@if $pagination-margin-start == calc(#{$pagination-border-width} * -1) {
@if $pagination-margin-start == calc(-1 * #{$pagination-border-width}) {
&:first-child {
.page-link {
@include border-start-radius(var(--#{$prefix}pagination-border-radius));

View file

@ -3,7 +3,7 @@
// scss-docs-start progress-keyframes
@if $enable-transitions {
@keyframes progress-bar-stripes {
0% { background-position-x: $progress-height; }
0% { background-position-x: var(--#{$prefix}progress-height); }
}
}
// scss-docs-end progress-keyframes

View file

@ -499,9 +499,9 @@ legend {
width: 100%;
padding: 0;
margin-bottom: $legend-margin-bottom;
@include font-size($legend-font-size);
font-weight: $legend-font-weight;
line-height: inherit;
@include font-size($legend-font-size);
+ * {
clear: left; // 2
@ -534,6 +534,12 @@ legend {
[type="search"] {
-webkit-appearance: textfield; // 1
outline-offset: -2px; // 2
// 3. Better affordance and consistent appearance for search cancel button
&::-webkit-search-cancel-button {
cursor: pointer;
filter: grayscale(1);
}
}
// 1. A few input types should stay LTR

View file

@ -5,6 +5,7 @@
.spinner-grow,
.spinner-border {
display: inline-block;
flex-shrink: 0;
width: var(--#{$prefix}spinner-width);
height: var(--#{$prefix}spinner-height);
vertical-align: var(--#{$prefix}spinner-vertical-align);

View file

@ -34,11 +34,11 @@
// Type display classes
@each $display, $font-size in $display-font-sizes {
.display-#{$display} {
@include font-size($font-size);
font-family: $display-font-family;
font-style: $display-font-style;
font-weight: $display-font-weight;
line-height: $display-line-height;
@include font-size($font-size);
}
}

View file

@ -82,6 +82,21 @@ $form-invalid-border-color-dark: $red-300 !default;
$accordion-icon-color-dark: $primary-text-emphasis-dark !default;
$accordion-icon-active-color-dark: $primary-text-emphasis-dark !default;
$accordion-button-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>") !default;
$accordion-button-active-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>") !default;
$accordion-button-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/></svg>") !default;
$accordion-button-active-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/></svg>") !default;
// scss-docs-end sass-dark-mode-vars
//
// Carousel
//
$carousel-indicator-active-bg-dark: $carousel-dark-indicator-active-bg !default;
$carousel-caption-color-dark: $carousel-dark-caption-color !default;
$carousel-control-icon-filter-dark: $carousel-dark-control-icon-filter !default;
//
// Close button
//
$btn-close-filter-dark: $btn-close-white-filter !default;

View file

@ -67,8 +67,8 @@ $colors: (
) !default;
// scss-docs-end colors-map
// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.
// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.2 are 3, 4.5 and 7.
// See https://www.w3.org/TR/WCAG/#contrast-minimum
$min-contrast-ratio: 4.5 !default;
// Customize the light and dark text colors for use in our color contrast function.
@ -1091,7 +1091,7 @@ $form-feedback-valid-color: $success !default;
$form-feedback-invalid-color: $danger !default;
$form-feedback-icon-valid-color: $form-feedback-valid-color !default;
$form-feedback-icon-valid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'><path fill='#{$form-feedback-icon-valid-color}' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/></svg>") !default;
$form-feedback-icon-valid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'><path fill='#{$form-feedback-icon-valid-color}' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/></svg>") !default;
$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;
$form-feedback-icon-invalid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='#{$form-feedback-icon-invalid-color}'><circle cx='6' cy='6' r='4.5'/><path stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/><circle cx='6' cy='8.2' r='.6' fill='#{$form-feedback-icon-invalid-color}' stroke='none'/></svg>") !default;
// scss-docs-end form-feedback-variables
@ -1302,7 +1302,7 @@ $pagination-color: var(--#{$prefix}link-color) !default;
$pagination-bg: var(--#{$prefix}body-bg) !default;
$pagination-border-radius: var(--#{$prefix}border-radius) !default;
$pagination-border-width: var(--#{$prefix}border-width) !default;
$pagination-margin-start: calc(#{$pagination-border-width} * -1) !default; // stylelint-disable-line function-disallowed-list
$pagination-margin-start: calc(-1 * #{$pagination-border-width}) !default; // stylelint-disable-line function-disallowed-list
$pagination-border-color: var(--#{$prefix}border-color) !default;
$pagination-focus-color: var(--#{$prefix}link-hover-color) !default;
@ -1394,8 +1394,8 @@ $accordion-icon-active-color: $primary-text-emphasis !default;
$accordion-icon-transition: transform .2s ease-in-out !default;
$accordion-icon-transform: rotate(-180deg) !default;
$accordion-button-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-color}' stroke-linecap='round' stroke-linejoin='round'><path d='M2 5L8 11L14 5'/></svg>") !default;
$accordion-button-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-active-color}' stroke-linecap='round' stroke-linejoin='round'><path d='M2 5L8 11L14 5'/></svg>") !default;
$accordion-button-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-color}' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>") !default;
$accordion-button-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-active-color}' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>") !default;
// scss-docs-end accordion-variables
// Tooltips
@ -1507,7 +1507,7 @@ $modal-dialog-margin-y-sm-up: 1.75rem !default;
$modal-title-line-height: $line-height-base !default;
$modal-content-color: null !default;
$modal-content-color: var(--#{$prefix}body-color) !default;
$modal-content-bg: var(--#{$prefix}body-bg) !default;
$modal-content-border-color: var(--#{$prefix}border-color-translucent) !default;
$modal-content-border-width: var(--#{$prefix}border-width) !default;
@ -1652,6 +1652,7 @@ $carousel-control-width: 15% !default;
$carousel-control-opacity: .5 !default;
$carousel-control-hover-opacity: .9 !default;
$carousel-control-transition: opacity .15s ease !default;
$carousel-control-icon-filter: null !default;
$carousel-indicator-width: 30px !default;
$carousel-indicator-height: 3px !default;
@ -1669,17 +1670,17 @@ $carousel-caption-spacer: 1.25rem !default;
$carousel-control-icon-width: 2rem !default;
$carousel-control-prev-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$carousel-control-color}'><path d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/></svg>") !default;
$carousel-control-next-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$carousel-control-color}'><path d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/></svg>") !default;
$carousel-control-prev-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$carousel-control-color}'><path d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/></svg>") !default;
$carousel-control-next-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$carousel-control-color}'><path d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/></svg>") !default;
$carousel-transition-duration: .6s !default;
$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)
// scss-docs-end carousel-variables
// scss-docs-start carousel-dark-variables
$carousel-dark-indicator-active-bg: $black !default;
$carousel-dark-caption-color: $black !default;
$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default;
$carousel-dark-indicator-active-bg: $black !default; // Deprecated in v5.3.4
$carousel-dark-caption-color: $black !default; // Deprecated in v5.3.4
$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default; // Deprecated in v5.3.4
// scss-docs-end carousel-dark-variables
@ -1706,13 +1707,14 @@ $btn-close-height: $btn-close-width !default;
$btn-close-padding-x: .25em !default;
$btn-close-padding-y: $btn-close-padding-x !default;
$btn-close-color: $black !default;
$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/></svg>") !default;
$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/></svg>") !default;
$btn-close-focus-shadow: $focus-ring-box-shadow !default;
$btn-close-opacity: .5 !default;
$btn-close-hover-opacity: .75 !default;
$btn-close-focus-opacity: 1 !default;
$btn-close-disabled-opacity: .25 !default;
$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default;
$btn-close-filter: null !default;
$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default; // Deprecated in v5.3.4
// scss-docs-end close-variables

View file

@ -14,9 +14,11 @@
top: 0;
left: 0;
z-index: 2;
max-width: 100%;
height: 100%; // allow textareas
padding: $form-floating-padding-y $form-floating-padding-x;
overflow: hidden;
color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
text-align: start;
text-overflow: ellipsis;
white-space: nowrap;
@ -49,6 +51,7 @@
> .form-select {
padding-top: $form-floating-input-padding-t;
padding-bottom: $form-floating-input-padding-b;
padding-left: $form-floating-padding-x;
}
> .form-control:focus,
@ -56,27 +59,30 @@
> .form-control-plaintext,
> .form-select {
~ label {
color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
transform: $form-floating-label-transform;
&::after {
position: absolute;
inset: $form-floating-padding-y ($form-floating-padding-x * .5);
z-index: -1;
height: $form-floating-label-height;
content: "";
background-color: $input-bg;
@include border-radius($input-border-radius);
}
}
}
// Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
> .form-control:-webkit-autofill {
~ label {
color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
transform: $form-floating-label-transform;
}
}
> textarea:focus,
> textarea:not(:placeholder-shown) {
~ label::after {
position: absolute;
inset: $form-floating-padding-y ($form-floating-padding-x * .5);
z-index: -1;
height: $form-floating-label-height;
content: "";
background-color: $input-bg;
@include border-radius($input-border-radius);
}
}
> textarea:disabled ~ label::after {
background-color: $input-disabled-bg;
}
> .form-control-plaintext {
~ label {
@ -87,9 +93,5 @@
> :disabled ~ label,
> .form-control:disabled ~ label { // Required for `.form-control`s because of specificity
color: $form-floating-label-disabled-color;
&::after {
background-color: $input-disabled-bg;
}
}
}

View file

@ -121,7 +121,7 @@
}
> :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
margin-left: calc(#{$input-border-width} * -1); // stylelint-disable-line function-disallowed-list
margin-left: calc(-1 * #{$input-border-width}); // stylelint-disable-line function-disallowed-list
@include border-start-radius(0);
}

View file

@ -1,7 +1,7 @@
@mixin bsBanner($file) {
/*!
* Bootstrap #{$file} v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Bootstrap #{$file} v5.3.8 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
}

View file

@ -0,0 +1,24 @@
@mixin box-shadow($shadow...) {
@if $enable-shadows {
$result: ();
$has-single-value: false;
$single-value: null;
@each $value in $shadow {
@if $value != null {
@if $value == none or $value == initial or $value == inherit or $value == unset {
$has-single-value: true;
$single-value: $value;
} @else {
$result: append($result, $value, "comma");
}
}
}
@if $has-single-value {
box-shadow: $single-value;
} @else if (length($result) > 0) {
box-shadow: $result;
}
}
}

View file

@ -72,7 +72,7 @@
@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
flex: 1 0 0;
}
.row-cols#{$infix}-auto > * {

View file

@ -19,12 +19,17 @@
&:not(caption) {
position: absolute !important;
}
// Fix to prevent overflowing children to become focusable
* {
overflow: hidden !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
// Useful for "Skip to main content" links; see https://www.w3.org/WAI/WCAG22/Techniques/general/G1.html
@mixin visually-hidden-focusable() {
&:not(:focus):not(:focus-within) {

View file

@ -0,0 +1,188 @@
@import "../../functions";
@import "../../variables";
@import "../../mixins";
// Store original value
$original-enable-shadows: $enable-shadows;
// Enable shadows for all tests
$enable-shadows: true !global;
@include describe("box-shadow mixin") {
@include it("handles single none value") {
@include assert() {
@include output() {
.test {
@include box-shadow(none);
}
}
@include expect() {
.test {
box-shadow: none;
}
}
}
}
@include it("handles multiple none values by consolidating them") {
@include assert() {
@include output() {
.test {
@include box-shadow(none, none, none);
}
}
@include expect() {
.test {
box-shadow: none;
}
}
}
}
@include it("handles other single-value keywords (initial, inherit, unset)") {
@include assert() {
@include output() {
.test-initial {
@include box-shadow(initial);
}
.test-inherit {
@include box-shadow(inherit);
}
.test-unset {
@include box-shadow(unset);
}
}
@include expect() {
.test-initial {
box-shadow: initial;
}
.test-inherit {
box-shadow: inherit;
}
.test-unset {
box-shadow: unset;
}
}
}
}
@include it("handles multiple single-value keywords by using the last one") {
@include assert() {
@include output() {
.test {
@include box-shadow(initial, inherit, unset);
}
}
@include expect() {
.test {
box-shadow: unset;
}
}
}
}
@include it("handles regular box-shadow values") {
@include assert() {
@include output() {
.test {
@include box-shadow(0 0 10px rgba(0, 0, 0, .5));
}
}
@include expect() {
.test {
box-shadow: 0 0 10px rgba(0, 0, 0, .5);
}
}
}
}
@include it("handles multiple regular box-shadow values") {
@include assert() {
@include output() {
.test {
@include box-shadow(0 0 10px rgba(0, 0, 0, .5), 0 0 20px rgba(0, 0, 0, .3));
}
}
@include expect() {
.test {
box-shadow: 0 0 10px rgba(0, 0, 0, .5), 0 0 20px rgba(0, 0, 0, .3);
}
}
}
}
@include it("handles null values by ignoring them") {
@include assert() {
@include output() {
.test {
@include box-shadow(null, 0 0 10px rgba(0, 0, 0, .5), null);
}
}
@include expect() {
.test {
box-shadow: 0 0 10px rgba(0, 0, 0, .5);
}
}
}
}
@include it("handles mixed values with keywords and regular shadows") {
@include assert() {
@include output() {
.test {
@include box-shadow(none, 0 0 10px rgba(0, 0, 0, .5));
}
}
@include expect() {
.test {
box-shadow: none;
}
}
}
}
@include it("handles empty input") {
@include assert() {
@include output() {
.test {
@include box-shadow();
}
}
@include expect() {
.test { // stylelint-disable-line block-no-empty
}
}
}
}
@include it("respects $enable-shadows variable") {
$enable-shadows: false !global;
@include assert() {
@include output() {
.test {
@include box-shadow(0 0 10px rgba(0, 0, 0, .5));
}
}
@include expect() {
.test { // stylelint-disable-line block-no-empty
}
}
}
$enable-shadows: true !global;
}
}
// Restore original value
$enable-shadows: $original-enable-shadows !global;

View file

@ -0,0 +1,139 @@
@import "../../functions";
@import "../../variables";
@import "../../variables-dark";
@import "../../maps";
@import "../../mixins";
@include describe("color-contrast function") {
@include it("should return a color when contrast ratio equals minimum requirement (WCAG 2.1 compliance)") {
// Test case: Background color that produces contrast ratio close to 4.5:1
// This tests the WCAG 2.1 requirement that contrast should be "at least 4.5:1" (>= 4.5)
// rather than "strictly greater than 4.5:1" (> 4.5)
// #777777 produces 4.4776:1 contrast ratio with white text
// Since this is below the 4.5:1 threshold, it should return the highest available contrast color
$test-background: #777;
$result: color-contrast($test-background);
@include assert-equal($result, $black, "Should return black (highest available contrast) for background with 4.4776:1 contrast ratio (below threshold)");
}
@include it("should return a color when contrast ratio is above minimum requirement") {
// Test case: Background color that produces contrast ratio above 4.5:1
// #767676 produces 4.5415:1 contrast ratio with white text
$test-background: #767676;
$result: color-contrast($test-background);
@include assert-equal($result, $white, "Should return white for background with 4.5415:1 contrast ratio (above threshold)");
}
@include it("should return a color when contrast ratio is below minimum requirement") {
// Test case: Background color that produces contrast ratio below 4.5:1
// #787878 produces 4.4155:1 contrast ratio with white text
$test-background: #787878;
$result: color-contrast($test-background);
// Should return the color with the highest available contrast ratio
@include assert-equal($result, $black, "Should return black (highest available contrast) for background with 4.4155:1 contrast ratio (below threshold)");
}
@include it("should handle edge case with very light background") {
// Test case: Very light background that should return dark text
$test-background: #f8f9fa; // Very light gray
$result: color-contrast($test-background);
@include assert-equal($result, $color-contrast-dark, "Should return dark text for very light background");
}
@include it("should handle edge case with very dark background") {
// Test case: Very dark background that should return light text
$test-background: #212529; // Very dark gray
$result: color-contrast($test-background);
@include assert-equal($result, $color-contrast-light, "Should return light text for very dark background");
}
@include it("should work with custom minimum contrast ratio") {
// Test case: Using a custom minimum contrast ratio
$test-background: #666;
$result: color-contrast($test-background, $color-contrast-dark, $color-contrast-light, 3);
@include assert-equal($result, $white, "Should return white when using custom minimum contrast ratio of 3.0");
}
@include it("should test contrast ratio calculation accuracy") {
// Test case: Verify that contrast-ratio function works correctly
$background: #767676;
$foreground: $white;
$ratio: contrast-ratio($background, $foreground);
// Bootstrap's implementation calculates this as ~4.5415, not exactly 4.5, due to its luminance math.
// We use 4.54 as the threshold for this test to match the actual implementation.
@include assert-true($ratio >= 4.54 and $ratio <= 4.55, "Contrast ratio should be approximately 4.54:1 (Bootstrap's math)");
}
@include it("should test luminance calculation") {
// Test case: Verify luminance function works correctly
$white-luminance: luminance($white);
$black-luminance: luminance($black);
@include assert-equal($white-luminance, 1, "White should have luminance of 1");
@include assert-equal($black-luminance, 0, "Black should have luminance of 0");
}
@include it("should handle rgba colors correctly") {
// Test case: Test with rgba colors
$test-background: rgba(118, 118, 118, 1); // Same as #767676
$result: color-contrast($test-background);
@include assert-equal($result, $white, "Should handle rgba colors correctly");
}
@include it("should test the WCAG 2.1 boundary condition with color below threshold") {
// Test case: Background color that produces contrast ratio below 4.5:1
// #787878 produces 4.4155:1 contrast ratio with white
$test-background: #787878; // Produces 4.4155:1 contrast ratio
$contrast-ratio: contrast-ratio($test-background, $white);
// Verify the contrast ratio is below 4.5:1
@include assert-true($contrast-ratio < 4.5, "Contrast ratio should be below 4.5:1 threshold");
// The color-contrast function should return the color with highest available contrast
$result: color-contrast($test-background);
@include assert-equal($result, $black, "color-contrast should return black (highest available contrast) for below-threshold ratio");
}
@include it("should test the WCAG 2.1 boundary condition with color at threshold") {
// Test case: Background color that produces contrast ratio close to 4.5:1
// #777777 produces 4.4776:1 contrast ratio with white
$test-background: #777; // Produces 4.4776:1 contrast ratio
$contrast-ratio: contrast-ratio($test-background, $white);
// Verify the contrast ratio is below 4.5:1 threshold
@include assert-true($contrast-ratio < 4.5, "Contrast ratio is below threshold, function should handle gracefully");
}
@include it("should demonstrate the difference between > and >= operators") {
// Test case: Demonstrates the difference between > and >= operators
// Uses #767676 with a custom minimum contrast ratio that matches its actual ratio (4.5415)
// With > 4.5415: should return black (fallback to highest available)
// With >= 4.5415: should return white (meets threshold)
$test-background: #767676; // Produces 4.5415:1 contrast ratio
$actual-ratio: contrast-ratio($test-background, $white);
// Test with a custom minimum that matches the actual ratio
$result: color-contrast($test-background, $color-contrast-dark, $color-contrast-light, $actual-ratio);
// Should return white when using >= implementation
@include assert-equal($result, $white, "color-contrast should return white when using exact ratio as threshold (>= implementation)");
}
@include it("should test additional working colors above threshold") {
// Test case: Background color that produces contrast ratio well above 4.5:1
// #757575 produces 4.6047:1 contrast ratio with white text
$test-background: #757575; // Produces 4.6047:1 contrast ratio
$result: color-contrast($test-background);
@include assert-equal($result, $white, "Should return white for background with 4.6047:1 contrast ratio (well above threshold)");
}
}

Some files were not shown because too many files have changed in this diff Show more