I want to create something like a themepicker. I use LESS.css.
LESS.css has a variable which contains the main colors :
#colorOne: #222;
#colorTwo: #fff;
#darkGradientStart: lighten(#colorOne, 10%);
#darkGradientStop: lighten(#colorOne, 5%);
#lightGradientStart: #colorTwo;
#lightradientStop: darken(#colorTwo, 7%);
I want to change them if the tag has the color-class like this:
<body class='theme-blue'>
then I have written this in my less.css (after the default variables)
.theme-blue{
#colorOne: blue;
}
but it still uses the default #222. It is not overwritten.
How can I solve this problem?
Thanks
You cannot overwrite variables in LESS (within the same scope). The documentation specifically says:
Note that variables in LESS are actually ‘constants’ in that they can only be defined once.
For what you desire, you need to do a mixin:
Example LESS Code
.colorDefs(#c1: #222, #c2: #fff) {
#colorOne: #c1;
#colorTwo: #c2;
#darkGradientStart: lighten(#colorOne, 10%);
#darkGradientStop: lighten(#colorOne, 5%);
#lightGradientStart: #colorTwo;
#lightGradientStop: darken(#colorTwo, 7%);
}
.theme-blue {
//import color definitions
.colorDefs(blue, yellow);
// use them
color: #colorOne;
background-color: #colorTwo;
.gradient1 {
background-image: linear-gradient(top, #darkGradientStart, #darkGradientStop);
}
.gradient1 {
background-image: linear-gradient(top, #lightGradientStart, #lightGradientStop);
}
}
.theme-green {
//import different color definitions
.colorDefs(green, red);
// use them
color: #colorOne;
background-color: #colorTwo;
.gradient1 {
background-image: linear-gradient(top, #darkGradientStart, #darkGradientStop);
}
.gradient1 {
background-image: linear-gradient(top, #lightGradientStart, #lightGradientStop);
}
}
Example CSS Output
.theme-blue {
color: #0000ff;
background-color: #ffff00;
}
.theme-blue .gradient1 {
background-image: linear-gradient(top, #3333ff, #1a1aff);
}
.theme-blue .gradient1 {
background-image: linear-gradient(top, #ffff00, #dbdb00);
}
.theme-green {
color: #008000;
background-color: #ff0000;
}
.theme-green .gradient1 {
background-image: linear-gradient(top, #00b300, #009a00);
}
.theme-green .gradient1 {
background-image: linear-gradient(top, #ff0000, #db0000);
}
Solving 4K (i.e. a lot of) Lines of Code
ed1nh0 commented about having 4K lines of code using the color variables, and not being able to "put that in a mixin." Let me make a few comments on that:
If 4K lines of code depend upon the body class to define the colors, then it is probably best to split each color into its own css file, and only load that file as needed (i.e. not grouping every code color into one file). This then calls into question whether you really want to be controlling color by body class.
Regardless of whether one does what is recommended in 1., I believe one could still handle this with 4K of lines that use the colors. I believe the issue is not in using a mixin to define the color values themselves (i.e. not 4K lines of color variable definitions), but rather in the 4K lines of properties, classes, etc. that need repeating that are using the colors. But that repetition can be handled just as easily by wrapping it all in a mixin also. So my original answer above could be abstracted further to this (note that .colorDefs is the same as above and not repeated here):
LESS
.themeProperties() { // Imagine inside here the 4K lines of code
// use them
color: #colorOne;
background-color: #colorTwo;
.gradient1 {
background-image: linear-gradient(top, #darkGradientStart, #darkGradientStop);
}
.gradient1 {
background-image: linear-gradient(top, #lightGradientStart, #lightGradientStop);
}
}
.theme-blue {
//import color definitions
.colorDefs(blue, yellow);
.themeProperties(); //4K lines repeated here
}
.theme-green {
//import different color definitions
.colorDefs(green, red);
.themeProperties(); //4K lines repeated here
}
The above does assume that there are not differences in how the variables are used by the properties, just what the values of those properties are. If there were any "differences," then some tweaking mixins may need to be done for certain situations, but the concept should still hold.
What you are doing would get compiled like this in css:
.theme-blue{
#222: blue;
}
See why it doesn't work now? :)
If you are trying to override the color style, you should do it the usual css way:
.theme-blue{
color: blue;
}
#blue:#0000FF;
#green:#00FF00;
.theme-blue {
color:#blue;
}
.theme-green {
color:#green;
}
Related
What I basically want to do
#gradientType: radial-gradient;
background: #gradientType(to bottom, ...)
I want to put a CSS Function, a gradient in this case (btw: are these called functions? I'm not sure) in a Less variable and call it later with the variable like #variable()?
Why? Maintainability!
I have a file variables.less where I want to change to gradient (ie) for a couple of places on the fly.
So instead of
.a { background: linear-gradient(..) }
.b { background: linear-gradient(..) }
.c { background: linear-gradient(..) }
I would have something like
.a { background: #myGradient(..) }
.b { background: #myGradient(..) }
.c { background: #myGradient(..) }
Linear gradients (linear-gradient(to bottom, red, blue)) and Radial gradients (radial-gradient(circle, red, blue)) kind of have the same syntax. So I would have four variables like gradientType, gradientOpts, gradientStart, gradientStop and change the gradient type based on the variable. This is just a simplified example.
To answer your question directly, you can use variable interpolation to achieve this.
#myGradient: radial-gradient;
.a { background: ~"#{myGradient}(to bottom, red, blue)"; }
.b { background: ~"#{myGradient}(to bottom, green, yellow)"; }
.c { background: ~"#{myGradient}(to bottom, orange, gold)"; }
But I would suggest using something like a parameterized mixin to handle things like this. Below is the code for a sample mixin which creates either a radial-gradient or linear-gradient depending on the input variable.
#gradType: linear-gradient;
.a { .background(#gradType; to bottom; red; blue); }
.b { .background(#gradType; to bottom; green; yellow); }
.c { .background(#gradType; to bottom; orange; gold); }
.background(#gradType; #gradOpts; #gradStart; #gradEnd){
& when (#gradType = linear-gradient){
background: linear-gradient(#gradOpts, #gradStart, #gradEnd);
}
& when (#gradType = radial-gradient){
background: radial-gradient(#gradOpts, #gradStart, #gradEnd);
}
}
The catch with using guarded mixins (like the above) is that when the #gradType variable has a value outside of the two possible ones (say conical-gradient was the value) then the code would just fail and will not cause any errors to be thrown. This would be a problem when you're writing a library that is going to be used by others (and so want to throw an error when the value is invalid). In such cases, you could change the code like given below. When this approach is used, Less compiler would throw an error when an invalid input value is provided for the #gradType variable.
#gradType: linear-gradient;
.a { .background(#gradType; to bottom; red; blue); }
.b { .background(#gradType; to bottom; green; yellow); }
.c { .background(#gradType; to bottom; orange; gold); }
.background(linear-gradient; #gradOpts; #gradStart; #gradEnd){
background: linear-gradient(#gradOpts, #gradStart, #gradEnd);
}
.background(radial-gradient; #gradOpts; #gradStart; #gradEnd){
background: radial-gradient(#gradOpts, #gradStart, #gradEnd);
}
I have a less file which contains a class that defines a few variables for colors:
.some-css-class{
#color1: #E6E6E6
#color2: #808080
/* some other rules follow */
}
Since this is a 3rd party file I don't wont to change it directly. Instead I created my own less file and imported the 3rd party file. Now I want to override the colors defined in the base file but I don't get it working.
I tried to override the colors on root level, but this way I am not able to change the variables since they are defined within a class in the base file:
/* Including base styles */
#import "base.less";
#color1: #DF007B;
#color2: #00A855;
So I tried to override the colors within the class, but apparently I am not able to change them this way, as it is not possible to override variables in the same scope (.som-css-class):
/* Including base styles */
#import "base.less";
.some-css-class {
#color1: #DF007B;
#color2: #00A855;
}
Any hints on how I am able to override the colors?
EDIT: It seems that there isn't a solution for this. In my particular case I was looking for a way to override colors in an angularjs module(angular-wizard).
As i saw now, a pull request to solve this problem already exists:
https://github.com/mgonto/angular-wizard/pull/68
Let's think about the way LESS compiles for a second. If you want, you can try to run any of this in the https://fiddlesalad.com/less editor.
Here in this example, I've just included some arbitrary colors and a 'background' property to make it easier to understand. In your example when you import base.less, you are really just putting some LESS rules before the ones in your file. So, just to clarify, if we "expand" your import, the file might look like this before its compiled to CSS:
.some-css-class{
#color1: red;
background: #color1;
}
.some-css-class {
#color1: purple;
}
What does this output?
.some-css-class {
background: red;
}
Why? You might say "I've redeclared the color variable so shouldn't it recompile the rules?". However, this is fundamentally not how LESS is designed--its backwards compatible with CSS, so a rule must happen later to override a previous rule. Take this example instead:
.some-css-class{
#color1: red;
background: #color1;
}
.some-css-class {
#color1: purple;
background: #color1;
}
Here is the output you get now:
.some-css-class {
background: red;
}
.some-css-class {
background: purple;
}
The rules are split into two outputs, and because the second rule happens later in the cascade, it has a higher precedence--barring this class existing nowhere else, the 'purple' rule will take precedence.
In LESS, you "lazy load" variables -- it will always take the last value found, starting from the inner scope. Thus:
.some-css-class {
#color1: purple;
background: #color1;
#color1: blue;
}
Will output:
.some-css-class {
background: blue;
}
The unfortunate thing though, however, is if we try to extend your previous rule as a mixin, it resolves the values from each class before pulling them into your new class:
.some-css-class{
#color1: red;
background: #color1;
}
.some-css-class {
#color1: purple;
background: #color1;
#color1: blue;
}
.another-css-class {
.some-css-class();
#color1: orange;
}
Outputs:
.some-css-class {
background: red;
}
.some-css-class {
background: blue;
}
.another-css-class {
background: red;
background: blue;
}
The ideal way to "override" these values then, is to turn the original ruleset into a parametric mixin:
.some-css-class(#color1, #color2) {
background: #color1;
color: #color2;
}
.another-css-class {
.some-css-class(red, blue);
}
Finally, your output will look like this:
.another-css-class {
background: red;
color: blue;
}
EDIT: Ultimately I recommend, you copy the original rule out of the file (without modifying that file) and alter it to be a mixin in your version. Ultimately you'll end up with the same net amount of CSS as you would if you could extend and override the variables for your new class.
Did you try to extend it?
.new-some-css-class {
&:extend(.some-css-class);
#color1: #DF007B;
#color2: #00A855;
}
I'm using LESS as css compiler.
Everything works fine, but now I need to create a specific class structure and I'm a bit stuck.
I'd like to have this structure:
.default .{color} {.icon-after/.icon-before} {.icon}
this is the code that I've done:
.default {
&.disabled {
background: lighten(#grayBackground, 5%);
color: lighten(#darkText, 35%);
cursor: default;
border: #grayBorder;
text-shadow: #grayTextShadow;
}
&.gray {
background: #grayBackground;
color: #darkText;
border: #grayBorder;
text-shadow: #grayTextShadow;
&:hover {
background: darken(#grayBackground, 5%);
}
}
&.green {
background: #greenBackground;
border: #greenBorder;
color: #lightText;
text-shadow: #greenTextShadow;
&:hover {
background: darken(#greenBackground, 10%);
}
}
&.yellow {
background: #yellowBackground;
border: #yellowBorder;
color: #lightText;
text-shadow: #yellowTextShadow;
&:hover {
background: darken(#yellowBackground, 10%);
}
}
&.blue {
background: #blueBackground;
border: #blueBorder;
color: #lightText;
text-shadow: #blueTextShadow;
&:hover {
background: darken(#blueBackground, 10%);
}
}
&.black {
background: #blackBackground;
border: #blackBorder;
color: #lightText;
text-shadow: #blackTextShadow;
&:hover {
background: darken(#blackBackground, 10%);
}
}
&.red {
background: #redBackground;
border: #redBorder;
color: #lightText;
text-shadow: #redTextShadow;
&:hover {
background: darken(#redBackground, 10%);
}
}
&.icon-before{
.IconDefaultStyleBefore
}
&.icon-after{
.IconDefaultStyleAfter()
}
}
obviously this doesn't work, as the result is something like this:
.default .{color / .icon-after / .icon-before}
Any suggestions on how can I obtain my structure?
Thanks a lot
EDIT
I'd like to add the classes to the buttons in this order:
.default( gives the default style )
{.colours} (so that the background, the border and all colour related properties are setted)
{.icon-after or .icon-before} so that I can choose if adding the icon before or after with the proper margin
{.icon-name} (for example a questionmark or a tick etc)
so, for example, adding this classes:
.default .blue .icon-before .tick
I will have:
default blue button with the tick icon before the text
Hope is now more clear than before.
The required structure can be achieved as shown in the below example. The code can be simplified a lot by using loops (guarded mixins).
Explanation:
#colors - An array list variable which has the list of colors required for the element.
#bckground - Another array list variable which holds the required background color for each color class declared in the #colors list.
e(extract(#colors, #index)) and extract(#bckground, #index) - Extract functions are used to fetch the color name and background color value corresponding to the index of each array iteration (similar to colors[i]). e() function is used to extract the color values without the quotes.
&.#{color} - Selector interpolation to form the selector value. & is the parent selector and #{color} is the name of the color from the #colors list variable.
length(#colors) - The no. of color items present in the #colors array list variable. This is passed to the loop function to tell the Less Compiler as to how many times the loop should be executed.
#colors: "red","green","black","blue","gray";
#bckground: #AAA, #0F0, #00F, #000, #F00;
.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));
#bgColor: extract(#bckground, #index);
&.#{color}{
background: #bgColor; //set background
/* all other props */
&:hover {
background: darken(#bgColor, 5%);
}
&.icon-before{
.IconDefaultStyleBefore;
}
&.icon-after{
.IconDefaultStyleAfter();
}
}
}
.default{
.loop-colors(length(#colors));
}
Note: As seven-phases-max mentioned in his comment, we are essentially generating a selector structure like .default.red.icon-before. Such a selector would essentially mean the same element has all the three classes and so even if it is specified like .default.icon-before.red it wouldn't make any difference but I assume that you are trying to make a more readable structure (like a default red button with an icon-before).
.default{
[...]
&.gray, &.black, [...every color...] {
.icon-before{
[...]
}
}
}
EDIT: or if you need a different .icon-before for every color you have to insert it one by one:
.default{
[...]
&.gray{
[...]
.icon-before{
[...]
}
}
}
I'm trying to create a LESS mixin into which I can pass a text string to be used to construct an svg file name and a png file name.
My first attempt is below. Perhaps you can see what I'm trying to do.
.make-bg-svg(#name){
#svg: ~"../images/#{name}.svg";
#png: ~"../images/#{name}.png";
height: 200px;
background: transparent url(~"#{svg}");
.no-svg & {
background: transparent url(~"#{png}") no-repeat;
}
}
// usage
.out-of-business {
.make-bg-svg('out-of-business');
}
Any help would be appreciated.
Updated
I just figured it out and updated the example above.
Just in case you don't need that many quotes, the following is equal:
.make-bg-svg(#name) {
#svg: ~"../images/#{name}.svg";
#png: ~"../images/#{name}.png";
height: 200px;
background: transparent url(#svg);
.no-svg & {
background: transparent url(#png) no-repeat;
}
}
// usage
.out-of-business {
.make-bg-svg(out-of-business);
}
Hi I'm still very new to SASS and no programming guru.
I have ten asides elements that all require different background colours based on their class name.
I've looked through the SASS documentation and I can't figure it out.
I want to say if aside has a class name of x make background colour x if aside has a class name of y make background colour y etc
Is there a nice efficient way of doing this?
Thanks guys and sorry if its a simpleton question.
If you're using colors that don't have "standard" names (or the name of the class isn't going to be the name a color at all, eg. products = blue, addresses = red), a list of lists is what you want:
$colors:
( black #000
, white #FFF
, red #F00
, green #0F0
, blue #00F
);
#each $i in $colors {
aside.#{nth($i, 1)} {
background: nth($i, 2);
}
}
Output:
aside.black {
background: black; }
aside.white {
background: white; }
aside.red {
background: red; }
aside.green {
background: lime; }
aside.blue {
background: blue; }
If you're using colors with standard keywords, this could work:
$colors2: black, white, red, green, blue;
#each $i in $colors2 {
aside.#{$i} { background: $i; }
}
Output (though this only seems to work with --style debug, using --style compress generates errors.. weird):
aside.black {
background: black; }
aside.white {
background: white; }
aside.red {
background: red; }
aside.green {
background: green; }
aside.blue {
background: blue; }
This is simply down to how much typing you want to do. You could make a background mixin and include it within the aside CSS rule, but is that really necessary?
If it is though....
#mixin bg($color) {
background: $color;
}
aside#foo {
#include bg(#fff);
}
"if aside has a class name of x make background colour x if aside has a class name of y make background colour y" translates to the following CSS:
aside.x {background-color: x}
aside.y {background-color: y}
Is there a reason you want to use SASS? Is it to make it dynamic so that you can add any class you want in the future without updating the CSS? (If so that's not possible with SASS because the SASS code compiles to CSS and doesn't change after).
To make that work you'd have to use JS (jQuery):
$('aside').each(function () {
$(this).css('background-color', $(this).attr('class'));
});
Edit: You could use loops in SASS to generate a large number of classes and corresponding background-colors though.