Grouping Media Queries in Stylus - css

Handling media queries with preprocessors is very cool, but I didn't found a way to group same rules to avoid repeated media queries rules like:
Example 1
#media only screen and (max-width: 600px) {
body {
background-color: #f00;
}
}
#media only screen and (max-width: 600px) {
.div1 {
background-color: #0c0;
}
}
#media only screen and (max-width: 600px) {
.div2 {
background-color: #00c;
}
}
I to want to group the same rules into a single one like:
Example 2
#media only screen and (max-width: 600px) {
body {
background-color: #f00;
}
.div1 {
background-color: #0c0;
}
.div2 {
background-color: #00c;
}
}
MY STYLUS CODE
This is how I am handling the media queries in Stylus:
media_queries = {
mobile : "only screen and (max-width: 600px)",
tablet : "only screen and (min-width: 601px) and (max-width: 800px)",
desktop : "only screen and (min-width: 801px)"
}
And I have a function to call the media sizes:
for_breakpoint(breakpoints)
conditions = ()
for breakpoint in breakpoints
push(conditions, media_queries[breakpoint])
conditions = join(", ", conditions)
#media conditions
{block}
After that, I call it inside the rules I want to have a specific media query:
+for_breakpoint(mobile)
.div1
background-color red
But the problem is that i ends having a tons of repeated media queries like the ones on example 1. Is there a way to group them like the example 2?

Use plugin groupCssMediaQueries:
gulp.task('css', function() {
gulp.src('./styl/*.styl')
.pipe(stylus({
use: nib()
}))
.pipe(groupCssMediaQueries())
.pipe(gulp.dest('./public/css/'))
})

Related

Should media query rules coincide at the breaking point or not?

Recently, I find myself doing CSS in a way that I really like. Not mobile first, not desktop first. I just go and do:
Generic properties
Add stuff for different screen sizes with breakpoints that make that specific design look good
So I will do something like:
.polaroid-cards {
display: grid;
}
/* Up until 860px */
#media (max-width: 860px) {
.polaroid-cards {
grid-template-columns: 1fr;
padding: 3rem;
}
}
/* From 860px on */
#media (min-width: 860px) {
.polaroid-cards {
grid-template-columns: 1fr 1fr;
padding: 3rem 15%;
}
}
And those rules are specific for that component. Other components may break at lower sizes or break three times, whatever is needed to make them look good.
Yeah well that was to give some context.
But the question I have is, regarding:
#media (max-width: 860px) { ...
#media (min-width: 860px) { ...
Is that okay?
Should it be?
#media (max-width: 859px) { ...
#media (min-width: 860px) { ...
I of course tested both versions and both work fine (apparently), but I want to understand the math behind this, and what internal rules the browser is applying, so I "help the browser" or at least don't cause unexpected bugs.
min- and max-width are both inclusive, i.e. min-width: 860px means any screen that is 860px wide or wider. This means that
#media (max-width: 860px) { ...
#media (min-width: 860px) { ...
do overlap and the usual css precedence rules determine which to choose at a screen of width 860px exactly. So if you want to be absolutely, totally sure which rule will apply when, one should use 859px (or 861px).
Luckily, the Media Queries Level 4 spec, which is beginning to roll out to browsers, enables using regular comparison operators, making this cleaner and more obvious. You can then write
#media (width < 860px) { ...
#media (width >= 860px) { ...
And for three breakpoints, you can even do
#media (width < 860px) { ...
#media (860 <= width < 1080) { ...
#media (width >= 1080) { ...
When CSS media queries overlap, they follow the cascade rule, so in the example you shared (with some addition):
#media (max-width: 860px) { div { color: red; } }
#media (min-width: 860px) { div { color: green; } }
If the viewport is exactly 860px, both media queries will return true, which will be the equivalent of:
{ div { color: red; } }
{ div { color: green; } }
I which case, the second rule takes over
You should give 1px difference.
If you inspect the square below at 1024px, you can see that the green background overrides the red one but only because it's written after the red background, both rules are applied.
But if you check the border, only the orange one is applyed for a width >=1024px.
div{
width:50px;
height:50px;
}
#media (max-width: 1023px) {
div{
border:5px solid blue;
}
}
#media (max-width: 1024px) {
div{
background-color:red;
}
}
#media (min-width: 1024px) {
div{
background-color:green;
border:5px solid orange;
}
}
<div></div>
Also, a good way to set your media queries is to use the default css for the smallest size and set media rules with min-width like the example below :
div{
width:50px;
height:50px;
background-color:blue;
}
#media (min-width: 1024px) {
div{
background-color:orange;
}
}
#media (min-width: 1920px) {
div{
background-color:red;
}
}
<div></div>

