Media Query Grouping Instead of Multiple Scattered Media Queries That Match

Media Query grouping instead of multiple scattered media queries that match

First, your solution given in the question certainly has some usefulness to it.

One thing I thought, however, was that it would be nice to define all the media query variables "near" one another (your solution would have them under each media query call). So I propose the following as an alternative solution. It also has drawbacks, one being perhaps a bit more coding up front.

LESS Code

//define our break points as variables
@mediaBreak1: 800px;
@mediaBreak2: 1024px;
@mediaBreak3: 1280px;

//this mixin builds the entire media query based on the break number
.buildMediaQuery(@min) {
@media only screen and (min-width: @min) {

//define a variable output mixin for a class included in the query
.myClass1(@color) {
.myClass1 {
color: @color;
}
}

//define a builder guarded mixin for each break point of the query
//in these is where we change the variable for the media break (here, color)
.buildMyClass1() when (@min = @mediaBreak1) {
.myClass1(red);
}
.buildMyClass1() when (@min = @mediaBreak2) {
.myClass1(green);
}
.buildMyClass1() when (@min = @mediaBreak3) {
.myClass1(blue);
}

//call the builder mixin
.buildMyClass1();

//define a variable output mixin for a nested selector included in the query
.mySelector1(@fontSize) {
section {
width: (@min - 40);
margin: 0 auto;
a {
font-size: @fontSize;
}
}
}

//Again, define a builder guarded mixin for each break point of the query
//in these is where we change the variable for the media break (here, font-size)
.buildMySelector1() when (@min = @mediaBreak1) {
.mySelector1(10px);
}
.buildMySelector1() when (@min = @mediaBreak2) {
.mySelector1(12px);
}
.buildMySelector1() when (@min = @mediaBreak3) {
.mySelector1(14px);
}

//call the builder mixin
.buildMySelector1();

//ect., ect., etc. for as many parts needed in the media queries.
}
}

//call our code to build the queries
.buildMediaQuery(@mediaBreak1);
.buildMediaQuery(@mediaBreak2);
.buildMediaQuery(@mediaBreak3);

CSS Output

@media only screen and (min-width: 800px) {
.myClass1 {
color: #ff0000;
}
section {
width: 760px;
margin: 0 auto;
}
section a {
font-size: 10px;
}
}
@media only screen and (min-width: 1024px) {
.myClass1 {
color: #008000;
}
section {
width: 984px;
margin: 0 auto;
}
section a {
font-size: 12px;
}
}
@media only screen and (min-width: 1280px) {
.myClass1 {
color: #0000ff;
}
section {
width: 1240px;
margin: 0 auto;
}
section a {
font-size: 14px;
}
}

Fancy Media Queries with some LESS Magic

Here is what I've done in my projects:

@desktop:   ~"only screen and (min-width: 960px) and (max-width: 1199px)";
@tablet: ~"only screen and (min-width: 720px) and (max-width: 959px)";

@media @desktop {
footer {
width: 940px;
}
}

@media @tablet {
footer {
width: 768px;
}
}

This allows you to only define your media queries once and you can use it throughout your less files. Also a little easier to read. :)

Combine matching media queries?

The LESS team opted against combining multiple matching media queries. You can read about it in this issue on Github: https://github.com/less/less.ruby/issues/196

I think you need to look for a CSS tool, to combine the rules, as the LESS compiler wont be supporting it in the near future (if ever).

The Grunt tool you found, also parses the compiled CSS to combine the media queries:
https://github.com/buildingblocks/grunt-combine-media-queries/blob/master/tasks/combine_media_queries.js

Consider using Grunt to build your LESS.

Using class set in media query as mixin in less

Your problem is a common misconception. LESS does not process the @media query, the browser does after LESS has done its work. LESS can only create the CSS code that the browser is going to read. So the @media is "meaningless" to LESS, it is just like any other selector (.someClass div table, etc.), it only processes what the @media is going to serve to the browser.

So that means you need to put all your code that changes for the @media in the @media block. But you also don't want a bunch of repeated code. So instead, create a master mixin to set your @media code, and then call that mixin from the media queries:

