Ricardo Zea PRO
Sr. Web Designer
By: Ricardo Zea - Web Designer
July 5th, 2016
DublinCSS Meetup
Media Queries Mayhem: Sass, I'm looking at you!
Is a CSS3 module allowing content rendering to adapt to [...] screen resolution[s] (e.g. smartphone screen vs. computer screen).
Media Queries Mayhem: Sass, I'm looking at you!
@media (min-width: 40em) {
.selector {
width: 100%;
}
}
Media Queries Mayhem: Sass, I'm looking at you!
@media (min-width: 40em) {
.selector {
width: 100%;
}
}
.selector {
width: 50%;
}
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
header {
width: 50%;
}
.
.
.
/*=== Media Queries ===*/
/*640*/
@media (min-width: 40em) {
header {
width: 75%;
}
}
/*800*/
@media (min-width: 50em) {
header {
width: 100%;
padding-bottom: 10px;
border-bottom: #999 1px dotted;
}
}
Media Queries Mayhem: Sass, I'm looking at you!
// Define some screen sizes for media queries
$phone: 320px;
$phone-wide: 500px;
$tablet: 600px;
$large-screen: 992px;
@mixin at-least($device-width) {
@media screen and (min-width: $device-width) {
@content
}
}
@mixin until($device-width) {
@media screen and (max-width: $device-width - 1) {
@content
}
}
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
// Define some screen sizes for media queries
$phone: 320px;
$phone-wide: 500px;
$tablet: 600px;
$large-screen: 992px;
Let's focus on the screen sizes list for a minute...
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
// Define some screen sizes for media queries
$phone: 320px;
$phone-pseudo-wide: 400px;
$phone-wide: 500px;
$tablet: 600px;
$large-screen: 992px;
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
// Define some screen sizes for media queries
$phone: 320px;
$phone-pseudo-wide: 400px;
$phone-wide: 500px;
$tablet: 600px;
$tablet-pseudo-wide: 640px;
$large-screen: 992px;
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
// Define some screen sizes for media queries
$phone: 320px;
$phone-pseudo-wide: 400px;
$phone-wide: 500px;
$tablet: 600px;
$tablet-pseudo-wide: 640px;
$tablet-semi-wide: 700px;
$large-screen: 992px;
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
// Define some screen sizes for media queries
$phone: 320px;
$phone-pseudo-wide: 400px;
$phone-wide: 500px;
$tablet: 600px;
$tablet-pseudo-wide: 640px;
$tablet-semi-wide: 700px;
$tablet-like-ipad: 768px;
$some-wider-tablet: 800px;
$good-luck-width: 860px;
$better-luck-width: 900px;
$good-'ol-960gs-width: 960px;
$i-prefer-980gs-width: 980px;
$large-screen: 992px;
$no-more-17-screens: 1024px;
$media-queries-mayhem: 1140px;
Things are starting to look ugly, eh?
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Usage
.selector {
@include at-least($phone-wide) {
width: 50%;
}
}
//Usage
.selector {
@include at-least($tablet) {
width: 50%;
}
}
//Usage
.selector {
@include at-least($media-queries-mayhem) {
width: 50%;
}
}
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
The "Screen Size Based" Style #1
Media Queries Mayhem: Sass, I'm looking at you!
The "Named Media Queries" Style:
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
}
Media Queries Mayhem: Sass, I'm looking at you!
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == papa-bear-chair {
@media (max-width: 1400px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == papa-bear-chair {
@media (max-width: 1400px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == mama-bear-chair {
@media (max-width: 1180px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == papa-bear-chair {
@media (max-width: 1400px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == mama-bear-chair {
@media (max-width: 1180px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
@else if $point == baby-bear-chair {
@media (max-width: 500px) { @content; }
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == papa-bear-chair {
@media (max-width: 1400px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == mama-bear-chair {
@media (max-width: 1180px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
@else if $point == baby-bear-chair {
@media (max-width: 500px) { @content; }
}
@else if $point == goldilocks {
@media (max-width: 420px) { @content; }
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == papa-bear-chair {
@media (max-width: 1400px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == mama-bear-chair {
@media (max-width: 1180px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
@else if $point == baby-bear-chair {
@media (max-width: 500px) { @content; }
}
@else if $point == goldilocks {
@media (max-width: 420px) { @content; }
}
@else if $point == media-queries-mayhem {
@media (max-width: 320px) { @content; }
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == papa-bear-chair {
@media (max-width: 1400px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == mama-bear-chair {
@media (max-width: 1180px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
@else if $point == baby-bear-chair {
@media (max-width: 500px) { @content; }
}
@else if $point == goldilocks {
@media (max-width: 420px) { @content; }
}
@else if $point == media-queries-mayhem {
@media (max-width: 320px) { @content; }
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
papa-bear
papa-bear-chair
mama-bear
mama-bear-chair
baby-bear
baby-bear-chair
goldilocks
media-queries-mayhem
Starbucks
short
tall
grande
venti
GoT
eddard
catelyn
robb
sansa
arya
bran
rickon
LOTR
gollum
bilbo
gimly
aragorn
gandalf
balrog
Heavy Metal
fucking-small
small
medium
large
fucking-large
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
//Usage
.selector {
@include breakpoint(baby-bear) {
width: 50%;
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Usage
.selector {
@include breakpoint(baby-bear) {
width: 50%;
}
}
//Usage
.selector {
@include breakpoint(mama-bear-chair) {
width: 50%;
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Usage
.selector {
@include breakpoint(baby-bear) {
width: 50%;
}
}
//Usage
.selector {
@include breakpoint(mama-bear-chair) {
width: 50%;
}
}
//Usage
.selector {
@include breakpoint(goldilocks) {
width: 50%;
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Chris' style
@mixin breakpoint($point) {
@if $point == papa-bear {
@media (max-width: 1600px) { @content; }
}
@else if $point == mama-bear {
@media (max-width: 1250px) { @content; }
}
@else if $point == baby-bear {
@media (max-width: 650px) { @content; }
}
}
//Ricardo's adaption for "Desktop-first" method
@mixin maxw($point) {
@if $point == 1140 {
@media (max-width: 1140px) { @content; }
}
@else if $point == 1024 {
@media (max-width: 1024px) { @content; }
}
@else if $point == 980 {
@media (max-width: 980px) { @content; }
}
}
Media Queries Mayhem: Sass, I'm looking at you!
//Ricardo's adaptation for "Desktop-first" method
@mixin maxw($point) {
@if $point == 1140 {
@media (max-width: 1140px) { @content; }
}
@else if $point == 1024 {
@media (max-width: 1024px) { @content; }
}
@else if $point == 980 {
@media (max-width: 980px) { @content; }
}
}
//Ricardo's adaptation for "Mobile-first" method
@mixin minw($point) {
@if $point == 980 {
@media (min-width: 980px) { @content; }
}
@else if $point == 1024 {
@media (min-width: 1024px) { @content; }
}
@else if $point == 1140 {
@media (min-width: 1140px) { @content; }
}
}
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Usage - Ricardo's version
.selector {
@include minw(980) {
width: 50%;
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Usage - Ricardo's version
.selector {
@include minw(980) {
width: 50%;
}
}
//Usage - Ricardo's version
.selector {
@include minw(1024) {
width: 75%;
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Usage - Ricardo's version
.selector {
@include minw(980) {
width: 50%;
}
}
//Usage - Ricardo's version
.selector {
@include minw(1024) {
width: 75%;
}
}
//Usage - Ricardo's version
.selector {
@include minw(1140) {
width: 100%;
}
}
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
The "Named Media Queries" Style:
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
//Desktop-first
@mixin maxw($point) {
@if $point == 1140 {
@media (max-width: 76.125em) { @content; }
}
@else if $point == 1000 {
@media (max-width: 62.5em) { @content; }
}
@else if $point == 960 {
@media (max-width: 60em) { @content; }
}
@else if $point == 900 {
@media (max-width: 56.25em) { @content; }
}
@else if $point == 800 {
@media (max-width: 50em) { @content; }
}
@else if $point == 768 {
@media (max-width: 48em) { @content; }
}
@else if $point == 700 {
@media (max-width: 43.75em) { @content; }
}
@else if $point == 640 {
@media (max-width: 40em) { @content; }
}
@else if $point == 600{
@media (max-width: 37.5em) { @content; }
}
@else if $point == 580 {
@media (max-width: 36.25em) { @content; }
}
@else if $point == 520 {
@media (max-width: 32.5em) { @content; }
}
@else if $point == 480 {
@media (max-width: 30em) { @content; }
}
@else if $point == 400 {
@media (max-width: 25em) { @content; }
}
@else if $point == 320 {
@media (max-width: 20em) { @content; }
}
}
//Mobile-first
@mixin minw($point) {
@if $point == 321 {
@media (min-width: 20.06em) { @content; }
}
@else if $point == 401 {
@media (min-width: 25.06em) { @content; }
}
@else if $point == 481 {
@media (min-width: 30.06em) { @content; }
}
@else if $point == 521 {
@media (min-width: 32.56em) { @content; }
}
@else if $point == 581 {
@media (min-width: 36.31em) { @content; }
}
@else if $point == 601 {
@media (min-width: 37.56em) { @content; }
}
@else if $point == 641 {
@media (min-width: 40.06em) { @content; }
}
@else if $point == 701 {
@media (min-width: 43.81em) { @content; }
}
@else if $point == 769 {
@media (min-width: 48.06em) { @content; }
}
@else if $point == 801 {
@media (min-width: 50.06em) { @content; }
}
@else if $point == 901 {
@media (min-width: 56.31em) { @content; }
}
@else if $point == 961 {
@media (min-width: 60.06em) { @content; }
}
@else if $point == 1001 {
@media (min-width: 62.56em) { @content; }
}
@else if $point == 1141 {
@media (min-width: 71.31em) { @content; }
}
}
Not DRY and ugly, isn't it?
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
$breakpoints: (phone: 480px, tablet: 768px, desktop: 1024px) !default;
$media-expressions: (screen: "screen",
print: "print",
retina2x: ("(-webkit-min-device-pixel-ratio: 2)", "(min-resolution: 192dpi)"),
retina3x: ("(-webkit-min-device-pixel-ratio: 3)", "(min-resolution: 350dpi)")
) !default;
@mixin media($conditions...) {
$i: 1;
@each $condition in $conditions {
$conditions: set-nth($conditions, $i, parse-expression(nth($conditions, $i)));
$i: $i + 1;
}
$branches: get-query-branches($conditions);
$query: "";
@each $branch in $branches {
@if ($query != "") {
$query: $query + ", ";
}
$query: $query + $branch;
}
@media #{$query} { @content; }
}
@function get-query-branches($expressions) {
$result: "";
$has-groups: false;
// Getting initial snapshot and looking for groups
@each $expression in $expressions {
@if ($result != "") {
$result: $result + " and ";
}
@if (type-of($expression) == "string") {
$result: $result + $expression;
} @else if (type-of($expression) == "list") {
$result: $result + nth($expression, 1);
$has-groups: true;
}
}
// If we have groups, we have to create all possible combinations
@if $has-groups {
@each $expression in $expressions {
@if (type-of($expression) == "list") {
$first: nth($expression, 1);
@each $member in $expression {
@if ($member != $first) {
@each $partial in $result {
$result: join($result, str-replace-first($first, $member, $partial));
}
}
}
}
}
}
@return $result;
}
@function parse-expression($expression) {
$operator: "";
$value: "";
$element: "";
$result: "";
$is-width: true;
// Separating the operator from the rest of the expression
@if (str-slice($expression, 2, 2) == "=") {
$operator: str-slice($expression, 1, 2);
$value: str-slice($expression, 3);
} @else {
$operator: str-slice($expression, 1, 1);
$value: str-slice($expression, 2);
}
// Checking what type of expression we're dealing with
@if map-has-key($breakpoints, $value) {
$result: map-get($breakpoints, $value);
} @else if map-has-key($media-expressions, $expression) {
$result: map-get($media-expressions, $expression);
$is-width: false;
} @else {
$result: to-number($value);
}
@if ($is-width) {
@if ($operator == ">") {
$element: "(min-width: #{$result + 1})";
} @else if ($operator == "<") {
$element: "(max-width: #{$result - 1})";
} @else if ($operator == ">=") {
$element: "(min-width: #{$result})";
} @else if ($operator == "<=") {
$element: "(max-width: #{$result})";
}
} @else {
$element: $result;
}
@return $element;
}
@function str-replace-first($search, $replace, $subject) {
$search-start: str-index($subject, $search);
@if not $search-start {
@return $subject;
}
$result: str-slice($subject, 0, $search-start - 1);
$result: $result + $replace;
$result: $result + str-slice($subject, $search-start + str-length($search));
@return $result;
}
@function _length($number, $unit) {
$strings: 'px' 'cm' 'mm' '%' 'ch' 'pica' 'in' 'em' 'rem' 'pt' 'pc' 'ex' 'vw' 'vh' 'vmin' 'vmax';
$units: 1px 1cm 1mm 1% 1ch 1pica 1in 1em 1rem 1pt 1pc 1ex 1vw 1vh 1vmin 1vmax;
$index: index($strings, $unit);
@if not $index {
@warn "Unknown unit `#{$unit}`.";
@return false;
}
@return $number * nth($units, $index);
}
@function to-number($string) {
// Matrices
$strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
$numbers: 0 1 2 3 4 5 6 7 8 9;
// Result
$result: 0;
$divider: 0;
$minus: false;
// Looping through all characters
@for $i from 1 through str-length($string) {
$character: str-slice($string, $i, $i);
$index: index($strings, $character);
@if $character == '-' {
$minus: true;
}
@else if $character == '.' {
$divider: 1;
}
@else {
@if not $index {
$result: if($minus, $result * -1, $result);
@return _length($result, str-slice($string, $i));
}
$number: nth($numbers, $index);
@if $divider == 0 {
$result: $result * 10;
}
@else {
// Move the decimal dot to the left
$divider: $divider * 10;
$number: $number / $divider;
}
$result: $result + $number;
}
}
@return if($minus, $result * -1, $result);
}
/**
*
* Testing
*
**/
div {
@include media("<=phone") {
background-color: #def;
&:before {
content: "<=phone";
}
}
@include media(">phone") {
background-color: #abc;
&:before {
content: ">phone";
}
}
@include media(">=815px", "<desktop") {
background-color: #fed;
&:before {
content: ">=815px, <desktop";
}
}
@include media(">=desktop") {
background-color: #fab;
&:before {
content: ">=desktop";
}
}
@include media("retina2x") {
&:after {
content: "Retina" !important;
}
}
}
The "Screen Size Based" Style #2
Media Queries Mayhem: Sass, I'm looking at you!
The Screen Size Based Style #2
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
@media (min-width: 640px) {
.selector {
width: 100%;
}
}
Media Queries Mayhem: Sass, I'm looking at you!
Simple.
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
Because you're saying "So Long!" to those other mixins we saw before :)
@mixin goingLarge($width) {
@media (min-width: $width){ @content; }
}
Media Queries Mayhem: Sass, I'm looking at you!
The "Hasta Luego" Style:
A mere 3 line mixin:
Media Queries Mayhem: Sass, I'm looking at you!
.selector {
//…Mobile stuff
@include goingLarge(600px) { //Content-based breakpoint
//Desktop stuff
}
}
Mixin:
@mixin goingLarge($width) {
@media (min-width: $width){ @content; }
}
//Mobile-first Mixin
@mixin goingLarge($width) {
@media (min-width: $width){ @content; }
}
//Example:
header {
width: 100%;
@include goingLarge(487px) { //Content-based breakpoint
width: 50%;
}
}
//Compiles to:
header {
width: 100%;
}
@media (min-width: 487px){
header {
width: 50%;
}
}
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
The "Hasta Luego" Style
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
The "Hasta Luego" Style
//Desktop-first Mixin
@mixin goingSmall($width) {
@media (max-width: $width){ @content; }
}
//Example:
header {
width: 50%;
@include goingSmall(725px) { //Content-based breakpoint
width: 100%;
}
}
//Compiles to:
header {
width: 50%;
}
@media (max-width: 725px){
header {
width: 100%;
}
}
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
Media Queries Mayhem: Sass, I'm looking at you!
“Life’s most persistent and urgent question is:
What are you doing for others?”
— Dr. Martin Luther King Jr.
Sass, I'm Looking at You!
Ramón, 1994 - 2004
By: Ricardo Zea - Web Designer
July 5th, 2016
DublinCSS Meetup
Media Queries Mayhem: Sass, I'm looking at you!
//Ricardo's adaptation for "Desktop-first" method
@mixin maxw($point) {
@if $point == 1140 {
@media (max-width: 71.25em) { @content; }
}
@else if $point == 1024 {
@media (max-width: 64em) { @content; }
}
@else if $point == 980 {
@media (max-width: 61.25em) { @content; }
}
}
//Ricardo's adaptation for "Mobile-first" method
@mixin minw($point) {
@if $point == 980 {
@media (min-width: 61.25em) { @content; }
}
@else if $point == 1024 {
@media (min-width: 64em) { @content; }
}
@else if $point == 1140 {
@media (min-width: 71.25em) { @content; }
}
}
Formula: width(px) / font base(px) = em value
Example: 1024px / 16px = 64em
By Ricardo Zea
I've seen amazingly clever Sass mixins created to handle CSS media queries, but I think that some of these solutions are overkill. In this presentation I show you how a mere 3 line Sass mixin can accomplish the same, keeping us focused on what matters most at the end of our day: happy users. -- Presentation for the Dublin CSS meetup (Ireland) - http://www.meetup.com/DublinCSS/