Skip to content

4.2 breaking change: custom $grid-breakpoints that are smaller than default ones give errors #27927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
phazei opened this issue Dec 27, 2018 · 13 comments

Comments

@phazei
Copy link

phazei commented Dec 27, 2018

I'm unable to compile sass with the new $grid-breakpoint changes.
This worked just fine with 4.1. It now doesn't seem possible with 4.2

In my custom variable file that's included before everything BS4 I have the following:

$grid-breakpoints: (
        xxs: 0,
        xs: 480px,
        sm: 576px,
        md: 768px,
        lg: 992px,
        xl: 1200px
);

As you can see, an additional custom breakpoint exists at 480px.

Previously 4.1 _variable.sass contained:

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;

@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
@include _assert-starts-at-zero($grid-breakpoints);

And my code overrode all the breakpoints and worked fine.

Now in 4.2 _variable.sass:

$grid-breakpoints: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$grid-breakpoints: map-merge(
                (
                        xs: 0,
                        sm: 576px,
                        md: 768px,
                        lg: 992px,
                        xl: 1200px
                ),
                $grid-breakpoints
);

@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
@include _assert-starts-at-zero($grid-breakpoints);

And it gives the following errors:

WARNING: Invalid value for $grid-breakpoints: This map must be in ascending order, but key 'xxs' has value 0 which isn't greater than 1200px, the value of the previous key 'xl' !

WARNING: First breakpoint in `$grid-breakpoints` must start at 0, but starts at 480px.

WARNING: Invalid value for $container-max-widths: This map must be in ascending order, but key 'xs' has value 500px which isn't greater than 1140px, the value of the previous key 'xl' !
@phazei phazei changed the title 4.2 broke building custom $grid-breakpoints that are smaller than default ones 4.2 breaking change: custom $grid-breakpoints that are smaller than default ones give errors Dec 27, 2018
@phazei
Copy link
Author

phazei commented Dec 27, 2018

The def of map-merge() specifies that the order of $map1 will have precedence while $map2 will override values.

/**
 * Merges two maps together into a new map. Keys in `$map2` will take
 * precedence over keys in `$map1`.
 * 
 * This is the best way to add new values to a map.
 * 
 * All keys in the returned map that also appear in `$map1` will have the
 * same order as in `$map1`. New keys from `$map2` will be placed at the end
 * of the map.
 * 
 * Like all map functions, `map-merge()` returns a new map rather than
 * modifying its arguments in place.
 * 
 * @example
 * map-merge(("foo": 1), ("bar": 2)) => ("foo": 1, "bar": 2)
 * map-merge(("foo": 1, "bar": 2), ("bar": 3)) => ("foo": 1, "bar": 3)
 * @overload map_merge($map1, $map2)
 * @param $map1 [Sass::Script::Value::Map]
 * @param $map2 [Sass::Script::Value::Map]
 * @return [Sass::Script::Value::Map]
 * @raise [ArgumentError] if either parameter is not a map
 */
@function map_merge($map1, $map2) { /* stub */ }

Since, unlike the other maps that are merged, the order is important, $map2 should have precedence.

To achieve this I think the double application of map-merge() should take care of it:

$grid-breakpoints: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$grid-breakpoints-ordered: map-merge(
                $grid-breakpoints,
                (
                        xs: 0,
                        sm: 576px,
                        md: 768px,
                        lg: 992px,
                        xl: 1200px
                )
);
$grid-breakpoints: map-merge($grid-breakpoints-ordered, $grid-breakpoints)

Unless there's some other sass function that could handle this, I'm not sure of any better way.

@phazei
Copy link
Author

phazei commented Dec 27, 2018

Additionally the docs give a specific use example:

If you wanted just four grid tiers, you’d update the $grid-breakpoints ...

which is no longer true since by just defining fewer breakpoints, the default ones will be added back in on merge.

@nicolomonili
Copy link

nicolomonili commented Dec 28, 2018

I have the same problem, i'm trying to compile with https://github.com/leafo/scssphp
Before the version 4.2 everything was ok, now i have the same mistakes as you.

@nicolomonili
Copy link

The def of map-merge() specifies that the order of $map1 will have precedence while $map2 will override values.