.makeFooGroup(@w1, @w2) {
.foo {width: @w1}
.foo-2{width: @w2}
.bar{.foo}
.bar-2{.foo}
.bar-3{.foo-2}
}

@media (min-width: 1000px) {
.makeFooGroup(120px, 150px);
}
@media (max-width: 999px) {
.makeFooGroup(110px, 120px);
}

Produces this css:

@media (min-width: 1000px) {
.foo {width: 120px;}
.foo-2 {width: 150px;}
.bar {width: 120px;}
.bar-2 {width: 120px;}
.bar-3 {width: 150px;}
}
@media (max-width: 999px) {
.foo {width: 110px;}
.foo-2 {width: 120px;}
.bar {width: 110px;}
.bar-2 {width: 110px;}
.bar-3 {width: 120px;}
}

For some further info I've given on LESS and @media related to this, see:

  1. CSS pre-processor with a possibility to define variables in a @media query
  2. Media Query grouping instead of multiple scattered media queries that match

css, change less variable with @media

Since I don't seem to be getting a lot of support for closing this as a duplicate of this question, I'll essentially repeat my answer (with some variety).

Remember @media is a CSS query, not a LESS query

The @media query is a CSS capability that allows the currently loaded CSS to respond to the media it is being presented on (typically some type of browser, but could be print, etc.).

LESS is a CSS preprocessor that creates CSS before it is loaded into the browser, and thus before the media is ever being checked (which is done in the CSS itself, after it has been generated by LESS).

So the proper method for LESS is to have the same type of output as straight CSS, which means you have to repeat the .menu selector in the @media query so that its value changes for the media type.

Final CSS Output Should Be Something Like

@media only screen and (max-width: 400px) {
.menu {
width: 100px;
}
}

.menu {
width: 300px;
}

There are various ways to generate something like that with LESS. Strictly taking your basic format above, there is this:

@menu-width: 300px; // default size

@media only screen and (max-width: 400px) {
.menu {
width: @menu-width - 200px; /* assuming you want it dynamic to default */
}
}

.menu {
width: @menu-width;
}

But also look at:

  • For the basic idea of LESS media queries: How can I use media queries more efficiently with LESS?
  • For discussion of getting media queries grouped and less repetition of selector code, see: Media Query grouping instead of multiple scattered media queries that match

LESS, Media Queries, and Performance

Its almost impossible to give you a totally accurate answer.

When asking about performance the typical answer is to profile it and see what you get. Fix the slowest part first, then repeat.

You can profile CSS selectors in the Chrome Developer tools:

Sample Image

Ultimately I don't think the media queries will have anywhere near as much impact on performance compared to even a slightly complicated selector.

You can see more information about writing efficient CSS here:

https://developers.google.com/speed/docs/best-practices/rendering

Also with regards to file size and repeated CSS, gzip almost completely nullifies this as the gzip algorithm works best with repeated data.

CSS pre-processor with a possibility to define variables in a @media query

Let me answer more directly the question:

"Is it possible to achieve this in any other CSS pre-processor, or we
are doomed to override the .myElement within each media query?"

The answer actually resides in the question. Because LESS (and other similar tools) is a "pre-processor," the @media means nothing to it at compilation time, because it is not looking at the media state of the browser for its preprocessing. The @media state is only relevant after the preprocessing, at which point any @someVariable has already been calculated. This is why what you are trying to do cannot work. Your @myVar can only be output as a single value in a CSS style sheet, and that output occurs before the browser state is ever evaluated by @media.

So it does not have to do with the "global" or "local" scope of a media query, but the fact that LESS compiles the code into CSS utilizing the variables, and it is the CSS (not LESS) that pays attention to the @media query information.

For some further discussion on building media queries with LESS in such a way that they are all together and not scattered throughout the code, see this question.

SASS Placeholder for media query?

Sass does not have that functionality at this time. Your only option is to manually group your styles within a single media query (or use a 3rd party CSS compressor that has that functionality).

https://github.com/nex3/sass/issues/116



Related Topics



Leave a reply



Submit