How to Thematize in Lesscss

Themed variables in LESS/SASS

Not sure about Less, but in Sass it can be implemented relatively easy by storing theme information into maps and using ability to pass content blocks into mixins using @content. Here is example of how it may look like, quite fast solution but you can get an idea:

// Themes definition
// - First level keys are theme names (also used to construct theme class names)
// - Second level keys are theme settings, can be referred as theme(key)
$themes: (
light: (
background: #fff,
foreground: #000,
),
dark: (
background: #000,
foreground: #fff,
),
);

// Internal variable, just ignore
$_current-theme: null;

// Function to refer to theme setting by name
//
// @param string $name Name of the theme setting to use
// @return mixed
@function theme($name) {
@if ($_current-theme == null) {
@error "theme() function should only be used into code that is wrapped by 'theme' mixin";
}
@if (not map-has-key(map-get($themes, $_current-theme), $name)) {
@warn "Unknown theme key '#{$name}' for theme '#{$_current-theme}'";
@return null;
}
@return map-get(map-get($themes, $_current-theme), $name);
}

// Theming application mixin, themable piece of style should be wrapped by call to this mixin
@mixin theme() {
@each $theme in map-keys($themes) {
$_current-theme: $theme !global;
.theme-#{$theme} & {
@content;
}
}
$_current-theme: null !global;
}

.button {
border-radius: 4px;
@include theme() {
background-color: theme(background);
color: theme(foreground);
}
}

This piece of code will give you this result:

.button {
border-radius: 4px;
}
.theme-light .button {
background-color: #fff;
color: #000;
}
.theme-dark .button {
background-color: #000;
color: #fff;
}

Looks pretty close to what you're trying to achieve. You can play with this snippet at Sassmeister.

Get Less variable value from string

