I am working on a project with multiple sections. Depending on the section, elements in that section have varying colors. Through my LESS files I am defining all these possibilities like so:
a{
.section_what &{
color: darken(#what,10%);
}
.section_where &{
color: darken(#where,10%);
}
.section_who &{
color: darken(#who,10%);
}
.section_post &{
color: darken(#post,10%);
}
.section_events &{
color: darken(#events,10%);
}
.section_deals &{
color: darken(#deals,10%);
}
}
It seems like this is not the most streamlined way of doing things. Using this method I have to repeat this list of sections quite a bit. So every element that is altered by its section requires this list of sections defined. Sometimes its color, background-color, border-color etc...
Is there a better way?
In LESS you can get it with more generic code like this:
#what: #111;
#where: #222;
#who: #333;
#post: #444;
#events: #555;
#deals: #666;
#items: what,
where,
who,
post,
events,
deals;
#items-count: 6;
.sections() {
.-(#items-count);
.-(#i) when (#i > 0) {
.-((#i - 1));
#name: extract(#items, #i);
.section_#{name} & {
color: darken(##name, 10%);
}
}
}
a {
.sections();
}
b {
.sections();
}
Or, if you don't need those variables for anything else, even better:
#items: what #111,
where #222,
who #333,
post #444,
events #555,
deals #666;
.sections() {
.-(length(#items));
.-(#i) when (#i > 0) {
.-((#i - 1));
#item: extract(#items, #i);
#name: extract(#item, 1);
.section_#{name} & {
color: darken(extract(#item, 2), 10%);
}
}
}
a {
.sections();
}
b {
.sections();
}
It looks quite verbose but I suppose a level of customization worths this.
Note that length function is available only in LESS 1.5.x, for earlier versions you can use a predefined count variable as in the first example.
And yet another approach (if you prefer "copy-paste" style):
#what: #111;
#where: #222;
#who: #333;
#post: #444;
#events: #555;
#deals: #666;
.item(#name) {
.section_#{name} & {
color: darken(##name, 10%);
}
}
a {
.item(what);
.item(where);
.item(who);
.item(post);
.item(events);
.item(deals);
}
P.S.
So every element that is altered by its section requires this list of sections defined. Sometimes its color, background-color, border-color etc...
It is also possible to add more "customization points" for properties as well - but it depends on how exactly those sections and CSS properties tie to each other... (So I did not put that into examples above to not make things even more complicated to understand).
Basically the key is "list/loops", "mixins/abstraction" etc.
Related
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;
}
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’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.
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;
}
I am trying to develop a 'theming engine' in LESS CSS to allow clients to update the colour scheme for their site with the least number of variables. At present we have a variables file where we are assigning the default set, and to shorthand the number of user inputs we utilise the lighten and darken functions a lot e.g.
// snippet 1
#grayLight: #999999;
#grayLighter: darken(#grayLight, 10%);
Is there a way in which the lighten/darken functions can be assigned as a variable? This way we only need to specify the tone contrast (dark/light or light/dark):
// snippet 2 (does not work)
#contrastVerb: darken;
#linkColorHover: #contrastVerb(#linkColor, 10%);//#FFA500;
I have tried using Javascript bracket notation to call the function request e.g.
// snippet 3 (does not work)
#linkColorHover: `window[#contrastVerb]`(#linkColor, 10%);//#FFA500;
An option is pattern-matching:
// snippet 4
#tone: light;
.linkColor(light, #color){
color: lighten(#color, 10%);
}
.linkColor(dark, #color){
color: darken(#color, 10%);
}
.linkColor(#tone, #f17900);
However snippet 4 will is much more verbose than snippets 2 or 3, and will result in a lot of extra code being generated.
Thanks in advance!
I'm in a similar situation regarding user generated themes and I've come up with the following LESS mixin to help me manage colour schemes generated from a few base colours:
//LESS 1.3.3
.color (#color, #amount: 100) {
color: contrast(#color, darken(#color, lightness(#color) * (#amount / 100)), lighten(#color, (100 - lightness(#color)) * (#amount / 100)), 0.5);
}
The above code was written for LESS 1.3.3 which has a different contrast function to the LESS version I normally use in Sublime Text 2! If this doesn't give the expected result try:
.color (#color, #amount: 100) {
color: contrast(#color, lighten(#color, (100 - lightness(#color)) * (#amount / 100)), darken(#color, lightness(#color) * (#amount / 100)), 50%);
}
This mixin works by generating a contrasting colour based on an input colour and an amount between 0 - 100 inclusive. 100 = full contrast (i.e. black or white); 0 = no contrast (i.e. input colour). E.g.
LESS
#c: #4f2634;
.c0 {.color(#c, 0)}
.c20 {.color(#c, 20)}
.c50 {.color(#c, 50)}
.c80 {.color(#c, 80)}
.c100 {.color(#c, 100)}
CSS
.c0 {color: #4f2634;}
.c20 {color: #844057;}
.c50 {color: #bf7a92;}
.c80 {color: #e5cad3;}
.c100 {color: #ffffff;}
This example obviously generates a text colour so is useful when given a background colour, but the same idea can be re-purposed for border colours etc. Hope this helps.
Try just add a class in the <body> and create css rules (with mixin) to change whatever in the site.
<body class="section1"></body>
LESS Demo:
.color-mixin (#color) {
color: #color;
.foo{
background-color: darken(#color, 10%);
}
a {
&:hover{
background-color: lighten(#color, 30%);
}
}
}
.section1 {
.color-mixin(red);
}
.section2 {
.color-mixin(blue);
}
CSS Result:
.section1 {
color: #ff0000;
}
.section1 .foo {
background-color: #cc0000;
}
.section1 a:hover {
background-color: #ff9999;
}
.section2 {
color: #0000ff;
}
.section2 .foo {
background-color: #0000cc;
}
.section2 a:hover {
background-color: #9999ff;
}