]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Upgrade to Sass v1.95.0, redo if() style (#41943)
authorMark Otto <markd.otto@gmail.com>
Thu, 18 Dec 2025 17:15:26 +0000 (09:15 -0800)
committerMark Otto <markdotto@gmail.com>
Fri, 9 Jan 2026 04:09:53 +0000 (20:09 -0800)
* Migrate to latest Sass, redo if() style

* Refactor and disable

18 files changed:
build/generate-utilities-metadata.scss
package-lock.json
package.json
scss/_carousel.scss
scss/_dialog.scss
scss/_dropdown.scss
scss/_functions.scss
scss/_list-group.scss
scss/_navbar.scss
scss/_theme.scss
scss/buttons/_button.scss
scss/forms/_form-control.scss
scss/forms/_validation.scss
scss/layout/_breakpoints.scss
scss/mixins/_box-shadow.scss
scss/mixins/_forms.scss
scss/mixins/_grid.scss
scss/mixins/_utilities.scss

index 8d0a387ed22aaf4d8df37b45ecc11a62d6487d16..e146be59e139d66d7c05ec82696fb60bf8fc7f71 100644 (file)
@@ -27,13 +27,22 @@ $total-utilities: list.length(map.keys($utilities-map)) !default;
   // Skip if utility is null or false (disabled)
   @if $utility {
     // Extract class prefix
-    $class: if(map.has-key($utility, "class"), map.get($utility, "class"), $key);
+    $class: $key;
+    @if map.has-key($utility, "class") {
+      $class: map.get($utility, "class");
+    }
 
     // Extract property
-    $property: if(map.has-key($utility, "property"), map.get($utility, "property"), null);
+    $property: null;
+    @if map.has-key($utility, "property") {
+      $property: map.get($utility, "property");
+    }
 
     // Extract values
-    $values: if(map.has-key($utility, "values"), map.get($utility, "values"), null);
+    $values: null;
+    @if map.has-key($utility, "values") {
+      $values: map.get($utility, "values");
+    }
 
     // Generate class list
     $classes: "";
@@ -45,7 +54,10 @@ $total-utilities: list.length(map.keys($utilities-map)) !default;
           @if not $first {
             $classes: $classes + ", ";
           }
-          $class-name: if($value-key == "null" or $value-key == null, $class, "#{$class}-#{$value-key}");
+          $class-name: "#{$class}-#{$value-key}";
+          @if $value-key == "null" or $value-key == null {
+            $class-name: $class;
+          }
           $classes: $classes + '"' + $class-name + '"';
           $first: false;
         }
index f06285e5f176787ca1a8249d0c5eb09d72818406..f969af475c24b1d8257c4819285e21a3974aab5d 100644 (file)
@@ -85,7 +85,7 @@
         "remark-html": "^16.0.1",
         "rollup": "^4.54.0",
         "rollup-plugin-istanbul": "^5.0.0",
-        "sass": "^1.90.0",
+        "sass": "^1.95.0",
         "sass-true": "^10.1.0",
         "shelljs": "^0.10.0",
         "stylelint": "^16.26.1",
       "license": "MIT"
     },
     "node_modules/sass": {
-      "version": "1.90.0",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz",
-      "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
+      "version": "1.97.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.0.tgz",
+      "integrity": "sha512-KR0igP1z4avUJetEuIeOdDlwaUDvkH8wSx7FdSjyYBS3dpyX3TzHfAMO0G1Q4/3cdjcmi3r7idh+KCmKqS+KeQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "chokidar": "^4.0.0",
         "immutable": "^5.0.2",
index c8c8ef381127939301fc808ca4b8c50e7e511323..036f27bed7323b3471ef7c28d85b720843498cae 100644 (file)
     "remark-html": "^16.0.1",
     "rollup": "^4.54.0",
     "rollup-plugin-istanbul": "^5.0.0",
-    "sass": "^1.90.0",
+    "sass": "^1.95.0",
     "sass-true": "^10.1.0",
     "shelljs": "^0.10.0",
     "stylelint": "^16.26.1",
index 0876d196c3dfbab87ab6335d074a6af9d3a5cb9a..8c8ec91f466591b2ed9d3972ba44071f4ad78b4c 100644 (file)
@@ -159,11 +159,13 @@ $carousel-control-icon-filter-dark:   invert(1) grayscale(100) !default;
   }
   .carousel-control-prev {
     inset-inline-start: 0;
-    background-image: if($enable-gradients, linear-gradient(90deg, rgba($black, .25), rgba($black, .001)), null);
+    // stylelint-disable-next-line scss/at-function-named-arguments, @stylistic/function-whitespace-after
+    background-image: if(sass($enable-gradients): linear-gradient(90deg, rgba($black, .25), rgba($black, .001)); else: null);
   }
   .carousel-control-next {
     inset-inline-end: 0;
-    background-image: if($enable-gradients, linear-gradient(270deg, rgba($black, .25), rgba($black, .001)), null);
+    // stylelint-disable-next-line scss/at-function-named-arguments, @stylistic/function-whitespace-after
+    background-image: if(sass($enable-gradients): linear-gradient(270deg, rgba($black, .25), rgba($black, .001)); else: null);
   }
 
   // Icons for within
