47

Say I have three separate color schemes that are used on various pages in a site. Each color has a a light, medium and dark tint defined, and the color scheme is defined by a class in the body. Assume that the "red" color scheme is the default. Like this:

Color Definitions:

@red-lt:   #121;
@red-md:   #232;
@red-dk:   #343;
@green-lt: #454;
@green-md: #565;
@green-dk: #676;
@blue-lt:  #787;
@blue-md:  #898;
@blue-dk:  #909;

Basic Default Style Example

body { background-color: @red-dk;
  #container { background-color: @red-md;
     p { color: @red-dk; }
  }
}

Different Color Scheme Style Example

body.green { background-color: @green-dk;
  #container { background-color: @green-md;
     p { color: @green-dk; }
  }
}

I'd like to use variables so that I don't have to re-write all of the color variations for each scheme, so that I can just write something like this:

body.[color-var] { background-color: @[color-var]-dk;
  #container { background-color: @[color-var]-md;
     p { color: @[color-var]-dk; }
  }
}

…but I can't quite wrap my head around how to accomplish that. Help…?

Kerri
  • 1,211
  • 2
  • 15
  • 22
  • I'm not aware of dynamic variables, but for the selector, this works: `~("body.@{color-var}")` – Rob W Jun 25 '12 at 22:38
  • I retract that comment, dynamic variables are certainly possibly: `~"@{@{var}-suffix}"` (see my answer below). – Rob W Jun 26 '12 at 08:27
  • I had the same question but had difficulty recognizing my own issue within the specifics of this scenario, it would be helpful for other users if the question were phrased in a more generic way. – jrz Sep 10 '13 at 22:06
  • Hi Jon, I'm not so great at explaining a broader issue without a concrete example. Feel free to edit (or at least add some key words) if you have a way to make the question appear more generic. – Kerri Sep 20 '13 at 13:12
  • @Kerri have any of the answers solved your issue? Please mark as accepted. I guess the most voted one should be - at least, it solved mine :) – igorsantos07 Oct 25 '15 at 18:26

5 Answers5

81

Use interpolation and escaping, parentheses in the selector and parametric mixins to get the desired effect:

  • Dynamic variables by interpolation: In a string, "@{variable}" is replaced with the value of the variable. They can also be nested: Given @{@{var}-foo} and @var: bar;, the result is "barfoo".
    The resulting value is quoted. To remove these quotes, prefix ~.
  • Dynamic selectors by Selector interpolation: body.@{var} turns into body.bar.

Example:

@red-md:   #232;
@red-dk:   #343;

.setColor(@color) {
    body.@{color} { background-color: ~"@{@{color}-dk}";
        #container { background-color: ~"@{@{color}-md}";
         p { color: ~"@{@{color}-md}"; }
      }
    }
}
.setColor(~"red"); // Escape to prevent "red" turning "#FF0000"
//.setColor(~"blue"); etc..

Turns into:

body.red {
  background-color: #334433;
}
body.red #container {
  background-color: #223322;
}
body.red #container p {
  color: #223322;
}

Note: When the answer was originally written, selector interpolation did not exist. See the previous revision for the solution if you're working with an old LESS compiler (before LESS 1.3.1a). Support for the old method will be dropped in LESS 1.4.0.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • +1 nice answer! I was looking for a way to do that in LESS and couldn't dope it out from the docs (I use Sass usually). – steveax Jun 26 '12 at 16:40
  • Thank you! It's going to take me a bit to wrap my head around this, but it seems to be what I'm looking for. I'll' be back! – Kerri Jun 26 '12 at 20:32
  • @moonunit7 The online editor at http://leafo.net/lessphp/editor.html is slightly outdated. The latest version (still called "v0.3.8", even though new features have been added) supports the syntax as used in this answer. I've successfully used this code ( http://pastebin.com/DhnW765E ) with the latest LESSPHP version ([commit 21a012](https://github.com/leafo/lessphp/commit/21a012)). – Rob W Feb 12 '13 at 13:15
  • OP, if this solves your problem, you should mark it as "answered". (Also, +1 to Rob; the LESS docs are complete crap.) – jonathan3692bf Feb 12 '15 at 12:55
  • It is a very informative answer. However, this syntax is nicer & more readable: `@currBgColor: "@{color}-dk"; background-color: @@currBgColor` . Another advantage is that when using that syntax you can add this dynamic parameter in a call to a mixin/function (which does not seem possible when using `~"@{@{color}-md}"`) , ie. `background-color: fade(@@currBgColor, 80%);`. see http://stackoverflow.com/questions/23823833/dynamic-variable-names-in-less-css – Adriano Apr 24 '15 at 08:19
  • 1
    "They can also be nested: Given @{@{var}-foo} and @var: bar;, the result is "barfoo"." Isn't it "bar-foo"? – RandomPleb Sep 13 '16 at 02:13
8

If those values really follow a predictable format like that, seems like a perfect case for a parametric mixin:

Less:

@red:   #232;
@green: #565;
@blue:  #898;


.theme (@color) {
  background-color: @color - #111;
  #container {
    background-color: @color;
    p { color: @color + #111; }
  }
}

body.red {
  .theme(@red);
}

Compiled CSS:

body.red{background-color:#112211;}
body.red #container{background-color:#223322;}
body.red #container p{color:#334433;}
steveax
  • 17,527
  • 6
  • 44
  • 59
6

I know this question is pretty old, but for those that come to this post my answer maybe can help

I`m not really sure for what you want to use this, but one of my suggestion is based on @ScottS answer. On my real world, I need to create a web app, where it would show several brands and each brand have their own text color, background and so on... so I started to chase a way to accomplish this in LESS, what I could easily do on SASS and the result is below:

LESS

// Code from Seven Phase Max
// ............................................................
// .for
.for(@i, @n) {.-each(@i)}
.for(@n)     when (isnumber(@n)) {.for(1, @n)}
.for(@i, @n) when not (@i = @n)  {
    .for((@i + (@n - @i) / abs(@n - @i)), @n);
}

// ............................................................
// .for-each

.for(@array)   when (default()) {.for-impl_(length(@array))}
.for-impl_(@i) when (@i > 1)    {.for-impl_((@i - 1))}
.for-impl_(@i)                  {.-each(extract(@array, @i))}


// Brands
@dodge : "dodge";
@ford : "ford";
@chev : "chev";

// Colors
@dodge-color : "#fff";
@ford-color : "#000";
@chev-color : "#ff0";

// Setting variables and escaping than
@brands: ~"dodge" ~"ford" ~"chev";

// Define our variable   
.define(@var) {
  @brand-color: '@{var}-color';
}

// Starting the mixin
.color() {
    // Generating the loop to each brand
    .for(@brands); .-each(@name) {
        // After loop happens, it checks what brand is being called
        .define(@name);
         // When the brand is found, match the selector and color
        .brand-@{name} & {
            color: @@brand-color;
        }
    }
}

.carColor {
    .color();
}

Te result will be:

CSS

.brand-dodge .carColor {
    color: "#fff";
}
.brand-ford .carColor {
    color: "#000";
}
.brand-chev .carColor {
    color: "#ff0";
}

This is very tricky and I had to use several elements to get what I needed, first used a set of mixins provided by Seven Phase Max and you can find it here and than, the @ScottS answer was the piece that was missing fro my puzzle... hope this helps you and others that need to create a set of Variables to be part of another variable and create a more dynamic less file.

You can copy my entire code and test at http://lesstester.com/

Paulo Griiettner
  • 1,115
  • 12
  • 18
1

Try this

@red-lt:   #121;
@red-md:   #232;
@red-dk:   #343;
@green-lt: #454;
@green-md: #565;
@green-dk: #676;
@blue-lt:  #787;
@blue-md:  #898;
@blue-dk:  #909;

@color: 'red-lt';

div{
background: @@color;
border: 1px solid lighten(@@color,20%);
}
0

To my knowledge, variable variable names are not supported in LESS. You could however restructure your declarations in a more semantic manner:

/* declare palette */
@red-lt:   #121; 
@red-md:   #232; 
@red-dk:   #343; 
@green-lt: #454; 
@green-md: #565; 
@green-dk: #676; 
@blue-lt:  #787; 
@blue-md:  #898; 
@blue-dk:  #909; 

/* declare variables based on palette colors */
@lt: @red-lt;
@md: @red-md;
@dk: @red-dk;

/* ...and only use them for main declarations */
body { background-color: @dk;        
  #container { background-color: @md;        
     p { color: @dk; }        
  }        
}  

This should let you switch between palettes quite painlessly by avoiding explicit color references.

Oleg
  • 24,465
  • 8
  • 61
  • 91
  • Thanks, but I'm not sure I follow yet — it appears that the (a)lt, (a)md and (a)dk variables are identical to the (a)red-lt, (a)red-md, and (a)red-dk variables, just renamed to shorter variables. I don't know how I get from there to switching to the other palettes. (Sorry for the '(a)'s instead of "at" characters — stackoverflow thinks I'm trying to notify a bunch of users) – Kerri Jun 26 '12 at 01:18
  • How dynamic is this supposed to be? I must have misread the question, but I assumed it would have been appropriate for you to substitute the references *manually*. Also, are you compiling these client-side or server-side? – Oleg Jun 26 '12 at 01:44
  • Sorry if I didn't explain well. Basically what I'm wondering if I can accomplish is only writing out the "main declaration" once, and then, if the body class is "green", for example, the css pulls the green values for all color declarations. In other words, using the name of the body class to define part of the name of the color variable. It may not be possible, but it feels like Mixins or Parametric Mixins might accomplish it — but I can't quite work out how. – Kerri Jun 26 '12 at 02:46
  • @Kerri: why not have definitions like `.green p`, `.red p` etc. in the same file then? I don't believe you'd be able to toggle the body color at runtime – Oleg Jun 26 '12 at 03:01
  • They are all in the same file. I really must be lousy at explaining this! Let me try without any code: I have 3 color schemes (red, green, blue). Each scheme has dk, md and lt tints defined. The tints are assigned throughout the style sheet (a div might have the dk border, and another div's background color might be lt). Each page has a body class that defines the color (). I want to be use that body class of "blue", and wherever a color is defined as "light", "medium" or "dark", make it "light blue", "medium blue" etc, without having to write out the statements 3 times. – Kerri Jun 26 '12 at 20:30