#media or expression and max-width

let me say as first I am a CSS noob. I can freely accept any blaming
#media (max-width: 735px) {... }
#media (min-width: 735px) {... }
#media (width: 320px) {... }
#media (width: 360px) {... }
#media (width: 375px) {... }
#media (width: 414px) {... }
I have these things for able to make it sensitive.
I wanted to use max-width but failed not sure why but browser always choose another max-width expression and executes it so I just collected the mobile phone's width and made this.
And also I want to send same content to who has 360px and 320px is there any "or" expression may be I can minify it.
If I got it correct from your question, you can add the following media query.
#media (min-width: 320px) and (max-width: 360px) {
html { color: blue; }
}
I recommended using a different approach, either mobile first or desktop first using media queries. In that way you can simply address the correct layout to a lot of people without writing exceptions.
Mobile first
html { color: purple; }
#media (min-width: 600px) {
html { color: black; }
}
/* And go up the road */
Desktop first
html { color: purple; }
#media (max-width: 600px) {
html { color: black; }
}
/* And go down the road */

Using Sass mixin to set global variable [duplicate]

I'm trying to combine the use of a Sass variable with #media queries as follows:
$base_width:1160px;
#media screen and (max-width: 1170px) {$base_width: 960px;}
#media screen and (min-width: 1171px) {$base_width: 1160px;}
$base_width is then defined at various points in the stylesheet width percentage-based measurements to produce fluid layouts.
When I do this, the variable seems to be recognized properly but the conditions for the media query are not. For example, the above code produces an 1160px layout regardless of screen width. If I flip-flop the #media statements like so:
#media screen and (min-width: 1171px) {$base_width: 1160px;}
#media screen and (max-width: 1170px) {$base_width: 960px;}
It produces a 960px layout, again regardless of screen width. Also note that if I remove the first line of $base_width: 1160px; it returns an error for an undefined variable. Any ideas what I'm missing?
This is simply not possible. Since the trigger #media screen and (max-width: 1170px) happens on the client-side.
Achieving your expected result would only be possible if SASS grabbed all rules and properties in your stylesheet containing your $base_width variable and copied/changed them accordingly.
Since it won't work automatically you could do it by hand like this:
#media screen and (max-width: 1170px)
$base_width: 960px // you need to indent it to (re)set it just within this media-query
// now you copy all the css rules/properties that contain or are relative to $base_width e.g.
#wrapper
width: $base_width
...
#media screen and (min-width: 1171px)
$base_width: 1160px
#wrapper
width: $base_width
...
This is not really DRY but the best you can do.
If the changes are the same every time you could also prepare a mixin containing all the changing values, so you wouldn't need to repeat it. Additionally you can try to combine the mixin with specific changes. Like:
#media screen and (min-width: 1171px)
+base_width_changes(1160px)
#width-1171-specific-element // additional specific changes, that aren't in the mixin
display: block
And the Mixin would look like this
=base_width_changes($base_width)
#wrapper
width: $base_width
Similar to Philipp Zedler's answer, you can do it with a mixin. That lets you have everything in a single file if you want.
#mixin styling($base-width) {
// your SCSS here, e.g.
#Contents {
width: $base-width;
}
}
#media screen and (max-width: 1170px) {
#include styling($base-width: 960px);
}
#media screen and (min-width: 1171px) {
#include styling($base-width: 1160px);
}
This isn't possible with SASS, but it is possible with CSS variables (or CSS custom properties). The only drawback is browser support – but there's actually a PostCSS plugin - postcss-css-variables - that "flattens" the use of CSS variables (which gives you support for older browsers, too).
The following example works great with SASS (and with postcss-css-variables you get support for older browsers too).
SCSS
$mq-laptop: 1440px;
$mq-desktop: 1680px;
:root {
--font-size-regular: 14px;
--gutter: 1rem;
}
// The fact that we have to use a `max-width` media query here, so as to not
// overlap with the next media query, is a quirk of postcss-css-variables
#media (min-width: $mq-laptop) and (max-width: $mq-desktop - 1px) {
:root {
--font-size-regular: 16px;
--gutter: 1.5rem;
}
}
#media (min-width: $mq-desktop) {
:root {
--font-size-regular: 18px;
--gutter: 1.75rem;
}
}
.my-element {
font-size: var(--font-size-regular);
padding: 0 calc(var(--gutter) / 2);
}
This would result in the following CSS. The repetitive media queries will increase the file size, but I have found that the increase is usually negligible once the web server applies gzip (which it will usually do automatically).
CSS
.my-element {
font-size: 14px;
padding: 0 calc(1rem / 2);
}
#media (min-width: 1680px) {
.my-element {
padding: 0 calc(1.75rem / 2);
}
}
#media (min-width: 1440px) and (max-width: 1679px) {
.my-element {
padding: 0 calc(1.5rem / 2);
}
}
#media (min-width: 1680px) {
.my-element {
font-size: 18px;
}
}
#media (min-width: 1440px) and (max-width: 1679px) {
.my-element {
font-size: 16px;
}
}
Edit: Please do not use this solution. The answer by ronen is much better.
As a DRY solution, you can use the #import statement inside a media query, e.g. like this.
#media screen and (max-width: 1170px) {
$base_width: 960px;
#import "responsive_elements";
}
#media screen and (min-width: 1171px) {
$base_width: 1160px;
#import "responsive_elements";
}
You define all responsive elements in the file included using the variables defined in the media query. So, all you need to repeat is the import statement.
With #ronen's great answer and a map, there's some real power available:
#mixin styling($map) {
.myDiv {
background: map-get($map, 'foo');
font-size: map-get($map, 'bar');
}
}
#media (min-height: 500px) {
#include styling((
foo: green,
bar: 50px
));
}
#media (min-height: 1000px) {
#include styling((
foo: red,
bar: 100px
));
}
It's now possible to have lots more DRY media queries targeting .myDiv with a bunch of different values.
Map docs: https://sass-lang.com/documentation/functions/map
Example map usage: https://www.sitepoint.com/using-sass-maps/
I had the same problem.
The $menu-width variable should be 240px on the mobile view #media only screen and (max-width : 768px) and 340px on the desktop view.
So i have simply created two variables:
$menu-width: 340px;
$menu-mobile-width: 240px;
And here is how i have used it:
.menu {
width: $menu-width;
#media only screen and (max-width : 768px) {
width: $menu-mobile-width;
}
}
Two recommendations
1
Write your "default" CSS statements to be for small screens and only use media queries for larger screens. There's usually no need for a max-width media query.
Example (assuming the element has class "container")
#mixin min-width($width) {
#media screen and (max-width: $width) {
#content;
}
}
.container {
width: 960px;
#include min-width(1170px) {
width: 1160px;
}
}
2 Use CSS variables to solve the problem, if you can.
#mixin min-width($width) {
#media screen and (max-width: $width) {
#content;
}
}
:root {
--container-width: 960px;
#include min-width(1170px) {
--container-width: 1160px;
}
}
.container {
width: var(--container-width);
}
Note:
Since it will have the width of 1160px when the window has a width of 1170px, it may be better to use a width of 100% and max-width of 1160px, and the parent element might have a horizontal padding of 5px, as long as the box-sizing property is set to border-box. There are a lot of ways to solve the problem. If the parent is not a flex or grid container you might use .container { margin: auto }.
This is also possible with %placeholders.
%placeholders can be wrapped in media queries. So you could set up multiple variables to use at different screen sizes, and then the placeholders would automagically pre-process accordingly. I'm using some mixins to shorten my media query declarations here also.
In your _vars.scss file:
$width-1: 960px;
$width-2: 1160px;
In your _placeholders.scss file:
%variable-site-width {
#media screen and (max-width: 1170px) { width: $width-1; }
#media screen and (min-width: 1171px) { width: $width-2; }
}
In your page.scss file:
.wrapper. { #extend %variable-site-width; background: red; etc... }
And this will compile to something similar to:
#media screen and (max-width: 1170px) {
.wrapper { width: 960px; }
}
#media screen and (min-width: 1171px) {
.wrapper { width: 1160px; }
}
Voila!
I use this technique extensively for things like variable font sizes and a raft of other things.