/**
 * Merges two maps together into a new map. Keys in `$map2` will take
 * precedence over keys in `$map1`.
 * 
 * This is the best way to add new values to a map.
 * 
 * All keys in the returned map that also appear in `$map1` will have the
 * same order as in `$map1`. New keys from `$map2` will be placed at the end
 * of the map.
 * 
 * Like all map functions, `map-merge()` returns a new map rather than
 * modifying its arguments in place.
 * 
 * @example
 * map-merge(("foo": 1), ("bar": 2)) => ("foo": 1, "bar": 2)
 * map-merge(("foo": 1, "bar": 2), ("bar": 3)) => ("foo": 1, "bar": 3)
 * @overload map_merge($map1, $map2)
 * @param $map1 [Sass::Script::Value::Map]
 * @param $map2 [Sass::Script::Value::Map]
 * @return [Sass::Script::Value::Map]
 * @raise [ArgumentError] if either parameter is not a map
 */
@function map_merge($map1, $map2) { /* stub */ }

Since, unlike the other maps that are merged, the order is important, $map2 should have precedence.

To achieve this I think the double application of map-merge() should take care of it:

$grid-breakpoints: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$grid-breakpoints-ordered: map-merge(
                $grid-breakpoints,
                (
                        xs: 0,
                        sm: 576px,
                        md: 768px,
                        lg: 992px,
                        xl: 1200px
                )
);
$grid-breakpoints: map-merge($grid-breakpoints-ordered, $grid-breakpoints)

Unless there's some other sass function that could handle this, I'm not sure of any better way.

Have you solved in this way for now?

luisfpg pushed a commit to cyclosproject/cyclos4-ui that referenced this issue Dec 28, 2018
Sticking to Bootstrap 4.1 until the following issue is resolved:
twbs/bootstrap#27927
@phazei
Copy link
Author

phazei commented Dec 29, 2018

Have you solved in this way for now?

Nah, I solved it by changing the dep from ^4.1.0 to ~4.1.0 so it works in the build pipeline.
Easier than forking it for now.
Although, that code does fix it locally. It also needs to be done for the $container-max-widths as well:

$container-max-widths: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$container-max-widths-order: map-merge(
  $container-max-widths,
  (
    sm: 540px,
    md: 720px,
    lg: 960px,
    xl: 1140px
  )

);
$container-max-widths: map-merge($container-max-widths-order, $container-max-widths);

@XhmikosR
Copy link
Member

/CC @MartijnCuppens

@mdo
Copy link
Member

mdo commented Dec 30, 2018

I think we need to ditch these built-in map-merges for regular Sass maps. It now feels over-engineered, and perhaps poorly documented for how folks can customize things.

With the usage of map-merge everywhere, we have the ability to easily add key-value pairs, but not to remove them. There's no option in Sass to do this, but you can replace a value or add new ones (with map-merge). This is an issue because if you want fewer key-value pairs in a Sass map, you'll see errors like those of #27927 (comment).

So, what if we remove all map-merge in our variables?


Let's consider an example where we look at our $grid-breakpoints.

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;

To replace all of the values, we redefine the keys' values:

$grid-breakpoints: (
  xs: 0,
  sm: 400px,
  md: 800px,
  lg: 1200px,
  xl: 1600px
);

To replace individual key-value pairs, we use map-merge:

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;

$grid-breakpoints: map-merge($grid-breakpoints, (xl: 1600px));

To add additional values, we also use map-merge:

$grid-breakpoints: map-merge(
  $grid-breakpoints,
  (
    xxl: 1600px,
    xxxl: 1800px
  )
);

To do a bit of everything, we can use replace the default values and use multiple map-merges:

$grid-breakpoints: (
  xs: 300px,
  sm: 500px,
  md: 800px,
  lg: 1100px,
  xl: 1400px
);
$grid-breakpoints: map-merge(
  (
    xxs: 0
  ),
  $grid-breakpoints
);
$grid-breakpoints: map-merge(
  $grid-breakpoints,
  (
    xxl: 1600px,
    xxxl: 1800px
  )
);

You can see all of this in this Sassmeister gist.

@Bek81
Copy link

Bek81 commented Jan 2, 2019

Hi @mdo
following your instructions, we are trying this way:

$grid-breakpoints: (
    xs: 360px,
    sm: 576px,
    md: 768px,
    lg: 992px,
    xl: 1200px
);

