Skip to content

Instantly share code, notes, and snippets.

@killercup
Last active January 30, 2017 12:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save killercup/2394ef0c8f27a78dd267b8bd39d64b90 to your computer and use it in GitHub Desktop.
Save killercup/2394ef0c8f27a78dd267b8bd39d64b90 to your computer and use it in GitHub Desktop.
$breakpoints: (
s: 450px,
m: 600px,
l: 900px,
xl: 1200px
) !default;
@function get-breakpoint($name, $breakpoints: $breakpoints) {
$breakpoint: map-get($breakpoints, $name);
@if $breakpoint == null {
@error "Could not find breakpoint `#{$name}` in `#{$breakpoints}`.";
}
@return $breakpoint;
}
/// Viewport mixin to ergonomically generate media queries
///
/// # Parameters
///
/// - `$query`: The breakpoint constrait for this viewport. This is not
/// formatted like a CSS media query, please see the documentation below.
/// - `$breakpoints`: Optional, map of size names to breakpoints (e.g.,
/// `(phone: 600px, desktop: 1200px)`).
/// - `@content` block.
///
/// ## Query syntax
///
/// The `$query` paramter can be in one of the following forms:
///
/// - `$breakpoint`: Covers everything up to and including `$breakpoint`
/// - `below $breakpoint`: Covers everything up to but not including
/// `$breakpoint`
/// - `above $breakpoint`: Covers everything just above `$breakpoint`
/// - `from $narrow to $wide`: Covers everything from `$narrow` up to but not
/// including `$wide`. (I originally wanted to call this `between $x and $y`
/// but SCSS interprets the `and` as an operator and thus sees this as a tuple
/// with `between` and the result of `$x and $y` in it.)
///
/// # Examples
///
/// ```scss
/// @include viewport(s) { /* */ }
/// @include viewport(above m) { .t3 { content: "above m" } }
/// @include viewport(below xl) { .t4 { content: "below xl" } }
/// @include viewport(from m to l) { .t2 { content: "between m and l" } }
/// ```
@mixin viewport($query, $breakpoints: $breakpoints) {
$parse-error: "Viewport mixin could not parse query `#{$query}`.";
// Usage: `viewport($breakpoint)`
@if length($query) == 1 {
$breakpoint: get-breakpoint($query, $breakpoints);
@media screen and (max-width: $breakpoint) {
@content;
}
}
// Usage: `viewport(above|below $breakpoint)`
@else if length($query) == 2 {
$direction: nth($query, 1);
$breakpoint: get-breakpoint(nth($query, 2), $breakpoints);
@if $direction == "above" {
@media screen and (min-width: $breakpoint) {
@content;
}
} @else if $direction == "below" {
@media screen and (max-width: $breakpoint - 1px) {
@content;
}
} @else {
@error $parse-error;
}
}
// Usage: `viewport(from $narrow to $wide)`
@else if length($query) == 4 {
@if (nth($query, 1) != "from") or (nth($query, 3) != "to") {
@error $parse-error;
}
$narrow: get-breakpoint(nth($query, 2), $breakpoints);
$wide: get-breakpoint(nth($query, 4), $breakpoints);
@media screen and (min-width: $narrow) and (max-width: $wide - 1px) {
@content;
}
} @else {
@error $parse-error;
}
}
// Tests
// - Compile success
@include viewport(s) { .t1 { content: "s" } }
@include viewport(above m) { .t3 { content: "above m" } }
@include viewport(below xl) { .t4 { content: "below xl" } }
@include viewport(from m to l) { .t2 { content: "between m and l" } }
// - Compile fail
// @include viewport() { /* the endless void */ }
// @include viewport(nope) { /* so sad */ }
// @include viewport(what is going on) { /* dafuq */ }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment