Append string in LESS variable - css

I want to dynamicaly call .some-mixin() with some colors. This mixins should generate some styles, and when the colors are equals the special code should be generated.
Due to comprimation of final css code I want to do it by help variable, where can I store classes for the special code.
Maybe sample code will be more helpful:
.some-mixin(#newClass,#color,#color2){
.test-mixin(#newClass,#color,#color2);
.#{newClass}{
color: #color;
}
}
#classes: '';
.test-mixin(#newClass,#color,#color2) when (#color = #color2){
#classes: "#{classes}, #{newClass}";
}
.final-mixin(){
.#{classes}{
/*some styles*/
}
}
The call of mixins is generated by PHP and final code should looks like this:
.some-mixin("abc",#ffffff,#000000);
.some-mixin("xyz",#ffffff,#ffffff);
.some-mixin("jkl",#ff00ff,#ff00ff);
.final-mixin();
But when I want compile the LESS it shows infinite loop detected
Is this possible in LESS?
Any advice will be helpul.

As explained in comments, your problem is the recursive variable definition in the below line. Less does not support this as explained in this answer and this one.
#classes: "#{classes}, #{newClass}";
Based on your requirement explanation in comments (that there would be some extra padding etc when the colors are different), you could use one of the below methods.
Option 1: (will add the padding to every class and so repeated code)
.some-mixin(#newClass,#color,#color2){
.#{newClass}{
color: #color;
& when not (#color = #color2){
padding: 4px;
}
}
}
.some-mixin(abc,#ffffff,#000000);
.some-mixin(xyz,#ffffff,#ffffff);
.some-mixin(jkl,#ff00f0,#ff00ff);
The above Less would compile into below CSS:
.abc {
color: #ffffff;
padding: 4px; /* colors were different */
}
.xyz {
color: #ffffff;
}
.jkl {
color: #ff00f0;
padding: 4px; /* colors were different */
}
Option 2: (uses a dummy class + extend and so lesser code)
This option is probably what you are looking for as it avoids code repetition. We cannot extend a mixin and hence we use a dummy class. This should not be a big concern because it just adds one extra line to output CSS.
.common-padding-diff-color{ /* all styles that are needed when colors are different */
padding: 4px;
}
.some-mixin(#newClass,#color,#color2){
.#{newClass}{
color: #color;
& when not (#color = #color2){
&:extend(.common-padding-diff-color);
}
}
}
.some-mixin(abc,#ffffff,#000000);
.some-mixin(xyz,#ffffff,#ffffff);
.some-mixin(jkl,#ff00f0,#ff00ff);
This would compile into
.common-padding-diff-color,
.abc,
.jkl {
padding: 4px; /* style applied for all cases where colors are not same */
}
.abc {
color: #ffffff;
}
.xyz {
color: #ffffff;
}
.jkl {
color: #ff00f0;
}

Related

Themed variables in LESS/SASS

I'm looking to support multiple themes in my app - moreover, I want to be able to dynamically change themes, either by changing a class on the body element, or even have different parts of the app use different themes.
In my previous project, I did it by adding explicit rules every time I need to use a theme-specific variable:
.theme-light & { background-color: #theme-light-background; }
.theme-dark & { background-color: #theme-dark-background; }
However, that approach does not scale well and adds unnecessary bloat to source files.
Now, I'm looking for a more automated approach for this. I.e. the following
.button {
border-radius: 4px;
background-color: #ui-background;
color: #ui-foreground;
border: 1px solid mix(#ui-background, #ui-foreground, 50%);
}
would turn into something like
.button {
border-radius: 4px;
border: 1px solid #808080;
/* normally we wouldn't expect this to appear here, but in our case
both themes have the same border color so we can't tell the difference */
}
.theme-light .button {
background-color: #fff;
color: #000;
}
.theme-dark .button {
background-color: #000;
color: #fff;
}
As far as I can tell, neither LESS nor SASS can do this in a natural way. It seems that it wouldn't be too difficult to implement it as a separate post-processor, that builds stylesheets for every theme, then compares them and scopes the differences into the corresponding "namespaces". I suspect that something like this might already exist, but I can't find anything.
Any suggestions?
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.

Set a SASS variable depending

I'm searching a way to use a particular color depending on a class on the body tag.
I have a main scss file like this
// variables.scss
$bg-main: white;
$color-first: red;
$color-second: green;
And in my other files, I use the colors
// content.scss
.content {
.some-selector: {
// some styles
color: $color-second;
}
a:hover {
// some styles
color: $color-second;
}
}
// and same goes for menu.scss etc.
Now I have a dynamic class on the body, that changes depending on the current selected menu. I would like $color-second to be different for each body classes, and I don't know how to do that. The only solution I found was to move all the $color-second from each files into one single file, like this:
.body-1 {
.content a:hover, .content .some-selector {
color: green;
}
}
.body-2 {
.content a:hover, .content .some-selector {
color: blue;
}
}
.body-1 {
.content a:hover, .content .some-selector {
color: black;
}
}
So I don't need to write the color in each files. This works well, but if I need to set this $color-second to some other selector, I need to put that in this big file.
Is this possible to do this an other way?
I already checked these answers, but it didn't helped me much:
SASS set variable depending on CSS class
Creating or referencing variables dynamically in Sass
Merge string and variable to a variable with SASS
There are multiple ways to do this. The most obvious two which come to mind are mixins and loops:
Mixins
Just put everything you want into a single mixin, and then use it for every body class:
#mixin colored-content($color) {
.content a:hover, .content .some-selector {
color: $color;
}
/* Any other rules which use $color here */
}
.body-1 {
#include colored-content(green);
}
.body-2 {
#include colored-content('#FF0000');
}
.body-3 {
#include colored-content(darken(red, 20));
}
You can extend this example with any number of arguments (for example, $textColor and $bgColor), conditions or rules.
With this approach you will not have SCSS code repetitions, and any updates will be introduced easily.
Loop
Another way is to use a simple loop:
$body_themes: (
"body-1": green,
"body-2": #FF0000,
"body-3": darken(red, 2)
);
#each $body_class, $color in $body_themes {
.#{$body_class} {
.content a:hover, .content .some-selector {
color: $color;
}
/* Any other rules which use $color here */
}
}
It is even shorter, but imho it is less readable.
P.S. It is possible to combine mixins and loops, by the way :)

Name clash between Less mixin and CSS selector

I have this simplified Less script
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
.placeholder {
margin-top: 20px;
}
The output when I run this through my local compiler or winless online less compiler is
input {
margin-top: 20px;
}
input::-webkit-input-placeholder {
color: #333333;
}
.placeholder {
margin-top: 20px;
}
Insted of the desired output
input::-webkit-input-placeholder {
color: #333333;
}
.placeholder {
margin-top: 20px;
}
Is this a bug or am I missing something here?
By the result it looks to me like I can't have CSS-selectors with the same name as mixins with default values.
I'm running into this problem when compiling Bootstrap with my site specific code. In this particular case I can work around it, but as the project grows and I include other projects I can't imaging I have to keep track of any mixins with default values?
Edit: I see now that I should have read the manual and pretty much seen on the first page of the docs that everything can be treated as a mixin.
In Less, everything is technically a mixin irrespective of whether we write it with parantheses (as in with parameters) or without parantheses (as in like a CSS class selector). The only difference between the two is that when the parantheses are present, the properties present within it are not output unless called from within a selector block.
Quoting the Less Website:
It is legal to define multiple mixins with the same name and number of parameters. Less will use properties of all that can apply.
In this case, since the other mixin has a default value for its only parameter, both the properties can apply when called without any parameter and hence there is no way to avoid it from happening.
Workaround Solution: One possible solution to work-around this problem is to enclose all such conflicting rules within a parent selector (like body).
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
body{
.placeholder{
margin-top: 20px;
}
}
Compiled CSS:
input::-webkit-input-placeholder {
color: #333333;
}
body .placeholder {
margin-top: 20px;
}
Option 2: Extracted from the solution posted by seven-phases-max in the Less GitHub Issue thread.
For the particular use-case one of possible workarounds is to isolate conflicting classes in unnamed scope so they won't interfere with external names:
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
& { // unnamed namespace
.placeholder {
background: #ffffff;
}
} // ~ end of unnamed namespace
Note: The above is a straight copy/paste from the GitHub thread without any modifications so as to not tamper with the information.
#mixin placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
#include placeholder();
}
.placeholder {
margin-top: 20px;
}
that should work.
So if i understood right, you just want to add 20px on top of the placeholder ? Add padding-top to input instead.
input {
padding-top: 20px;
}

Set a variable in Sass depending on the selector

I’ve got a website that’s using a few different ‘main’ colors. The general HTML layout stays the same, only the colors change depending on the content.
I was wondering if I could set a color variable depending on the CSS selector. This way I can theme my website with a few variables and let Sass fill in the colors.
For example:
$color-1: #444;
$color-2: #555;
$color-3: #666;
$color-4: #777;
body.class-1 {
color-default: $color-1;
color-main: $color-2;
}
body.class-2 {
color-default: $color-3;
color-main: $color-4;
}
/* content CSS */
.content {
background: $color-default;
color: $color-main;
}
I was thinking of using a mixin for this, but I was wondering if there’s a better way to do this—with a function maybe? I’m not that great with Sass, so any help would be appreciated.
I think a mixin is the answer. (As I wrote, variables won’t work.)
#mixin content($color-default, $color-main) {
background: $color-default;
color: $color-main;
}
body.class-1 {
#include content(#444, #555);
}
body.class-2 {
#include content(#666, #777);
}
That SCSS compiles to this CSS:
body.class-1 {
background: #444444;
color: #555555; }
body.class-2 {
background: #666666;
color: #777777; }
If you wanted to group the color values together in your SCSS file, you could use variables in conjunction with the mixin:
$color-1: #444;
$color-2: #555;
$color-3: #666;
$color-4: #777;
body.class-1 {
#include content($color-1, $color-2);
}
body.class-2 {
#include content($color-3, $color-4);
}
as sass documentation explain nicely (https://sass-lang.com/documentation/variables):
Sass variables are all compiled away by Sass. CSS variables are included in the CSS output.
CSS variables can have different values for different elements, but Sass variables only have one value at a time.
Sass variables are imperative, which means if you use a variable and then change its value, the earlier use will stay the same. CSS variables are declarative, which means if you change the value, it’ll affect both earlier uses and later uses.
We may take advantage of that using a combination of sass and css variables to achieve what you want:
//theme colors
$red-cosmo: #e01019;
$green-cosmo: #00c398;
$primary-color: var(--primary-color);
body{
--primary-color: #{$red-cosmo};
}
body.univers-ride{
--primary-color: #{$green-cosmo};
}
So when I call my sass variable $primary-color, it will print as my css variable "var(--primary-color)" that will expand as $green-cosmo only if my body has the "univers-ride" class else it will be $red-cosmo the default color.
If you really want to get hacky you could also define your different color schemes in a single variable like $scheme1: class1 #333 #444, where the first value is always the name, and that is followed by all the colors in that scheme.
You can then use #each:
// Define your schemes with a name and colors
$scheme1: class1 #444 #555;
$scheme2: class2 #666 #777;
$scheme3: class4 #888 #999;
// Here are your color schemes
$schemes: $scheme1 $scheme2 $scheme3;
#each $scheme in $schemes {
// Here are the rules specific to the colors in the theme
body.#{nth($scheme, 1)} .content {
background-color: nth($scheme, 2);
color: nth($scheme, 3);
}
}
This will compile to:
body.class1 .content {
background-color: #444444;
color: #555555; }
body.class2 .content {
background-color: #666666;
color: #777777; }
body.class4 .content {
background-color: #888888;
color: #999999; }
Obviously if you don't want to combine body.class1 and .content in your selectors, you could just specify a mixin content($main, $default) and call it inside the #each using nth just like in the above code, but the point is you don't have to write out a rule for each of your classes.
EDIT There are lots of interesting answers on Creating or referencing variables dynamically in Sass and Merge string and variable to a variable with SASS.
You can also create a mixing that use the ampersand parent selector. http://codepen.io/juhov/pen/gbmbWJ
#mixin color {
body.blue & {
background: blue;
}
body.yellow & {
background: yellow;
}
}
UPDATE: its 2017 and variables does works!
#mixin word_font($page) {
#font-face {
font-family: p#{$page};
src: url('../../static/fonts/ttf/#{$page}.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
.p#{$page} {
font-family: p#{$page};
}
}
// Loop and define css classes
#for $i from 1 through 604 {
#include word_font($i);
}
If you don't want to use a variable for each color, you can use one variable for all kinds of colors. In the mixin you can choose the right color with nth. For instance, if you write the index of the color as 1, then you get the first color in the color variable.
$colors: #444, #555, #666, #777;
#mixin content($color-default-num, $color-main-num) {
background: nth($colors, $color-default-num);
color: nth($colors, $color-main-num);
}
body.class-1 {
#include content(1, 2);
}
For me the definite answer to my problem was creating a map of maps and loopig through them as follows:
$pallettes: (
light-theme: (
container-color: red,
inner-color: blue,
),
dark-theme: (
container-color: black,
inner-color: gray,
),
);
#each $pallette, $content in $pallettes {
.main.#{$pallette} {
background-color: map-get($content, container-color);
.inner-div {
background-color: map-get($content, inner-color);
}
}
}
You can simply override your scss variables inside of the class wrapper:
$color1: red;
$color2: yellow;
header { background: $color1; }
.override-class {
$color1: green;
header { background: $color1; }
}
Seems to work for me.

LESS CSS - Change variable value for theme colors depending on body class

Getting to grips with LESS here but one thing is still a little unclear.
Lets say I have multiple color themes for my website, controlled by a class on the body tag. From this I can redefine the various colors for each element within each theme. Easy enough but fairly time consuming if I have a lot of elements to change... and a lot of themes. Every time I add a new theme I need to write out all the selectors again, with different color values.
I am basing my working so far on another post I found:
LESS.css variable depending on class
... However it still seems overly complicated for what I want to do in that I still have to write out all the selectors and include the mixin before dropping in the same CSS with the color variable.
I have created a CODEPEN HERE
I'd appreciate it if anyone had time to take a little look and advise me how I could approach this differently or how I could streamline this process.
Many thanks to anyone who helps out :)
Assuming you remain with wanting to theme it within one style sheet (and not multiple sheets as cimmanon noted in the comments), and assuming you are using LESS 1.3.2+, then the following code works to reduce the amount of duplication by setting up a loop through the classes that need theme changes.
Note that this does not work on Codepen (it is throwing an error uncaught throw #, perhaps because they are running an earlier version of LESS), but you can see it compiling correctly by putting the code into LESS's compiler.
LESS (based off your Codepen code with an added theme for demo)
//////////////////////////////////////////////////////
// CONSTANTS
#lightColour: #fff;
#darkColour: #000;
#lightBg: #fff;
#darkBg: #000;
#numberOfThemes: 3; //controls theme loop
//////////////////////////////////////////////////////
// MIXINS
//Theme Definitions by parametric mixin numbers (1), (2), etc.
.themeDefs(1) {
#lightColour: #f00;
#darkColour: #fff;
#lightBg: #f00;
#darkBg: #fff;
}
.themeDefs(2) {
//inverse of 1
#lightColour: #fff;
#darkColour: #f00;
#lightBg: #fff;
#darkBg: #f00;
}
.themeDefs(3) {
#lightColour: #cfc;
#darkColour: #363;
#lightBg: #cfc;
#darkBg: #363;
}
.curvy {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
//////////////////////////////////////////////////////
// GENERAL STYLING
* {padding: 0;margin: 0;}
html {text-align: center;}
h2 {padding: 20px 0;}
.box {
.curvy;
color: #lightColour;
background: #darkBg;
display:inline-block; width:10%; padding:20px 5%; margin:0 1% 20px 1%;
}
//////////////////////////////////////////////////////
// THEME BUILDING
.buildThemes(#index) when (#index < #numberOfThemes + 1) {
.theme-#{index} {
.themeDefs(#index);
color: #lightColour;
background: #darkBg;
.box {
color: #darkColour;
background: #lightBg;
}
}
.buildThemes(#index + 1);
}
//stop loop
.buildThemes(#index) {}
//start theme building loop
.buildThemes(1);
CSS Output (only showing the looped theme css for brevity)
.theme-1 {
color: #ff0000;
background: #ffffff;
}
.theme-1 .box {
color: #ffffff;
background: #ff0000;
}
.theme-2 {
color: #ffffff;
background: #ff0000;
}
.theme-2 .box {
color: #ff0000;
background: #ffffff;
}
.theme-3 {
color: #ccffcc;
background: #336633;
}
.theme-3 .box {
color: #336633;
background: #ccffcc;
}

Resources