$grid-breakpoints: map-merge((xxs: 0), $grid-breakpoints);
$grid-breakpoints: map-merge($grid-breakpoints, (xxl: 1440px, xxxl: 1680px, xxxxl: 1920px));

$container-max-widths: (
    xs: 330px,
    sm: 540px,
    md: 720px,
    lg: 960px,
    xl: 1140px
);
$container-max-widths: map-merge((xxs: 0), $container-max-widths);
$container-max-widths: map-merge($container-max-widths, (xxl: 1380px, xxxl: 1620px, xxxxl: 1860px));

@import '../../scripts/vendor/bootstrap-4.2.1/scss/bootstrap.scss';

but these are the results:

WARNING: Invalid value for $grid-breakpoints: This map must be in ascending order, but key 'xxs' has value 0px which isn't greater than 1200px, the value of the previous key 'xl' !

WARNING: First breakpoint in $grid-breakpoints must start at 0, but starts at 360px.

WARNING: Invalid value for $container-max-widths: This map must be in ascending order, but key 'xxs' has value 0px which isn't greater than 1140px, the value of the previous key 'xl' !

Error: Invalid null operation: "null sub 0.02".

Can you help us, please? Thanks a lot.

Benedetto

@phazei
Copy link
Author

phazei commented Jan 2, 2019

@Bek81
Those weren't instructions, he was giving an example of how sass maps could work and how they could potentially work in the core _variables.scss file. Simply revert to 4.1 until it's resolved as there's no fix without modifying _variables.scss.

There's also no use in using the merge-map in your own definitions of the variables. The last example @bdo gave would have to have the extra xxs and xxl variables in like $breakpoints-before / $breakpoints-after which is way overly complicated.

The example I provided makes overriding the breakpoints nearly the same as 4.1, but still has the issue of not being able to remove breakpoints. Reverting to the way it was done in 4.1 without merge-map seems like it might be the best solution. The update provides no benefit other than being able to override a single break point or add one larger, most other cases you'd need to override each breakpoint manually anyway.

@JulienCabanes
Copy link

It's also possible to sort maps after merge, I tried with this gist and it worked without changing anything else : https://gist.github.com/Jakobud/a0ac11e80a1de453cd86f0d3fc0a1410#gistcomment-2327765

@ianks
Copy link

ianks commented Jan 9, 2019

This is preventing us from upgrading to 4.2

@carlosmauri
Copy link

carlosmauri commented Jan 10, 2019

Hi there,
what's the status of this "warning" issue?

I would suggest to just revert the new $grid-breakpoints declaration to what it was before, on the 4.1 version:

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
) !default;

the new approach uses:

$grid-breakpoints: map-merge(
  (
    xs: 0,
    sm: 576px,
    md: 768px,
    lg: 992px,
    xl: 1200px
  ),
  $grid-breakpoints
);

so when you try to update the variable using your custom.scss like this:

$grid-breakpoints: (
    xs: 0,
    ss: 380px,
    sm: 576px,
    md: 768px,
    lg: 992px,
    xl: 1200px,
    xx: 1440px
);

the _assert_ascending function will throw the following warning:

Invalid value for $grid-breakpoints: This map must be in ascending order, but key 'ss' has value 320px which isn't greater than 1200px, the value of the previous key 'xl' !
    scss/_functions.scss 18:7    _assert-ascending()

Adding @debug $key; @debug $num; you can easily see what's going on.

scss/_functions.scss:11 Debug: xs
scss/_functions.scss:12 Debug: 0
scss/_functions.scss:11 Debug: sm
scss/_functions.scss:12 Debug: 576px
scss/_functions.scss:11 Debug: md
scss/_functions.scss:12 Debug: 768px
scss/_functions.scss:11 Debug: lg
scss/_functions.scss:12 Debug: 992px
scss/_functions.scss:11 Debug: xl
scss/_functions.scss:12 Debug: 1200px
scss/_functions.scss:11 Debug: ss
scss/_functions.scss:12 Debug: 320px

The ss value I've added in my custom.scss goes to end of the map so this function.
when compares $prev-num >= $num (1200px >= 320px) -> warning message

Note that this is just a warning so the CSS gets compiled.
Thanks!

If you really want to avoid this warning message you can (temporary and hacky fix) comment this line:
@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
_variables.scss lines 201 and 221

@unrevised6419
Copy link

#25392

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests