diff --git a/core/scss/_bootstrap-config.scss b/core/scss/_bootstrap-config.scss
index 8e93b399a..ec03a0be8 100644
--- a/core/scss/_bootstrap-config.scss
+++ b/core/scss/_bootstrap-config.scss
@@ -1,5 +1,7 @@
// Config
@import "bootstrap/scss/functions";
+@import "bootstrap-functions";
+
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
diff --git a/core/scss/_bootstrap-functions.scss b/core/scss/_bootstrap-functions.scss
index b1264410a..3ff11830d 100644
--- a/core/scss/_bootstrap-functions.scss
+++ b/core/scss/_bootstrap-functions.scss
@@ -1,6 +1,258 @@
-
@use "sass:color";
+@use "sass:map";
+@use "sass:math";
+@use "sass:meta";
+@use "sass:string";
+@use "sass:list";
+/**
+ * Converts a color value to RGB format
+ *
+ * @param {Color} $value - The color value to convert
+ * @return {List} - RGB values as a list (red, green, blue)
+ *
+ * @example
+ * to-rgb(#ff0000) // Returns: (255, 0, 0)
+ */
@function to-rgb($value) {
- @return color.red($value), color.green($value), color.blue($value);
+ @return color.channel($value, "red", $space: rgb), color.channel($value, "green", $space: rgb), color.channel($value, "blue", $space: rgb);
+}
+
+/**
+ * Creates an opaque color by mixing a foreground color with a background color
+ *
+ * @param {Color} $background - The background color
+ * @param {Color} $foreground - The foreground color to make opaque
+ * @return {Color} - The resulting opaque color
+ *
+ * @example
+ * opaque(#ffffff, rgba(255, 0, 0, 0.5)) // Returns solid red color
+ */
+@function opaque($background, $foreground) {
+ @return color.mix(rgba($foreground, 1), $background, opacity($foreground) * 100%);
+}
+
+/**
+ * Adjusts a color channel value for luminance calculation
+ *
+ * @param {Number} $channel - The color channel value (0-1)
+ * @return {Number} - The adjusted channel value
+ *
+ * @example
+ * adjust(0.5) // Returns adjusted value for luminance calculation
+ */
+@function adjust($channel) {
+ @return if($channel < 0.03928, math.div($channel, 12.92), pow(($channel + 0.055) / 1.055, 2.4));
+}
+
+/**
+ * Calculates the relative luminance of a color using WCAG formula
+ *
+ * @param {Color} $color - The color to calculate luminance for
+ * @return {Number} - The luminance value (0-1)
+ *
+ * @example
+ * luminance(#ffffff) // Returns: 1
+ * luminance(#000000) // Returns: 0
+ */
+@function luminance($color) {
+ $r: math.div(color.channel($color, "red", $space: rgb), 255);
+ $g: math.div(color.channel($color, "green", $space: rgb), 255);
+ $b: math.div(color.channel($color, "blue", $space: rgb), 255);
+
+ $r: adjust($r);
+ $g: adjust($g);
+ $b: adjust($b);
+
+ @return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
+}
+
+/**
+ * Determines the best contrasting color for a given background
+ *
+ * @param {Color} $background - The background color
+ * @param {Color} $light - The light color option (default: #ffffff)
+ * @param {Color} $dark - The dark color option (default: #000000)
+ * @return {Color} - The contrasting color (light or dark)
+ *
+ * @example
+ * color-contrast(#000000) // Returns: #ffffff
+ * color-contrast(#ffffff) // Returns: #000000
+ */
+@function color-contrast($background, $light: #ffffff, $dark: #000000) {
+ // Get relative luminance using WCAG formula
+
+ $bg-luminance: luminance($background);
+
+ // If background is dark, return light color, otherwise dark
+ @return if($bg-luminance > 0.179, $dark, $light);
+}
+
+/**
+ * Adds two values together, handling different data types
+ *
+ * @param {*} $value1 - First value to add
+ * @param {*} $value2 - Second value to add
+ * @param {Boolean} $return-calc - Whether to return calc() function (default: true)
+ * @return {*} - The result of addition or calc() expression
+ *
+ * @example
+ * add(10px, 5px) // Returns: 15px
+ * add(10px, 5px, false) // Returns: 10px + 5px
+ */
+@function add($value1, $value2, $return-calc: true) {
+ @if $value1 == null {
+ @return $value2;
+ }
+
+ @if $value2 == null {
+ @return $value1;
+ }
+
+ @if meta.type-of($value1) == number and meta.type-of($value2) == number and math.compatible($value1, $value2) {
+ @return $value1 + $value2;
+ }
+
+ @return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + string.unquote(" + ") + $value2);
+}
+
+/**
+ * Subtracts the second value from the first value
+ *
+ * @param {*} $value1 - The value to subtract from
+ * @param {*} $value2 - The value to subtract
+ * @param {Boolean} $return-calc - Whether to return calc() function (default: true)
+ * @return {*} - The result of subtraction or calc() expression
+ *
+ * @example
+ * subtract(10px, 5px) // Returns: 5px
+ * subtract(10px, 5px, false) // Returns: 10px - 5px
+ */
+@function subtract($value1, $value2, $return-calc: true) {
+ @if $value1 == null and $value2 == null {
+ @return null;
+ }
+
+ @if $value1 == null {
+ @return -$value2;
+ }
+
+ @if $value2 == null {
+ @return $value1;
+ }
+
+ @if meta.type-of($value1) == number and meta.type-of($value2) == number and math.compatible($value1, $value2) {
+ @return $value1 - $value2;
+ }
+
+ @if meta.type-of($value2) != number {
+ $value2: string.unquote("(") + $value2 + string.unquote(")");
+ }
+
+ @return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(" - ") + $value2);
+}
+
+/**
+ * Divides the dividend by the divisor with specified precision
+ *
+ * @param {Number} $dividend - The number to be divided
+ * @param {Number} $divisor - The number to divide by
+ * @param {Number} $precision - The number of decimal places (default: 10)
+ * @return {Number} - The result of division
+ *
+ * @example
+ * divide(10, 2) // Returns: 5
+ * divide(10px, 2) // Returns: 5px
+ * divide(10, 3) // Returns: 3.3333333333
+ */
+@function divide($dividend, $divisor, $precision: 10) {
+ $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
+ $dividend: abs($dividend);
+ $divisor: abs($divisor);
+ @if $dividend == 0 {
+ @return 0;
+ }
+ @if $divisor == 0 {
+ @error "Cannot divide by 0";
+ }
+ $remainder: $dividend;
+ $result: 0;
+ $factor: 10;
+ @while ($remainder > 0 and $precision >= 0) {
+ $quotient: 0;
+ @while ($remainder >= $divisor) {
+ $remainder: $remainder - $divisor;
+ $quotient: $quotient + 1;
+ }
+ $result: $result * 10 + $quotient;
+ $factor: $factor * 0.1;
+ $remainder: $remainder * 10;
+ $precision: $precision - 1;
+ @if ($precision < 0 and $remainder >= $divisor * 5) {
+ $result: $result + 1;
+ }
+ }
+ $result: $result * $factor * $sign;
+ $dividend-unit: math.unit($dividend);
+ $divisor-unit: math.unit($divisor);
+ $unit-map: (
+ "px": 1px,
+ "rem": 1rem,
+ "em": 1em,
+ "%": 1%,
+ );
+ @if ($dividend-unit != $divisor-unit and map.has-key($unit-map, $dividend-unit)) {
+ $result: $result * map.get($unit-map, $dividend-unit);
+ }
+ @return $result;
+}
+
+/**
+ * Replaces all occurrences of a search string with a replace string
+ *
+ * @param {String} $string - The string to search in
+ * @param {String} $search - The string to search for
+ * @param {String} $replace - The string to replace with (default: "")
+ * @return {String} - The string with replacements made
+ *
+ * @example
+ * str-replace("hello world", "world", "universe") // Returns: "hello universe"
+ * str-replace("hello world", "o") // Returns: "hell wrld"
+ */
+@function str-replace($string, $search, $replace: "") {
+ $index: string.index($string, $search);
+
+ @if $index {
+ @return string.slice($string, 1, $index - 1) + $replace + str-replace(string.slice($string, $index + string.length($search)), $search, $replace);
+ }
+
+ @return $string;
+}
+
+/**
+ * Applies a function to each key-value pair in a map
+ *
+ * @param {Map} $map - The map to iterate over
+ * @param {String} $func - The name of the function to apply
+ * @param {*} $args... - Additional arguments to pass to the function
+ * @return {Map} - A new map with the function applied to each value
+ *
+ * @example
+ * $colors: (primary: #007bff, secondary: #6c757d);
+ * map-loop($colors, darken, 10%) // Returns map with darkened colors
+ */
+@function map-loop($map, $func, $args...) {
+ $_map: ();
+
+ @each $key, $value in $map {
+ // allow to pass the $key and $value of the map as an function argument
+ $_args: ();
+ @each $arg in $args {
+ $_args: list.append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg)));
+ }
+
+ $_map: map.merge($_map, ($key: meta.call(meta.get-function($func), $_args...)));
+ }
+
+ @return $_map;
}
\ No newline at end of file
diff --git a/core/scss/_bootstrap-override.scss b/core/scss/_bootstrap-override.scss
index 6b7367b32..d5d7db173 100644
--- a/core/scss/_bootstrap-override.scss
+++ b/core/scss/_bootstrap-override.scss
@@ -70,9 +70,4 @@
// Override bootstrap core
}
-//
-// TODO: remove when https://github.com/twbs/bootstrap/pull/37425/ will be released
-//
-@function opaque($background, $foreground) {
- @return mix(rgba($foreground, 1), $background, opacity($foreground) * 100%);
-}
+
diff --git a/core/scss/mixins/_functions.scss b/core/scss/mixins/_functions.scss
index 35a937a89..5f845cb86 100644
--- a/core/scss/mixins/_functions.scss
+++ b/core/scss/mixins/_functions.scss
@@ -1,26 +1,46 @@
@use "sass:math";
+/**
+ * Creates a lighter version of a theme color by mixing it with a background color
+ *
+ * @param {Color} $color - The base color to lighten
+ * @param {Color} $background - The background color to mix with (default: #fff)
+ * @return {Color} - The lighter version of the color (10% mix)
+ *
+ * @example
+ * theme-color-lighter(#007bff) // Returns lighter blue
+ * theme-color-lighter(#dc3545, #f8f9fa) // Returns lighter red mixed with light gray
+ */
@function theme-color-lighter($color, $background: #fff) {
@return mix($color, $background, 10%);
}
+/**
+ * Creates a darker version of a theme color by shading it
+ *
+ * @param {Color} $color - The base color to darken
+ * @return {Color} - The darker version of the color (10% shade)
+ *
+ * @example
+ * theme-color-darker(#007bff) // Returns darker blue
+ * theme-color-darker(#28a745) // Returns darker green
+ */
@function theme-color-darker($color) {
@return shade-color($color, 10%);
}
-//
-// Replace all occurrences of a substring within a string.
-//
-@function str-replace($string, $search, $replace: "") {
- $index: str-index($string, $search);
-
- @if $index {
- @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
- }
-
- @return $string;
-}
-
+/**
+ * Media query mixin for breakpoints smaller than the specified breakpoint
+ *
+ * @param {String} $name - The breakpoint name to target
+ * @param {Map} $breakpoints - The breakpoints map (default: $grid-breakpoints)
+ * @content - The CSS content to apply within the media query
+ *
+ * @example
+ * @include media-breakpoint-down-than(lg) {
+ * .container { max-width: 100%; }
+ * }
+ */
@mixin media-breakpoint-down-than($name, $breakpoints: $grid-breakpoints) {
$prev: breakpoint-prev($name);
@@ -39,6 +59,19 @@
}
}
+/**
+ * Gets the previous breakpoint name in the breakpoints map
+ *
+ * @param {String} $name - The current breakpoint name
+ * @param {Map} $breakpoints - The breakpoints map (default: $grid-breakpoints)
+ * @param {List} $breakpoint-names - List of breakpoint names (default: map-keys($breakpoints))
+ * @return {String|null} - The previous breakpoint name or null if none exists
+ *
+ * @example
+ * breakpoint-prev(lg) // Returns: md
+ * breakpoint-prev(sm) // Returns: xs
+ * breakpoint-prev(xs) // Returns: null
+ */
@function breakpoint-prev($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
$n: index($breakpoint-names, $name);
@if not $n {
@@ -47,9 +80,15 @@
@return if($n > 1, nth($breakpoint-names, $n - 1), null);
}
-//
-// Escape SVG strings.
-//
+/**
+ * Escapes special characters in SVG strings for use in CSS
+ *
+ * @param {String} $string - The SVG string to escape
+ * @return {String} - The escaped SVG string
+ *
+ * @example
+ * escape-svg("data:image/svg+xml,") // Returns escaped SVG
+ */
@function escape-svg($string) {
@if str-index($string, "data:image/svg+xml") {
@each $char, $encoded in $escaped-characters {
@@ -70,6 +109,10 @@
*
* @param {Number} $value - The value to be converted to a percentage.
* @return {String} - The percentage representation of the value.
+ *
+ * @example
+ * to-percentage(0.5) // Returns: 50%
+ * to-percentage(0.25) // Returns: 25%
*/
@function to-percentage($value) {
@return if(math.is-unitless($value), math.percentage($value), $value);
@@ -80,7 +123,12 @@
*
* @param {Color} $color - The base color to be made transparent.
* @param {Number} $alpha - The level of transparency, ranging from 0 (fully transparent) to 1 (fully opaque). Default is 1.
+ * @param {Color} $background - The background color to mix with (default: transparent)
* @return {Color} - The resulting color with the specified transparency.
+ *
+ * @example
+ * color-transparent(#007bff, 0.5) // Returns semi-transparent blue
+ * color-transparent(#dc3545, 0.8) // Returns mostly opaque red
*/
@function color-transparent($color, $alpha: 1, $background: transparent) {
@if $alpha == 1 {
@@ -90,6 +138,15 @@
}
}
+/**
+ * Converts an SVG string to a data URL format for use in CSS
+ *
+ * @param {String} $svg - The SVG markup to convert
+ * @return {String} - The data URL formatted SVG
+ *
+ * @example
+ * url-svg('') // Returns: url('data:image/svg+xml;charset=UTF-8,...')
+ */
@function url-svg($svg) {
$svg: str-replace($svg, '#', '%23');
$svg: str-replace($svg, '