(I cannot skip this to not provide an alt. answer since such "namespace emulation via variable name concatenation" is my most favourite bloody wrong Less pattern (unfortunately it is also the most widely spread one :().

Instead of emulating namespaces via using looong global variable names (doh#1 in most of programming languages and paradigms global variables are considered harmful since 1970's... :) and then assembling those variable names via the ugly concatenation syntax (doh#2, ~"@{@{@foo}}-@{bar}-seriously}?"), one can use normal namespaces with much more clean syntax:

.theme(beach) {
@font-color: #3d3d3d;
// other beach variables
}
.theme(ocean) {
@font-color: #d3d3d3;
// other ocean variables
}

@theme: beach;

.theme(@theme);

body {color: @font-color}

This is just one of possible variations (for more examples see:

  • Dynamic Variable Names with Less -> gist
  • How to thematize in lesscss
  • etc.)

Maps and themes

A question like this ...


... ("I'm learning a language X and I found some program in language Y that looks like what I need. How to do the same thing using X?") is pretty much impossible to answer in (and too broad for) the SO format, as soon as the snippet goes beyond a single distinct minimalistic statement/feature.


Either way, to not leave the Q unanswered:
To be able to write a similar code in Less you will need to get familiar with the following features:

  • Maps
  • Rulesets as values/parameters
  • plus all typical language basic facilities like variables, functions, scope etc. etc. and the last (but not the least) Mixins.

Answering how to make something like background: .theme(backgroundColor) to do what you need would require explaining all of this from scratch (i.e. turning the answer into a book or a very loooong tutorial). Technically, you should not really be able to miss that the code in the linked snippet is a waaaaaay more complex than just background: .theme(backgroundColor).


And here's a (minimalistic) Less equivalent of the snipped at CodePen you pointed to.
(No comments there. They are pointless since nothing magic happens in it - to understand what it does you just need to get familiar with the languages features I listed above):

@theme: {
@light: {
backgroundColor: white;
textColor: #408bbd;
}
@dark: {
backgroundColor: #222;
textColor: #ddd;
}
}

// ....................................
// usage:

.themify({
div {
background: .theme[backgroundColor];
}

span {
color: .theme[textColor];
}

// etc.
});

// ....................................
// impl.:

.themify(@style) {
each(@theme, {
@name: replace(@key, '@', '.');
.theme() {@value()}
@{name}-theme {@style()}
});
}

For other possible techniques and solutions related to similar "Theming" use-cases see also:

  • Variables based on ancestor class
  • How to thematize in ...
  • and other Q/As linked/referenced there.

Dynamically define a variable in LESS CSS

This Cannot Be Done

What you desire to do is not currently possible in LESS. I can think of two possible "workarounds" if you know ahead of time what variable names you want to allow to be used (in other words, not fully dynamic). Then something like one of the following could be done:

Idea #1 (Variable Variables)

.define(@var) {
@fooBar: 0;
@fooTwo: 2;
@fooYep: 4;

@fooSet: 'foo@{var}';
}

.define(Two);
.test {
.define(Bar);
prop: @@fooSet;
}
.test2 {
prop: @@fooSet;
}

Idea #2 (Parametric Mixins)

LESS

.define(@var) {
.foo() when (@var = Bar) {
@fooBar: 0;
}
.foo() when (@var = Two) {
@fooTwo: 2;
}
.foo() when (@var = Yep) {
@fooYep: 4;
}
.foo();
}

.define(Two);
.test {
.define(Bar);
prop: @fooBar;
}
.test2 {
prop: @fooTwo;
}

CSS Output (for both ideas)

.test {
prop: 0;
}
.test2 {
prop: 2;
}

Conclusion

But I'm not sure how useful either would really be, nor do I know if it could have any real application in your actual use case (since you mention the above is not the real use case). If you want a fully dynamic variable in LESS, then it cannot be done through LESS itself.

LessCss dynamic variables based on ancestor class

Well, no, you can't use class name to determine a variable or a return value. So it's usually done in reverse, for example like this:

@brand-default: #649d84;
@brand-africa: #df6f20;
@brand-nz: #444444;

h1 {
.brand-colors();
}

h2 {
.brand-colors(background-color);
}

.brand-colors(@property: color) {
.color(default);
.color(africa);
.color(nz);

.color(@name) {
.brand-@{name} & {
@value: 'brand-@{name}';
@{property}: @@value;
}
}
}

Or like this:

@brand-default: #649d84;
@brand-africa: #df6f20;
@brand-nz: #444444;

h1 {
.brand-colors({
color: @color;
});
}

h2 {
.brand-colors({
background-color: @color;
});
}

.brand-colors(@style) {
.brand-color(default);
.brand-color(africa);
.brand-color(nz);
}

.brand-color(@name) {
.brand-@{name} & {
@value: ~'brand-@{name}';
@color: @@value;
@style();
}
}

Or even like this:

.brand(default) {@{color}: #649d84}
.brand(africa) {@{color}: #df6f20}
.brand(nz) {@{color}: #444444}

h1 {
.brand-colors();
}

h2 {
.brand-colors(background-color);
}

.brand-colors(@color: color) {
.-(default);
.-(africa);
.-(nz);

.-(@name) {
.brand-@{name} & {
.brand(@name);
}
}
}

Or something in between. Or... oh wait, there's whole family of methods for this stuff (incl. various combinations), see for example:

  • https://stackoverflow.com/a/23660124
  • How to thematize in lesscss
  • https://stackoverflow.com/a/20072967
  • etc.

Usually list/array/loop based methods are more compact, though personally I prefer something dumb like this:

.themed({

h1 {
color: @color;
}

h2 {
background-color: @color;
}

});

.themed(@styles) {
.-(default, #649d84);
.-(africa, #df6f20);
.-(nz, #444444);

.-(@name, @color) {
.brand-@{name} {
@styles();
}
}
}

LESS Declare variables using class names?

You could achieve this by having a variable with the required list of colors, a loop to create the required rules and selector interpolation like shown below.

@colors: "green","blue","orange","red","yellow"; // the list of colors required
button {
padding: 0.5em 1em;
text-transform: uppercase;
.loop-colors(@index) when (@index > 0){ // loop to generate rules for each color
.loop-colors(@index - 1); // call for the next iteration
@color: e(extract(@colors, @index)); // pick the color value from the list one by one based on current index
&.@{color} {
background:@color;
}
}
.loop-colors(length(@colors));
}

Codepen Demo

Note: As mentioned in comments, LESS PHP is quite outdated and hence some of the new features offered by LESS (with respect to loops) are not supported by it. It could be overcome by doing the work-around mentioned in this answer.

You could also adopt an approach similar to the one mentioned by seven-phases-max in this answer (2nd option). That one achieves a similar effect without using loops.



Related Topics



Leave a reply



Submit