Skip to content

Commit 942fea4

Browse files
Copilotmdo
andcommitted
Update breakpoint Sass mixins to use range media query syntax
Co-authored-by: mdo <98681+mdo@users.noreply.github.com>
1 parent 638cebd commit 942fea4

5 files changed

Lines changed: 126 additions & 30 deletions

File tree

scss/mixins/_breakpoints.scss

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
1818
$n: index($breakpoint-names, $name);
1919
@if not $n {
20-
@error "breakpoint `#{$name}` not found in `#{$breakpoints}`";
20+
@error "breakpoint `#{$name}` not found in `#{$breakpoint-names}`";
2121
}
2222
@return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
2323
}
@@ -31,18 +31,19 @@
3131
@return if($min != 0, $min, null);
3232
}
3333

34-
// Maximum breakpoint width.
35-
// The maximum value is reduced by 0.02px to work around the limitations of
36-
// `min-` and `max-` prefixes and viewports with fractional widths.
37-
// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
38-
// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
39-
// See https://bugs.webkit.org/show_bug.cgi?id=178261
34+
// Maximum breakpoint width for range media queries.
35+
// Returns the breakpoint value to use as an upper bound in range queries.
4036
//
41-
// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
42-
// 767.98px
37+
// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
38+
// 576px
39+
// >> breakpoint-max(xxl, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))
40+
// null
4341
@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
42+
@if $name == null {
43+
@return null;
44+
}
4445
$max: map-get($breakpoints, $name);
45-
@return if($max and $max > 0, $max - .02, null);
46+
@return if($max and $max > 0, $max, null);
4647
}
4748