index 9989dba3e4f66fce55b6bfdd9ffd284e0e0325f4..d90aac8ffd8123dbdb1f65dc021078bcd1245a55 100644 (file)
@@ -174,7 +174,8 @@ $dialog-footer-gap:             .5rem !default;
   // Responsive fullscreen dialogs
   @each $breakpoint in map.keys($grid-breakpoints) {
     $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
-    $postfix: if($infix != "", #{$infix}-down, "");
+    // stylelint-disable-next-line scss/at-function-named-arguments
+    $postfix: if(sass($infix != ""): #{$infix}-down; else: "");
 
     @if $postfix != "" {
       @include media-breakpoint-down($breakpoint) {
index 5d0a3401005d58f9ee59d1a8f583cfbaa2bd1fde..6d8cedf689bf4434d9f49acfab58ecde88eb6283 100644 (file)
@@ -179,7 +179,8 @@ $dropdown-dark-header-color:        var(--#{$prefix}gray-500) !default;
       pointer-events: none;
       background-color: transparent;
       // Remove CSS gradients if they're enabled
-      background-image: if($enable-gradients, none, null);
+      // stylelint-disable-next-line scss/at-function-named-arguments
+      background-image: if(sass($enable-gradients): none; else: null);
     }
   }
 
index c836c40dc582629547fcfe25c22e8017574cfe77..2c1e9feb1cf96b684695ae68e0f4c37d2ca18d88 100644 (file)
     // 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)));
+      $resolved-arg: $arg;
+      @if $arg == "$key" {
+        $resolved-arg: $key;
+      } @else if $arg == "$value" {
+        $resolved-arg: $value;
+      }
+      $_args: list.append($_args, $resolved-arg);
     }
 
     $_map: map.merge($_map, ($key: meta.call(meta.get-function($func), $_args...)));
@@ -200,7 +206,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
   $l1: luminance($background);
   $l2: luminance(opaque($background, $foreground));
 
-  @return if($l1 > $l2, math.div($l1 + .05, $l2 + .05), math.div($l2 + .05, $l1 + .05));
+  @return if(sass($l1 > $l2): math.div($l1 + .05, $l2 + .05); else: math.div($l2 + .05, $l1 + .05));
 }
 
 // Return WCAG2.2 relative luminance
@@ -214,7 +220,8 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
   );
 
   @each $name, $value in $rgb {
-    $value: if(math.div($value, 255) < .04045, math.div(math.div($value, 255), 12.92), list.nth($_luminance-list, math.round($value + 1)));
+    // stylelint-disable-next-line scss/at-function-named-arguments, @stylistic/function-whitespace-after
+    $value: if(sass(math.div($value, 255) < .04045): math.div(math.div($value, 255), 12.92); else: list.nth($_luminance-list, math.round($value + 1)));
     $rgb: map.merge($rgb, ($name: $value));
   }
 
@@ -240,6 +247,6 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
 
 // // Shade the color if the weight is positive, else tint it
 @function shift-color($color, $weight) {
-  @return if($weight > 0, shade-color($color, $weight), tint-color($color, -$weight));
+  @return if(sass($weight > 0): shade-color($color, $weight); else: tint-color($color, -$weight));
 }
 // scss-docs-end color-functions
index ab67b18eca0f5e4eadc6329c7322047c6d9744e9..a51591fe565144961aee798d939cf3f9594443d2 100644 (file)
@@ -82,7 +82,8 @@ $list-group-action-active-bg:       var(--#{$prefix}secondary-bg) !default;
     display: block;
     padding: var(--#{$prefix}list-group-item-padding-y) var(--#{$prefix}list-group-item-padding-x);
     color: var(--#{$prefix}theme-text, var(--#{$prefix}list-group-color));
-    text-decoration: if($link-decoration == none, null, none);
+    // stylelint-disable-next-line scss/at-function-named-arguments
+    text-decoration: if(sass($link-decoration == none): null);
     background-color: var(--#{$prefix}theme-bg-subtle, var(--#{$prefix}list-group-bg));
     border: var(--#{$prefix}list-group-border-width) solid var(--#{$prefix}theme-border, var(--#{$prefix}list-group-border-color));
 
index 53c8dfd7721a784f983c46b3adb760bc7618a7f8..2851124f0252f8edca90df1f6ba83606178521a7 100644 (file)
@@ -59,7 +59,8 @@ $navbar-dark-brand-hover-color:     $navbar-dark-active-color !default;
 @layer components {
   .navbar {
     // scss-docs-start navbar-css-vars
-    --#{$prefix}navbar-padding-x: #{if($navbar-padding-x == null, 0, $navbar-padding-x)};
+    // stylelint-disable-next-line scss/at-function-named-arguments
+    --#{$prefix}navbar-padding-x: #{if(sass($navbar-padding-x == null): 0; else: $navbar-padding-x)};
     --#{$prefix}navbar-padding-y: #{$navbar-padding-y};
     --#{$prefix}navbar-color: #{$navbar-light-color};
     --#{$prefix}navbar-hover-color: #{$navbar-light-hover-color};
index 33e81f3992ef088958d12c3c1777b0f1c81d92fa..affd226a99fc0f27410950a85ea33b9427005616 100644 (file)
@@ -47,7 +47,8 @@
 // Recursive mixin to handle nested maps
 @mixin create-css-vars($map, $parent-key: "") {
   @each $key, $value in $map {
-    $current-key: if($parent-key == "", $key, "#{$parent-key}-#{$key}");
+    // stylelint-disable-next-line scss/at-function-named-arguments
+    $current-key: if(sass($parent-key == ""): $key; else: "#{$parent-key}-#{$key}");
 
     @if meta.type-of($value) == "map" {
       @include create-css-vars($value, $current-key);
index faacc3c86b7a1e8c3c48b5ed1df7d638ceec9a8f..7107dde3b13e124cd652819a13a5c602407d48e4 100644 (file)
@@ -164,7 +164,8 @@ $btn-variant-selectors: () !default;
     text-decoration: none;
     white-space: $btn-white-space;
     vertical-align: middle;
-    cursor: if($enable-button-pointers, pointer, null);
+    // stylelint-disable-next-line scss/at-function-named-arguments
+    cursor: if(sass($enable-button-pointers): pointer; else: null);
     user-select: none;
     background-color: var(--#{$prefix}btn-bg, var(--#{$prefix}bg-2));
     border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color);
@@ -199,7 +200,8 @@ $btn-variant-selectors: () !default;
       color: var(--#{$prefix}btn-disabled-color);
       pointer-events: none;
       background-color: var(--#{$prefix}btn-disabled-bg, var(--#{$prefix}bg-1));
-      background-image: if($enable-gradients, none, null);
+      // stylelint-disable-next-line scss/at-function-named-arguments
+      background-image: if(sass($enable-gradients): none; else: null);
       border-color: var(--#{$prefix}btn-disabled-border-color);
       opacity: var(--#{$prefix}btn-disabled-opacity);
     }
@@ -367,7 +369,8 @@ $btn-variant-selectors: () !default;
     &:has(input:checked) {
       color: var(--#{$prefix}btn-active-color);
       background-color: var(--#{$prefix}btn-active-bg, var(--#{$prefix}bg-3));
-      background-image: if($enable-gradients, none, null);
+      // stylelint-disable-next-line scss/at-function-named-arguments
+      background-image: if(sass($enable-gradients): none; else: null);
       border-color: var(--#{$prefix}btn-active-border-color);
       @include box-shadow(var(--#{$prefix}btn-active-shadow));
     }
@@ -381,7 +384,8 @@ $btn-variant-selectors: () !default;
       color: var(--#{$prefix}btn-disabled-color);
       pointer-events: none;
       background-color: var(--#{$prefix}btn-disabled-bg, var(--#{$prefix}bg-1));
-      background-image: if($enable-gradients, none, null);
+      // stylelint-disable-next-line scss/at-function-named-arguments
+      background-image: if(sass($enable-gradients): none; else: null);
       border-color: var(--#{$prefix}btn-disabled-border-color);
       opacity: var(--#{$prefix}btn-disabled-opacity);
       @include box-shadow(none);
index ac8f53f0bd26d8af8d7a696906643165d13d0ce8..dfd4749dd17c0d032cd7f29cfede5ec899ed33a7 100644 (file)
@@ -70,7 +70,8 @@
       // https://github.com/twbs/bootstrap/issues/23307
       // TODO: we can remove this workaround once https://bugs.webkit.org/show_bug.cgi?id=198959 is resolved
       // Multiply line-height by 1em if it has no unit
-      height: if(math.unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
+      // stylelint-disable-next-line scss/at-function-named-arguments
+      height: if(sass(math.unit($input-line-height) == ""): $input-line-height * 1em; else: $input-line-height);
 
       // Android Chrome type="date" is taller than the other inputs
       // because of "margin: 1px 24px 1px 4px" inside the shadow DOM
index e567bc97ed4f64e5dfdd527f08ce593a870cfa64..a6b97564ce9d848a4a03397de305d91e335e92ca 100644 (file)
 // 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} {
+    .was-validated #{if(sass(&): "&"; else: "")}:#{$state},
+    #{if(sass(&): "&"; else: "")}.is-#{$state} {
       @content;
     }
   } @else {
-    #{if(&, "&", "")}.is-#{$state} {
+    #{if(sass(&): "&"; else: "")}.is-#{$state} {
       @content;
     }
   }
index c8d33116c2ba1bf65ac5dffcf8f2f1b525c536c1..5ec006d3c02761c096cf78a0ebeb9f9a50483bee 100644 (file)
   @if not $n {
     @error "breakpoint `#{$name}` not found in `#{$breakpoint-names}`";
   }
-  @return if($n < list.length($breakpoint-names), list.nth($breakpoint-names, $n + 1), null);
+  // Use @if/@else because list.nth would error if evaluated when $n equals list length
+  @if $n < list.length($breakpoint-names) {
+    @return list.nth($breakpoint-names, $n + 1);
+  } @else {
+    @return null;
+  }
 }
 
 // Minimum breakpoint width. Null for the smallest (first) breakpoint.
@@ -32,7 +37,7 @@
 //    576px
 @function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
   $min: map.get($breakpoints, $name);
-  @return if($min != 0, $min, null);
+  @return if(sass($min != 0): $min; else: null);
 }
 
 // Maximum breakpoint width for range media queries.
@@ -47,7 +52,7 @@
     @return null;
   }
   $max: map.get($breakpoints, $name);
-  @return if($max and $max > 0, $max, null);
+  @return if(sass($max and $max > 0): $max; else: null);
 }
 
 // Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
@@ -58,7 +63,7 @@
 //    >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px))
 //    "-sm"
 @function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
-  @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
+  @return if(sass(breakpoint-min($name, $breakpoints) == null): ""; else: "-#{$name}");
 }
 
 // Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
index da4aa50bb8ea10773ee74fdad8ce391ecb80db3b..fa6c22277537d3e7d5b3122ac90cb2848dae2b83 100644 (file)
@@ -1,3 +1,4 @@
+@use "sass:list";
 @use "../config" as *;
 
 @mixin box-shadow($shadow...) {
           $has-single-value: true;
           $single-value: $value;
         } @else {
-          $result: append($result, $value, "comma");
+          $result: list.append($result, $value, "comma");
         }
       }
     }
 
     @if $has-single-value {
       box-shadow: $single-value;
-    } @else if (length($result) > 0) {
+    } @else if (list.length($result) > 0) {
       box-shadow: $result;
     }
   }
index faee7acc827094a6e6429bb13f5a86771e968c73..b6f82acaec8119e67960225390b1473d2ad8cbc9 100644 (file)
@@ -4,12 +4,12 @@
 // 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} {
+    .was-validated #{if(sass(&): "&"; else: "")}:#{$state},
+    #{if(sass(&): "&"; else: "")}.is-#{$state} {
       @content;
     }
   } @else {
-    #{if(&, "&", "")}.is-#{$state} {
+    #{if(sass(&): "&"; else: "")}.is-#{$state} {
       @content;
     }
   }
index 988d3bbad132d7f2319ac564816dd0104f28db20..384002a683ab70d067d8d54e6a131659e93bbb74 100644 (file)
@@ -20,7 +20,8 @@
 
 @mixin make-col-ready() {
   // Add box sizing if only the grid is loaded
-  box-sizing: if(meta.variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);
+  // stylelint-disable-next-line scss/at-function-named-arguments
+  box-sizing: if(sass(meta.variable-exists(include-column-box-sizing) and $include-column-box-sizing): border-box; else: 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.
@@ -49,7 +50,8 @@
 
 @mixin make-col-offset($size, $columns: $grid-columns) {
   $num: math.div($size, $columns);
-  margin-inline-start: if($num == 0, 0, math.percentage($num));
+  // stylelint-disable-next-line scss/at-function-named-arguments
+  margin-inline-start: if(sass($num == 0): 0; else: math.percentage($num));
 }
 
 // Row columns
index bb33b4cc1b3500c170b64908db4c79daf270010b..6556e69c1aa6b818c7c77706f94de20c411abb1e 100644 (file)
 
 @mixin generate-utility($utility, $infix: "") {
   // Determine if we're generating a class, or an attribute selector
-  $selectorType: if(map.has-key($utility, selector), map.get($utility, selector), "class");
+  $selectorType: "class";
+  @if map.has-key($utility, selector) {
+    $selectorType: map.get($utility, selector);
+  }
   // Then get the class name to use in a class (e.g., .class) or in a attribute selector (e.g., [class^="class"])
   $selectorClass: map.get($utility, class);
 
     @if meta.type-of($properties) == "map" {
       $propertyMap: $properties;
       // For property maps, we need to determine the class from the utility definition
-      $customClass: if(map.has-key($utility, class), map.get($utility, class), "");
+      $customClass: "";
+      @if map.has-key($utility, class) {
+        $customClass: map.get($utility, class);
+      }
     } @else {
       // Legacy approach: Multiple properties are possible, for example with vertical or horizontal margins or paddings
       @if meta.type-of($properties) == "string" {
         $properties: list.append((), $properties);
       }
       // Use custom class if present, otherwise use the first value from the list of properties
-      $customClass: if(map.has-key($utility, class), map.get($utility, class), list.nth($properties, 1));
-      $customClass: if($customClass == null, "", $customClass);
+      @if map.has-key($utility, class) {
+        $customClass: map.get($utility, class);
+      } @else {
+        $customClass: list.nth($properties, 1);
+      }
+      @if $customClass == null {
+        $customClass: "";
+      }
     }
 
     // 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 state variants
-    $state: if(map.has-key($utility, state), map.get($utility, state), ());
+    $state: ();
+    @if map.has-key($utility, state) {
+      $state: map.get($utility, state);
+    }
 
     // $infix: if($customClass == "" and str-slice($infix, 1, 1) == "-", str-slice($infix, 2), $infix);
 
     // Don't prefix if value key is null (e.g. with shadow class)
-    $customClassModifier: if($key, if($customClass == "" and $infix == "", "", "-") + $key, "");
+    $customClassModifier: "";
+    @if $key {
+      @if $customClass == "" and $infix == "" {
+        $customClassModifier: $key;
+      } @else {
+        $customClassModifier: "-" + $key;
+      }
+    }
 
     $selector: "";
     @if $selectorType == "class" {