48

I'm working on a iPad-specific website. To make my website work on both the retina display iPad and older versions of iPads, I want to set a variable in LESS CSS in media query like:

@media all and (max-width: 768px) {
    @ratio: 1;
}

@media all and (min-width: 769px) {
    @ratio: 2;  
}

So when you are setting anything in pixels, you can do

width: 100px * @ratio;

But I got a compile error saying @ratio is not defined. Is it possible to have a workaround to make it work? Thanks.

Sean
  • 567
  • 1
  • 5
  • 8

8 Answers8

48

It would be nice, but this is impossible to do like this.

LESS compiles your media queries to actual media queries, so at compile time there is no indication of which media query will be relevant to the browser.

After compile time you just have CSS not less so you can't have variables anymore.

You can do this instead but it isn't as elegant:

@base_width: 100px;

@media all and (max-width: 768px) {
    .something {
        width: @base_width;
    }
}

@media all and (min-width: 769px) {
    .something {
        width: @base_width * 2;
    }
}
Paul
  • 139,544
  • 27
  • 275
  • 264
  • Thanks for your explanation. So it seems like there is no way to achieve something close? – Sean Jun 05 '12 at 23:05
  • 1
    @Sean This project may be of interest to you: https://github.com/imulus/retinajs It looks like you wanted to do this with widths, but if you are looking for doubling resolution in background images for the retina display, that repo has a file called retina.less which has a mixin for it. – Paul Jun 05 '12 at 23:06
  • I see. that's not exactly what I want because I have a lot of less files. Maybe we have to use % or em. – Sean Jun 05 '12 at 23:08
  • In fact I just realized there no need to set a different width for iPad 3 because iPad 3 will also render web pages as if it's 1024 x 768. It does the magic scaling up everything 2x. – Sean Jun 06 '12 at 22:49
  • To expand on @ascii-lime answer and still keep the benefit of less variables, you could do define 2 variables, 1 for each media-query, perform the calculation here, and use the relevant variable in your media-queries. `@base_width : 100px` and `@base_width_retina: @base_width * 2` – DjebbZ Dec 13 '12 at 12:47
  • 1
    I dont think this would be impossible, less would just have to generate an overload for each preceding usage of the variable inside the generated media query. It certainly wouldnt be trivial code in the compiler but it would be super useful – undefined Oct 04 '14 at 05:06
  • Nice, i was wondering the same thing, I finally decided to declare double the variables, suffixing them with the media type like -lg -xs and -md, makes easier to me if I need to change numbers without having to go scrolling deep on that mass of CSS – Felype May 02 '15 at 21:32
17

I know I'm late with my answer but someone may find this useful.

You can move your styles to a separate file

// styles.less
.foo {
  width: 100px * @ratio;
}

And then import the file multiple times after changing variables' values

// main.less
@ratio: 1; // initial value

@media all and (max-width: 768px) {
  @ratio: 1;
  @import (multiple) "styles";
}

@media all and (min-width: 769px) {
  @ratio: 2;
  @import (multiple) "styles";
}

Note that (multiple) is important here

Compiled code will look like this

// main.css
@media all and (max-width: 768px) {
  .foo {
    width: 100px;
  }
}
@media all and (min-width: 769px) {
  .foo {
    width: 200px;
  }
}
6

For those who might allow supporting relatively modern browsers only (Chrome 49+, FF 31+, no IE), you can use css variables instead.

Here is browser support table from "Can I Use".

html {
    --width-var: 300px;

    @media only screen and (max-width: 750px) {
        --width-var: 200px;
   }
}

.your-class {
    max-width: calc( var(--width-var) * 2 );
    .... // tons of other props
}

With the code above, whenever screen is getting smaller than 750px, max-width property from .your-class is recalculated (since --width-var is changed), and of course when screen is resized to be bigger - css variable gets back to its original value.

Artyom Pranovich
  • 6,814
  • 8
  • 41
  • 60
1

That's a little hackish, but a possible solution is to set the font-size for a wrapper element and set all units to em:

HTML:

<div id="wrap">
    <div class="child1"></div>
    <div class="child2"></div>
</div>

LESS:

#wrap 
    {
    font-size: 100px;
    width: 10em; // = 1000px;
    @media all and (max-width: 768px)
        {
        font-size: 60px;
        }
    .child1
        {
        width: 5em; // 500px normally, 300px on small screens
        }
    .child1
        {
        width: 2.5em; // 250px normally, 150px on small screens
        }
    }

That of course does not work if you have elements that contain text AND children.

Manuel Ebert
  • 8,429
  • 4
  • 40
  • 61
1

LESS currently cannot do this, although it would be technically possible for it to do it. This feature has been requested in the GitHub issue entitled Less variables in media queries.

See also this question: CSS pre-processor with a possibility to define variables in a @media query

Community
  • 1
  • 1
Flimm
  • 136,138
  • 45
  • 251
  • 267
1

I had a LESS variable that I was using everywhere, including in calculations. For me, the variable had to be flexible and change for screen width, or all the calculations would have to be duplicated and either use a different variable or do a different calculation.

This is what I did, and now the same variable works properly wherever it is used.

It sets the variable in CSS, and changes it when javascript adds a conditional class. The LESS variable invokes the CSS variable.

IE 11 does not support, so be careful.

/* CSS var sets the width for normal screen size */
:root {
  --side-nav-width: 252px;
}

/* change the CSS var under conditions
   I think it would work in a media query but didn't test */
.side-nav-closed {
  --side-nav-width: 72px;
}


/* The LESS var correctly invokes whichever width applies */
@side-nav-width: var(--side-nav-width);
Deborah
  • 4,316
  • 8
  • 31
  • 45
0

I found the accepted solution not to work, as the compiler would complain the mixin was not defined. An alternative solution:

@base_width: 100px;

.mixin {
    width: @base_width;

    @media all and (min-width: 769px) {
        width: @base_width * 2;
    }
}
Rijk
  • 11,032
  • 3
  • 30
  • 45
-2

With less we can define variables for media query.
first of all detect iPad resolution:

@iPad: ~"only screen and (min-width: 40.063em) and (max-width: 64em);

These are em equivalent of 641px and 1024px.


Now detect high resolution:

@highResolution: ~"only screen and (-webkit-min-device-pixel-ratio: 1.5)",
          ~"only screen and (min--moz-device-pixel-ratio: 1.5)",
          ~"only screen and (-o-min-device-pixel-ratio: 3/2)",
          ~"only screen and (min-device-pixel-ratio: 1.5)";



Now we can combine these 2 variables in a media query like this:

@media @iPad, @highResolution{ .yourCssClass{} }

this will be valid just on iPad (or similar resolutions) with retina display (or similar pixel density).

Marco Allori
  • 3,198
  • 33
  • 25