12

Currently, I am using LESS as my main CSS pre-processor. I have (and I am sure a lot of people have this need) to define variables in a @media query like:

@media screen and (max-width: 479px) {
    myVar: 10px;
}
@media screen and (min-width: 480px) and (max-width: 767px) {
    myVar: 20px;
}
@media screen and (min-width: 768px) {
    myVar: 30px;
}

.myElement {
    padding: @myVar;
}

This doesn't work in LESS, because of the compiling nature (@myVar is defined within a scope of each particular @media only, but not globally). Even if I define @myVar globally before the media queries, it is not updated in accordance to the media queries and .myElement gets that initial value no matter what.

So, the question is, is it possible to achieve this in any other CSS pre-processor, or we are doomed to override the .myElement within each media query? Not a problem in this simplest example, but in real projects this could save a lot of time and copy/pasting.

EDIT: Not a solution, but a workaround for my particular project:

  1. Set font-size on <html> to base font-size
  2. myVar LESS variable is defined in rem instead of px as a derivative of the base font-size
  3. Use @media queries to adjust base font-size for different media.
  4. OPTIONAL Use REM unit polyfill for browsers, that don't yet support rem (http://caniuse.com/#search=rem). This will not work in IE 8 and below, but not because of lack of support for remit doesn't support media queries. So IE8 and below get full-size media-queries-free stylesheet anyway.

Code snippet:

@myVar: .77rem; // roughly 10px, 20px and 30px respectively

html { font-size: 13px }
@media screen and (min-width: 480px) and (max-width: 767px) {
    html { font-size: 26px }
}
@media screen and (min-width: 768px) {
    html { font-size: 39px }
}

.myElement {
    padding: @myVar;
}

Here is a JSBin with more extensive example that mixes different font-size elements with differently measured blocks.

Easy and flexible for my needs. Hopefully will be helpful for somebody else.

spliter
  • 12,321
  • 4
  • 33
  • 36
  • I don't thinks this is possible inany CSS compiler. The compiler had to generate `@media ... { .myElement { padding: ... } } ` for you. Even though this is quite possible, this would defy control from you. Why don't you just include `.myElement {...}` within your `@media ... { ... }` queries or use mixins (to reduce duplication) which you import within them?. – try-catch-finally Jan 03 '13 at 12:44
  • @try-catch-finally, the problem with repetition is maintenance. If I have quite a few media queries and even more elements, depending on media-query-dependent variables, this is going to be a maintenance nightmare. – spliter Jan 03 '13 at 12:49
  • 1
    If this is an example of the type of things you're doing with media queries, you're the one causing the maintenance nightmare. Different px values for your paddings depending on your browser's width? Use a percentage instead. – cimmanon Jan 03 '13 at 12:52
  • @cimmanon, I appreciate your opinion, but you don't think that I really use 'myVar' and '. myElement' in real code, do you? It would be not very smart I think. If I would give the real example, the question would be unreadable. Thanks for your time. – spliter Jan 03 '13 at 13:12
  • @spliter The names of variables and classes is irrelevant. I can only judge based on what you provide and my point still stands: you are the one causing the maintenance nightmare. If you dumb things down too much, you end up with oversimplified solutions. – cimmanon Jan 03 '13 at 13:23
  • I already have complex solution, @cimmanon. So, I am really looking for a simplified one. – spliter Jan 03 '13 at 13:26
  • possible duplicate of [LESS CSS set variables in media query?](http://stackoverflow.com/questions/10906114/less-css-set-variables-in-media-query) – Flimm Jul 02 '15 at 09:55
  • Defining the font-size on the HTML is a brilliant hack. Thank you. – Dan Dascalescu Jul 09 '15 at 02:33

3 Answers3

10

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.

Community
  • 1
  • 1
ScottS
  • 71,703
  • 13
  • 126
  • 146
  • Thanks for the detailed explanation, @ScottS. I do use the workaround mentioned in the question, but your answer answers the question precisely anyway. Hence I do accept it. – spliter Jan 03 '13 at 17:02
  • "`@media` means nothing to it at compilation time": that's not quite true, LESS performs [bubbling for `@media` queries](http://www.alwaystwisted.com/post.php?s=2012-05-05-everyday-im-bubbling-with-media-queries-and-less). – Flimm Jul 01 '15 at 11:22
  • 1
    @Flimm: True about "bubbling," but the context of my stating "means nothing" is not in reference to how LESS preprocesses the placement of `@media` in the final css, but to the following causal clause with respect to the "media state of the browser" being meaningless to the LESS preprocessing. The browser's media state is not being read by LESS at the time of compilation, so `@media` has no distinct value itself, and therefore cannot be used to assign a variable to use during LESS compilation (as the question asks). – ScottS Jul 01 '15 at 11:59
  • 1
    OK, but the LESS compiler wouldn't need to read the browser's state, it [could be done like this](https://github.com/less/less.js/issues/2205). – Flimm Jul 01 '15 at 12:23
  • 1
    @Flimm: There are many ways the setting of the variable can be done (and I'm sure some will find your link useful). But the question was specifically about setting a variable used _outside_ an `@media` block simply through the `@media` reference doing the setting of that variable, so that one does not have to "override the `.myElement` within each media query" (per the OP). That is, he did not want a bunch of `@media` redefinitions of `.myElement` in his final css. My answer is explaining _why_ that LESS configuration cannot work to produce the css he desired. – ScottS Jul 01 '15 at 13:44
6

You could refactor it out putting media queries in variables, and then using them within elements.

Example:

@phone: ~"screen and (max-width: 479px)";
@tablet: ~"screen and (min-width: 480px) and (max-width: 767px)";
@desktop: ~"screen and (min-width: 768px)";

.myElement {
  @media @phone { padding: 10px; }
  @media @tablet { padding: 20px; }
  @media @desktop { padding: 30px; }
}

Which produces:

@media screen and (max-width: 479px) {
  .myElement {
    padding: 10px;
  }
}
@media screen and (min-width: 480px) and (max-width: 767px) {
  .myElement {
    padding: 20px;
  }
}
@media screen and (min-width: 768px) {
  .myElement {
    padding: 30px;
  }
}

The whole make the media query an unparsed literal embed in a variable is not exactly pretty but also helps standardizing media queries.

And it helps readability because all of the three behaviors of .myElement are contained in one definition and not changed somewhere else (which could be an entirely different file) making "debugging" hard.

  • The OP sounds like he wants to have this pattern for a good number of elements. This will introduce a *lot* of CSS bloat: 3 media queries each time the padding is set on something. – cimmanon Jan 03 '13 at 13:07
  • 1
    Thanks Simone. Looks interesting. But not exactly what I need. I really need the variable to be set based on a media query, not the class being adjusted. I have a lot of different classes depending on the same var and, as @cimmanon, points out, it will lead to CSS bloat very very easy. – spliter Jan 03 '13 at 13:17
5

what about:

@media screen and (max-width: 479px) {
    .myElement(10px);
}

@media screen and (min-width: 768px) {
    .myElement(30px);
}


.myElement(@myVar) {
    .myElement {
        padding: @myVar;
    }
}
reebalazs
  • 71
  • 5
  • Thanks Balazs. This helps reducing maintenance hassle indeed. But still, this means copy/paste every element depending on the variable into every media query – spliter Jan 03 '13 at 13:21
  • 1
    ... you can also make just one mixin like .variableRules(@var1, @var2...) {...} and then you only need to insert 1 line with different parameters. – reebalazs Jan 03 '13 at 13:38