From: Mark Otto Date: Thu, 18 Dec 2025 17:15:26 +0000 (-0800) Subject: Upgrade to Sass v1.95.0, redo if() style (#41943) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=51beea1d91f4f89fb2f477a10dd7abf5ac0a54e7;p=thirdparty%2Fbootstrap.git Upgrade to Sass v1.95.0, redo if() style (#41943) * Migrate to latest Sass, redo if() style * Refactor and disable --- diff --git a/build/generate-utilities-metadata.scss b/build/generate-utilities-metadata.scss index 8d0a387ed2..e146be59e1 100644 --- a/build/generate-utilities-metadata.scss +++ b/build/generate-utilities-metadata.scss @@ -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; } diff --git a/package-lock.json b/package-lock.json index f06285e5f1..f969af475c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", @@ -15876,10 +15876,11 @@ "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", diff --git a/package.json b/package.json index c8c8ef3811..036f27bed7 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,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", diff --git a/scss/_carousel.scss b/scss/_carousel.scss index 0876d196c3..8c8ec91f46 100644 --- a/scss/_carousel.scss +++ b/scss/_carousel.scss @@ -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 diff --git a/scss/_dialog.scss b/scss/_dialog.scss index 9989dba3e4..d90aac8ffd 100644 --- a/scss/_dialog.scss +++ b/scss/_dialog.scss @@ -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) { diff --git a/scss/_dropdown.scss b/scss/_dropdown.scss index 5d0a340100..6d8cedf689 100644 --- a/scss/_dropdown.scss +++ b/scss/_dropdown.scss @@ -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); } } diff --git a/scss/_functions.scss b/scss/_functions.scss index c836c40dc5..2c1e9feb1c 100644 --- a/scss/_functions.scss +++ b/scss/_functions.scss @@ -64,7 +64,13 @@ // 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 diff --git a/scss/_list-group.scss b/scss/_list-group.scss index ab67b18eca..a51591fe56 100644 --- a/scss/_list-group.scss +++ b/scss/_list-group.scss @@ -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)); diff --git a/scss/_navbar.scss b/scss/_navbar.scss index 53c8dfd772..2851124f02 100644 --- a/scss/_navbar.scss +++ b/scss/_navbar.scss @@ -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}; diff --git a/scss/_theme.scss b/scss/_theme.scss index 33e81f3992..affd226a99 100644 --- a/scss/_theme.scss +++ b/scss/_theme.scss @@ -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); diff --git a/scss/buttons/_button.scss b/scss/buttons/_button.scss index faacc3c86b..7107dde3b1 100644 --- a/scss/buttons/_button.scss +++ b/scss/buttons/_button.scss @@ -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); diff --git a/scss/forms/_form-control.scss b/scss/forms/_form-control.scss index ac8f53f0bd..dfd4749dd1 100644 --- a/scss/forms/_form-control.scss +++ b/scss/forms/_form-control.scss @@ -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 diff --git a/scss/forms/_validation.scss b/scss/forms/_validation.scss index e567bc97ed..a6b97564ce 100644 --- a/scss/forms/_validation.scss +++ b/scss/forms/_validation.scss @@ -19,12 +19,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; } } diff --git a/scss/layout/_breakpoints.scss b/scss/layout/_breakpoints.scss index c8d33116c2..5ec006d3c0 100644 --- a/scss/layout/_breakpoints.scss +++ b/scss/layout/_breakpoints.scss @@ -23,7 +23,12 @@ @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. diff --git a/scss/mixins/_box-shadow.scss b/scss/mixins/_box-shadow.scss index da4aa50bb8..fa6c222775 100644 --- a/scss/mixins/_box-shadow.scss +++ b/scss/mixins/_box-shadow.scss @@ -1,3 +1,4 @@ +@use "sass:list"; @use "../config" as *; @mixin box-shadow($shadow...) { @@ -12,14 +13,14 @@ $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; } } diff --git a/scss/mixins/_forms.scss b/scss/mixins/_forms.scss index faee7acc82..b6f82acaec 100644 --- a/scss/mixins/_forms.scss +++ b/scss/mixins/_forms.scss @@ -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; } } diff --git a/scss/mixins/_grid.scss b/scss/mixins/_grid.scss index 988d3bbad1..384002a683 100644 --- a/scss/mixins/_grid.scss +++ b/scss/mixins/_grid.scss @@ -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 diff --git a/scss/mixins/_utilities.scss b/scss/mixins/_utilities.scss index bb33b4cc1b..6556e69c1a 100644 --- a/scss/mixins/_utilities.scss +++ b/scss/mixins/_utilities.scss @@ -84,7 +84,10 @@ @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); @@ -117,15 +120,24 @@ @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` @@ -133,12 +145,22 @@ // $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" {