@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map.keys($breakpoints)) {
$n: list.index($breakpoint-names, $name);
@if not $n {
- @error "breakpoint `#{$name}` not found in `#{$breakpoints}`";
+ @error "breakpoint `#{$name}` not found in `#{$breakpoint-names}`";
}
@return if($n < list.length($breakpoint-names), list.nth($breakpoint-names, $n + 1), null);
}
@return if($min != 0, $min, null);
}
-// Maximum breakpoint width.
-// The maximum value is reduced by 0.02px to work around the limitations of
-// `min-` and `max-` prefixes and viewports with fractional widths.
-// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max
-// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.
-// See https://bugs.webkit.org/show_bug.cgi?id=178261
+// Maximum breakpoint width for range media queries.
+// Returns the breakpoint value to use as an upper bound in range queries.
//
-// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px))
-// 767.98px
+// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px))
+// 576px
+// >> breakpoint-max(xxl, (xs: 0, sm: 576px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px))
+// null
@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
+ @if $name == null {
+ @return null;
+ }
$max: map.get($breakpoints, $name);
- @return if($max and $max > 0, $max - .02, null);
+ @return if($max and $max > 0, $max, null);
}
// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
$min: breakpoint-min($name, $breakpoints);
@if $min {
- @media (min-width: $min) {
+ @media (width >= $min) {
@content;
}
} @else {
@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
$max: breakpoint-max($name, $breakpoints);
@if $max {
- @media (max-width: $max) {
+ @media (width < $max) {
@content;
}
} @else {
$max: breakpoint-max($upper, $breakpoints);
@if $min != null and $max != null {
- @media (min-width: $min) and (max-width: $max) {
+ @media (width >= $min) and (width < $max) {
@content;
}
} @else if $max == null {
$max: breakpoint-max($next, $breakpoints);
@if $min != null and $max != null {
- @media (min-width: $min) and (max-width: $max) {
+ @media (width >= $min) and (width < $max) {
@content;
}
} @else if $max == null {
--- /dev/null
+@import "true";
+@import "../../mixins/breakpoints";
+
+// Test breakpoint functions and mixins for range media query syntax
+
+@include test-module("Breakpoint Functions") {
+ $test-breakpoints: (
+ xs: 0,
+ sm: 576px,
+ md: 768px,
+ lg: 1024px,
+ xl: 1280px,
+ xxl: 1536px
+ );
+
+ @include test("breakpoint-max with range syntax") {
+ @include assert-equal(breakpoint-max(xs, $test-breakpoints), null);
+ @include assert-equal(breakpoint-max(sm, $test-breakpoints), 576px);
+ @include assert-equal(breakpoint-max(md, $test-breakpoints), 768px);
+ @include assert-equal(breakpoint-max(lg, $test-breakpoints), 1024px);
+ @include assert-equal(breakpoint-max(xl, $test-breakpoints), 1280px);
+ @include assert-equal(breakpoint-max(xxl, $test-breakpoints), 1536px);
+ }
+}
+
+@include test-module("Media Query Mixins - Range Syntax") {
+ $test-breakpoints: (
+ xs: 0,
+ sm: 576px,
+ md: 768px,
+ lg: 1024px,
+ xl: 1280px,
+ xxl: 1536px
+ );
+
+ @include test("media-breakpoint-up generates range syntax") {
+ @include assert() {
+ @include output() {
+ @include media-breakpoint-up(sm, $test-breakpoints) {
+ .test { color: #f00; }
+ }
+ }
+ @include expect() {
+ @media (width >= 576px) {
+ .test { color: #f00; }
+ }
+ }
+ }
+ }
+
+ @include test("media-breakpoint-down generates range syntax") {
+ @include assert() {
+ @include output() {
+ @include media-breakpoint-down(md, $test-breakpoints) {
+ .test { color: #00f; }
+ }
+ }
+ @include expect() {
+ @media (width < 768px) {
+ .test { color: #00f; }
+ }
+ }
+ }
+ }
+
+ @include test("media-breakpoint-between generates range syntax") {
+ @include assert() {
+ @include output() {
+ @include media-breakpoint-between(sm, lg, $test-breakpoints) {
+ .test { color: #0f0; }
+ }
+ }
+ @include expect() {
+ @media (width >= 576px) and (width < 1024px) {
+ .test { color: #0f0; }
+ }
+ }
+ }
+ }
+
+ @include test("media-breakpoint-only generates range syntax") {
+ @include assert() {
+ @include output() {
+ @include media-breakpoint-only(md, $test-breakpoints) {
+ .test { color: #ff0; }
+ }
+ }
+ @include expect() {
+ @media (width >= 768px) and (width < 1024px) {
+ .test { color: #ff0; }
+ }
+ }
+ }
+ }
+}
font-size: 1.25rem !important;
}
- @media (min-width: 333px) {
+ @media (width >= 333px) {
.padding-sm-1rem {
padding: 1rem !important;
}
}
- @media (min-width: 666px) {
+ @media (width >= 666px) {
.padding-md-1rem {
padding: 1rem !important;
}
+++ /dev/null
-**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.
// No media query for `xs` since this is the default in Bootstrap
// Small devices (landscape phones, 576px and up)
-@media (min-width: 576px) { ... }
+@media (width >= 576px) { ... }
// Medium devices (tablets, 768px and up)
-@media (min-width: 768px) { ... }
+@media (width >= 768px) { ... }
// Large devices (desktops, 1024px and up)
-@media (min-width: 1024px) { ... }
+@media (width >= 1024px) { ... }
// X-Large devices (large desktops, 1280px and up)
-@media (min-width: 1280px) { ... }
+@media (width >= 1280px) { ... }
// XX-Large devices (larger desktops, 1536px and up)
-@media (min-width: 1536px) { ... }
+@media (width >= 1536px) { ... }
```
### Max-width
}
```
-These mixins take those declared breakpoints, subtract `.02px` from them, and use them as our `max-width` values. For example:
+These mixins use the breakpoint values to create `max-width` media queries using modern range syntax. For example:
```scss
// `xs` returns only a ruleset and no media query
// ... { ... }
// `sm` applies to x-small devices (portrait phones, less than 576px)
-@media (max-width: 575.98px) { ... }
+@media (width < 576px) { ... }
// `md` applies to small devices (landscape phones, less than 768px)
-@media (max-width: 767.98px) { ... }
+@media (width < 768px) { ... }
-// `lg` applies to medium devices (tablets, less than 992px)
-@media (max-width: 991.98px) { ... }
+// `lg` applies to medium devices (tablets, less than 1024px)
+@media (width < 1024px) { ... }
-// `xl` applies to large devices (desktops, less than 1200px)
-@media (max-width: 1199.98px) { ... }
+// `xl` applies to large devices (desktops, less than 1280px)
+@media (width < 1280px) { ... }
-// `2xl` applies to x-large devices (large desktops, less than 1600px)
-@media (max-width: 1599.98px) { ... }
+// `2xl` applies to x-large devices (large desktops, less than 1536px)
+@media (width < 1536px) { ... }
```
-<Callout name="info-mediaqueries-breakpoints" type="warning" />
-
### Single breakpoint
There are also media queries and mixins for targeting a single segment of screen sizes using the minimum and maximum breakpoint widths.
For example the `@include media-breakpoint-only(md) { ... }` will result in :
```scss
-@media (min-width: 768px) and (max-width: 991.98px) { ... }
+@media (width >= 768px) and (width < 992px) { ... }
```
### Between breakpoints
```scss
// Example
// Apply styles starting from medium devices and up to extra large devices
-@media (min-width: 768px) and (max-width: 1199.98px) { ... }
+@media (width >= 768px) and (width < 1200px) { ... }
```