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:
- CSS pre-processor with a possibility to define variables in a @media query
- 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:
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
How to Change the Text Color of First Select Option
When Does CSS's !Important Declaration Not Work
Sass Indented Syntax on Multiple Lines
Float a Div Above Page Content
Apply Different CSS Stylesheet for Different Parts of the Same Web Page
How to Use Vertical Align in Bootstrap
CSS Selector Involving Pseudo Class First-Child and Dropcap
Is the :Before Pseudo-Element Allowed on an Input[Type=Checkbox]
CSS Sticky Footers with Unknown Height
Styling Elements with a Dot (.) in the Class Name
How to Make Sure Select Option Text Align in the Center in Ie
Why Doesn't This CSS :Not() Declaration Filter Down
Convert 8-Digit Hex Colors to Rgba Colors
Cross-Fade Between Images with CSS in Loop