LESS: Can you group a CSS selector with a media query?

I really enjoyed finding out you could create a media query variable that you can easily reuse and makes your code much more readable.
#tablet: ~"(min-width: 768px) and (max-width: 980px)";
#media #tablet { ... }
I want to know if it's possible to group a media query with a selector. It doesn't appear to work the way I've implemented it, but I thought I'd ask to see if it's even probable.
#tablet: ~"(min-width: 768px) and (max-width: 980px)";
body {
aside { ... }
&.homepage,
#media #tablet {
aside { ... }
}
}
I understand that media queries are different from run-of-the-mill selectors because you have to define your selectors inside of the media query, but is there some voodoo LESS way to accomplish grouping like this?
I'm not a 100% certain of the output you are going for, but this LESS only defines the color red once, and applies it to both:
#tablet: ~"(min-width: 768px) and (max-width: 980px)";
body {
aside { color: blue }
&.homepage {
aside { color: red }
}
#media #tablet {
.homepage;
}
}
Yields this CSS:
body aside {
color: #0000ff;
}
body.homepage aside {
color: #ff0000;
}
#media (min-width: 768px) and (max-width: 980px) {
body aside {
color: #ff0000;
}
}

How to use media queries in css

I am very keen to use media queries in my CSS but i am confused to how to use it. I have stand
my queries requirement is
if screen width is 100 to 480 then different style comes,
if screen width 481 to 600 then different style comes,
if screen width 601 to 800 then different style comes,
if screen width 801 then default CSS should work.
.class { /* default style */ }
#media (min-width: 100px) and (max-width: 480px) {
.class { /* style */ }
}
#media (min-width: 481px) and (max-width: 600px) {
.class { /* style */ }
}
#media (min-width: 601px) and (max-width: 800px) {
.class { /* style */ }
}
The basic relies on using this kind of queries:
#media (min-width: 768px) and (max-width: 979px) {
/*here goes the exact changes you need to make in order to make
your site pretty on this range.*/
}
Please remember that when using responsive web design you should use percentages when possible also em for fonts.
media queries are now available for IE.
take a look in http://caniuse.com/#feat=css-mediaqueries when you can use them.
A polyfil I been using with good results is response.js
I believe something along the lines of:
#media screen and (min-width: 100px) and (max-width: 480px)
{
/* Change main container '.wrapper' */
.wrapper
{
width: 100px;
}
}
#media screen and (min-width: 480px) and (max-width: 600px)
{
.wrapper
{
width: 480px;
}
}
..etc

Resources