4849
// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
@@ -61,7 +62,7 @@
6162
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
6263
$min: breakpoint-min($name, $breakpoints);
6364
@if $min {
64-
@media (min-width: $min) {
65+
@media (width >= $min) {
6566
@content;
6667
}
6768
} @else {
@@ -74,7 +75,7 @@
7475
@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
7576
$max: breakpoint-max($name, $breakpoints);
7677
@if $max {
77-
@media (max-width: $max) {
78+
@media (width < $max) {
7879
@content;
7980
}
8081
} @else {
@@ -89,7 +90,7 @@
8990
$max: breakpoint-max($upper, $breakpoints);
9091

9192
@if $min != null and $max != null {
92-
@media (min-width: $min) and (max-width: $max) {
93+
@media (width >= $min) and (width < $max) {
9394
@content;
9495
}
9596
} @else if $max == null {
@@ -112,7 +113,7 @@
112113
$max: breakpoint-max($next, $breakpoints);
113114

114115
@if $min != null and $max != null {
115-
@media (min-width: $min) and (max-width: $max) {
116+
@media (width >= $min) and (width < $max) {
116117
@content;
117118
}
118119
} @else if $max == null {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
@import 'true';
2+
@import '../../mixins/breakpoints';
3+
4+
// Test breakpoint functions and mixins for range media query syntax
5+
6+
@include test-module('Breakpoint Functions') {
7+
$test-breakpoints: (
8+
xs: 0,
9+
sm: 576px,
10+
md: 768px,
11+
lg: 992px,
12+
xl: 1200px,
13+
xxl: 1400px
14+
);
15+
16+
@include test('breakpoint-max with range syntax') {
17+
@include assert-equal(breakpoint-max(xs, $test-breakpoints), null);
18+
@include assert-equal(breakpoint-max(sm, $test-breakpoints), 576px);
19+
@include assert-equal(breakpoint-max(md, $test-breakpoints), 768px);
20+
@include assert-equal(breakpoint-max(lg, $test-breakpoints), 992px);
21+
@include assert-equal(breakpoint-max(xl, $test-breakpoints), 1200px);
22+
@include assert-equal(breakpoint-max(xxl, $test-breakpoints), 1400px);
23+
}
24+
}
25+
26+
@include test-module('Media Query Mixins - Range Syntax') {
27+
$test-breakpoints: (
28+
xs: 0,
29+
sm: 576px,
30+
md: 768px,
31+
lg: 992px,
32+
xl: 1200px,
33+
xxl: 1400px
34+
);
35+
36+
@include test('media-breakpoint-up generates range syntax') {
37+
@include assert {
38+
@include output {
39+
@include media-breakpoint-up(sm, $test-breakpoints) {
40+
.test { color: red; }
41+
}
42+
}
43+
@include expect {
44+
@media (width >= 576px) {
45+
.test { color: red; }
46+
}
47+
}
48+
}
49+
}
50+
51+
@include test('media-breakpoint-down generates range syntax') {
52+
@include assert {
53+
@include output {
54+
@include media-breakpoint-down(md, $test-breakpoints) {
55+
.test { color: blue; }
56+
}
57+
}
58+
@include expect {
59+
@media (width < 768px) {
60+
.test { color: blue; }
61+
}
62+
}
63+
}
64+
}
65+
66+
@include test('media-breakpoint-between generates range syntax') {
67+
@include assert {
68+
@include output {
69+
@include media-breakpoint-between(sm, lg, $test-breakpoints) {
70+
.test { color: green; }
71+
}
72+
}
73+
@include expect {
74+
@media (width >= 576px) and (width < 992px) {
75+
.test { color: green; }
76+
}
77+
}
78+
}
79+
}
80+
81+
@include test('media-breakpoint-only generates range syntax') {
82+
@include assert {
83+
@include output {
84+
@include media-breakpoint-only(md, $test-breakpoints) {
85+
.test { color: yellow; }
86+
}
87+
}
88+
@include expect {
89+
@media (width >= 768px) and (width < 992px) {
90+
.test { color: yellow; }
91+
}
92+
}
93+
}
94+
}
95+
}

scss/tests/utilities/_api.test.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ $utilities: ();
5151
font-size: 1.25rem !important;
5252
}
5353

54-
@media (min-width: 333px) {
54+
@media (width >= 333px) {
5555
.padding-sm-1rem {
5656
padding: 1rem !important;
5757
}
5858
}
5959

60-
@media (min-width: 666px) {
60+
@media (width >= 666px) {
6161
.padding-md-1rem {
6262
padding: 1rem !important;
6363
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
**Why subtract .02px?** Browsers don’t currently support [range context queries](https://www.w3.org/TR/mediaqueries-4/#range-context), so we work around the limitations of [`min-` and `max-` prefixes](https://www.w3.org/TR/mediaqueries-4/#mq-min-max) and viewports with fractional widths (which can occur under certain conditions on high-dpi devices, for instance) by using values with higher precision.
1+
**Using range context queries:** Bootstrap now uses [range context queries](https://www.w3.org/TR/mediaqueries-4/#range-context) for improved precision and modern CSS media query syntax. The range syntax like `(width >= 768px)` eliminates the need for fractional pixel adjustments that were previously required with `min-` and `max-` prefixes.

site/src/content/docs/layout/breakpoints.mdx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,19 @@ These Sass mixins translate in our compiled CSS using the values declared in our
7575
// No media query for `xs` since this is the default in Bootstrap
7676

7777
// Small devices (landscape phones, 576px and up)
78-
@media (min-width: 576px) { ... }
78+
@media (width >= 576px) { ... }
7979

8080
// Medium devices (tablets, 768px and up)
81-
@media (min-width: 768px) { ... }
81+
@media (width >= 768px) { ... }
8282

8383
// Large devices (desktops, 992px and up)
84-
@media (min-width: 992px) { ... }
84+
@media (width >= 992px) { ... }
8585

8686
// X-Large devices (large desktops, 1200px and up)
87-
@media (min-width: 1200px) { ... }
87+
@media (width >= 1200px) { ... }
8888

8989
// XX-Large devices (larger desktops, 1400px and up)
90-
@media (min-width: 1400px) { ... }
90+
@media (width >= 1400px) { ... }
9191
```
9292

9393
### Max-width
@@ -110,26 +110,26 @@ We occasionally use media queries that go in the other direction (the given scre
110110
}
111111
```
112112

113-
These mixins take those declared breakpoints, subtract `.02px` from them, and use them as our `max-width` values. For example:
113+
These mixins use the breakpoint values to create `max-width` media queries using modern range syntax. For example:
114114

115115
```scss
116116
// `xs` returns only a ruleset and no media query
117117
// ... { ... }
118118

119119
// `sm` applies to x-small devices (portrait phones, less than 576px)
120-
@media (max-width: 575.98px) { ... }
120+
@media (width < 576px) { ... }
121121

122122
// `md` applies to small devices (landscape phones, less than 768px)
123-
@media (max-width: 767.98px) { ... }
123+
@media (width < 768px) { ... }
124124

125125
// `lg` applies to medium devices (tablets, less than 992px)
126-
@media (max-width: 991.98px) { ... }
126+
@media (width < 992px) { ... }
127127

128128
// `xl` applies to large devices (desktops, less than 1200px)
129-
@media (max-width: 1199.98px) { ... }
129+
@media (width < 1200px) { ... }
130130

131131
// `xxl` applies to x-large devices (large desktops, less than 1400px)
132-
@media (max-width: 1399.98px) { ... }
132+
@media (width < 1400px) { ... }
133133
```
134134

135135
<Callout name="info-mediaqueries-breakpoints" type="warning" />
@@ -150,7 +150,7 @@ There are also media queries and mixins for targeting a single segment of screen
150150
For example the `@include media-breakpoint-only(md) { ... }` will result in :
151151

152152
```scss
153-
@media (min-width: 768px) and (max-width: 991.98px) { ... }
153+
@media (width >= 768px) and (width < 992px) { ... }
154154
```
155155

156156
### Between breakpoints
@@ -166,5 +166,5 @@ Which results in:
166166
```scss
167167
// Example
168168
// Apply styles starting from medium devices and up to extra large devices
169-
@media (min-width: 768px) and (max-width: 1199.98px) { ... }
169+
@media (width >= 768px) and (width < 1200px) { ... }
170170
```

0 commit comments

Comments
 (0)