I've been messing around with SASS for a while and have made some nice functions that makes use of #if #else if etc. However I have the following two pieces of code on a Mixin:
&:last-child { float: right; margin-right: 0; }
&:only-child { float: left; margin-right: 0; }
This obviously applies both of these pieces of code to every element I include this particular Mixin on. Is there a way of dynamically checking whether an element is a :last-child / :only:child with an #if statement or any other methods?
Something like:
#if :last-child == True { float: right; margin-right:0; }
Hopefully I've explained that well enough.
Thanks
Sass doesn't know anything about your document. It can't check if the element is the first-child or only-child. Sass can only be used to generate valid CSS.
If you're looking for the last-child that is not an only-child, this is your selector:
&:last-child:not(:only-child) {
// styles
}
http://tinker.io/1717b
If you're wanting to disable output of one or the other (because you already know that only one of them will be applicable in this instance), then you'll need an extra argument passed to your mixin:
#mixin foo($children: true) {
#if $children or $children == last-child {
&:last-child {
// styles
}
}
#if $children or $children == only-child {
&:only-child {
// styles
}
}
}
Related
For example if I have the following HTML:
<div class="someDiv"></div>
and this CSS:
.opacity {
filter:alpha(opacity=60);
-moz-opacity:0.6;
-khtml-opacity: 0.6;
opacity: 0.6;
}
.radius {
border-top-left-radius: 15px;
border-top-right-radius: 5px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
}
.someDiv {
background: #000; height: 50px; width: 200px;
/*** How can I reference the opacity and radius classes here
so this div has those generic rules applied to it as well ***/
}
Like how in scripting languages you have generic functions that are used often written at the top of the script and every time you need to use that function you simply call the function instead of repeating all the code every time.
No, you cannot reference one rule-set from another.
You can, however, reuse selectors on multiple rule-sets within a stylesheet and use multiple selectors on a single rule-set (by separating them with a comma).
.opacity, .someDiv {
filter:alpha(opacity=60);
-moz-opacity:0.6;
-khtml-opacity: 0.6;
opacity: 0.6;
}
.radius, .someDiv {
border-top-left-radius: 15px;
border-top-right-radius: 5px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
}
You can also apply multiple classes to a single HTML element (the class attribute takes a space separated list).
<div class="opacity radius">
Either of those approaches should solve your problem.
It would probably help if you used class names that described why an element should be styled instead of how it should be styled. Leave the how in the stylesheet.
You can't unless you're using some kind of extended CSS such as SASS. However it is very reasonable to apply those two extra classes to .someDiv.
If .someDiv is unique I would also choose to give it an id and referencing it in css using the id.
You can easily do so with SASS pre-processor by using #extend.
someDiv {
#extend .opacity;
#extend .radius;
}
Ohterwise, you could use JavaScript (jQuery) as well:
$('someDiv').addClass('opacity radius')
The easiest is of course to add multiple classes right in the HTML
<div class="opacity radius">
If you're willing and able to employ a little jquery, you can simply do this:
$('.someDiv').css([".radius", ".opacity"]);
If you have a javascript that already processes the page or you can enclose it somewhere in <script> tags. If so, wrap the above in the document ready function:
$(document).ready( function() {
$('.someDiv').css([".radius", ".opacity"]);
}
I recently came across this while updating a wordpress plugin. The them has been changed which used a lot of "!important" directives across the css. I had to use jquery to force my styles because of the genius decision to declare !important on several tags.
Just add the classes to your html
<div class="someDiv radius opacity"></div>
I had this problem yesterday. #Quentin's answer is ok:
No, you cannot reference one rule-set from another.
but I made a javascript function to simulate inheritance in css (like .Net):
var inherit_array;
var inherit;
inherit_array = [];
Array.from(document.styleSheets).forEach(function (styleSheet_i, index) {
Array.from(styleSheet_i.cssRules).forEach(function (cssRule_i, index) {
if (cssRule_i.style != null) {
inherit = cssRule_i.style.getPropertyValue("--inherits").trim();
} else {
inherit = "";
}
if (inherit != "") {
inherit_array.push({ selector: cssRule_i.selectorText, inherit: inherit });
}
});
});
Array.from(document.styleSheets).forEach(function (styleSheet_i, index) {
Array.from(styleSheet_i.cssRules).forEach(function (cssRule_i, index) {
if (cssRule_i.selectorText != null) {
inherit_array.forEach(function (inherit_i, index) {
if (cssRule_i.selectorText.split(", ").includesMember(inherit_i.inherit.split(", ")) == true) {
cssRule_i.selectorText = cssRule_i.selectorText + ", " + inherit_i.selector;
}
});
}
});
});
Array.prototype.includesMember = function (arr2) {
var arr1;
var includes;
arr1 = this;
includes = false;
arr1.forEach(function (arr1_i, index) {
if (arr2.includes(arr1_i) == true) {
includes = true;
}
});
return includes;
}
and equivalent css:
.test {
background-color: yellow;
}
.productBox, .imageBox {
--inherits: .test;
display: inline-block;
}
and equivalent HTML :
<div class="imageBox"></div>
I tested it and worked for me, even if rules are in different css files.
Update: I found a bug in hierarchichal inheritance in this solution, and am solving the bug very soon
.
For example if I have the following HTML:
<div class="someDiv"></div>
and this CSS:
.opacity {
filter:alpha(opacity=60);
-moz-opacity:0.6;
-khtml-opacity: 0.6;
opacity: 0.6;
}
.radius {
border-top-left-radius: 15px;
border-top-right-radius: 5px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
}
.someDiv {
background: #000; height: 50px; width: 200px;
/*** How can I reference the opacity and radius classes here
so this div has those generic rules applied to it as well ***/
}
Like how in scripting languages you have generic functions that are used often written at the top of the script and every time you need to use that function you simply call the function instead of repeating all the code every time.
No, you cannot reference one rule-set from another.
You can, however, reuse selectors on multiple rule-sets within a stylesheet and use multiple selectors on a single rule-set (by separating them with a comma).
.opacity, .someDiv {
filter:alpha(opacity=60);
-moz-opacity:0.6;
-khtml-opacity: 0.6;
opacity: 0.6;
}
.radius, .someDiv {
border-top-left-radius: 15px;
border-top-right-radius: 5px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
}
You can also apply multiple classes to a single HTML element (the class attribute takes a space separated list).
<div class="opacity radius">
Either of those approaches should solve your problem.
It would probably help if you used class names that described why an element should be styled instead of how it should be styled. Leave the how in the stylesheet.
You can't unless you're using some kind of extended CSS such as SASS. However it is very reasonable to apply those two extra classes to .someDiv.
If .someDiv is unique I would also choose to give it an id and referencing it in css using the id.
You can easily do so with SASS pre-processor by using #extend.
someDiv {
#extend .opacity;
#extend .radius;
}
Ohterwise, you could use JavaScript (jQuery) as well:
$('someDiv').addClass('opacity radius')
The easiest is of course to add multiple classes right in the HTML
<div class="opacity radius">
If you're willing and able to employ a little jquery, you can simply do this:
$('.someDiv').css([".radius", ".opacity"]);
If you have a javascript that already processes the page or you can enclose it somewhere in <script> tags. If so, wrap the above in the document ready function:
$(document).ready( function() {
$('.someDiv').css([".radius", ".opacity"]);
}
I recently came across this while updating a wordpress plugin. The them has been changed which used a lot of "!important" directives across the css. I had to use jquery to force my styles because of the genius decision to declare !important on several tags.
Just add the classes to your html
<div class="someDiv radius opacity"></div>
I had this problem yesterday. #Quentin's answer is ok:
No, you cannot reference one rule-set from another.
but I made a javascript function to simulate inheritance in css (like .Net):
var inherit_array;
var inherit;
inherit_array = [];
Array.from(document.styleSheets).forEach(function (styleSheet_i, index) {
Array.from(styleSheet_i.cssRules).forEach(function (cssRule_i, index) {
if (cssRule_i.style != null) {
inherit = cssRule_i.style.getPropertyValue("--inherits").trim();
} else {
inherit = "";
}
if (inherit != "") {
inherit_array.push({ selector: cssRule_i.selectorText, inherit: inherit });
}
});
});
Array.from(document.styleSheets).forEach(function (styleSheet_i, index) {
Array.from(styleSheet_i.cssRules).forEach(function (cssRule_i, index) {
if (cssRule_i.selectorText != null) {
inherit_array.forEach(function (inherit_i, index) {
if (cssRule_i.selectorText.split(", ").includesMember(inherit_i.inherit.split(", ")) == true) {
cssRule_i.selectorText = cssRule_i.selectorText + ", " + inherit_i.selector;
}
});
}
});
});
Array.prototype.includesMember = function (arr2) {
var arr1;
var includes;
arr1 = this;
includes = false;
arr1.forEach(function (arr1_i, index) {
if (arr2.includes(arr1_i) == true) {
includes = true;
}
});
return includes;
}
and equivalent css:
.test {
background-color: yellow;
}
.productBox, .imageBox {
--inherits: .test;
display: inline-block;
}
and equivalent HTML :
<div class="imageBox"></div>
I tested it and worked for me, even if rules are in different css files.
Update: I found a bug in hierarchichal inheritance in this solution, and am solving the bug very soon
.
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 :)
I'm using LESS CSS 1.3.3. Sorry if this question has already been asked, I didn't find anything relevant on the web.
I have several class generators that look like this (example extremely simplified, just enough to trigger the error):
#genMarginTop (#name, #size) {
.#{name} { margin-top: #size; }
}
Then I use them to generate some actual classes:
#genMarginTop(mtStandard, 40px);
#genMarginTop(mtHalf, 20px);
So far, so good, LESS correctly generates those classes and I can use them in the HTML.
However when I want to reuse such a generated class as a mixin somewhere else, I get an error:
.someClass {
.mtStandard; // won't work, see error below
// more stuff
}
The error I get is:
NameError: .mtStandard is undefined in /.../example.less:161:4
160 .someClass {
161 .mtStandard;
162 // more stuff
Of course I try to use the mixin after the class has been generated. It looks like LESS somehow won't register such generated classes internally after it generates them, but I could well be wrong.
Is there a way to reuse such generated classes as mixins in other classes? Being quite new with LESS, and their documentation being rather sparse about generated classes, I'm at a total loss (especially since this is the only syntax that seems to be accepted for mixins).
Thanks for reading me.
Note: The reason why I use such class generators is because they are much more complex than the example above (think nested classes that all depend on a common set of parameters), and I'm embedding the generated classes in various #media queries to support any device type in a "Zen" fashion. In the end I get something like:
#media (max-width: 1024px) {
#genSomething(something, somethingParam1, ...);
#genSomething(somethingElse, somethingElseParam1, ...);
#genStuff(stuff, stuffParam1, ...);
}
#media (max-width: 240px) {
#genSomething(something, somethingParam2, ...);
#genSomething(somethingElse, somethingElseParam2, ...);
#genStuff(stuff, stuffParam2, ...);
}
// etc
Solution / test case
Here's a test case for #MartinTurjak 's solution, I can confirm that this works as expected, nested classes and everything:
.explicit {
margin-top: 1;
input { margin-top: 1; }
}
.reuseExplicit {
.explicit;
margin-bottom: 1;
}
#generator (#arg) {
margin-top: #arg;
input {
margin-top: #arg;
}
}
.generated { #generator(1); }
.reuseGenerated {
.generated;
margin-bottom: 1;
}
Which correctly generates: (notice how explicit/generated yield the very same result)
.explicit {
margin-top: 1;
}
.explicit input {
margin-top: 1;
}
.reuseExplicit {
margin-top: 1;
margin-bottom: 1;
}
.reuseExplicit input {
margin-top: 1;
}
.generated {
margin-top: 1;
}
.generated input {
margin-top: 1;
}
.reuseGenerated {
margin-top: 1;
margin-bottom: 1;
}
.reuseGenerated input {
margin-top: 1;
}
Unfortunately. The selector interpolation is just string interpolation, and the string gets then printed into css, so no class object is generated in the less run.
So you can design a generator/mixin, that includes your operation:
#genMarginTop (#size) {
margin-top: #size;
}
But then build classes by calling the mixins / generators:
.mtStandard {#genMarginTop(40px);}
.mtHalf {#genMarginTop(20px);}
And this way they are class objects that you can use for mixin =)
.someClass {
background-color: #FFF;
.mtStandard;
//more of this stuff
}
This looks a bit silly in this simple example, but maybe something like this:
#bggenerator (#color) {
background-color: #color;
}
#bggenerator (#color, dark) {
#blend : #color + #842210;
background-color: darken(#blend, 30%);
}
#bggenerator (#color, #url, #rest) {
background: "#{color} url('#{url}') #{rest}";
}
.mtStandard {
#genMarginTop(40px);
}
.someClass {
.mtStandard;
#bggenerator(#FFF, "bgimage.png", left top no-repeat);
//more of this stuff
}
Or something that does even more exciting stuff with the arguments
UPDATE LESS 1.7+ (Works as Desired)
The .#{name} syntax will now work just as the original question had desired.
LESS 1.4+ Workaround to Actually Use Dynamic Class Names
I came up with a work around for this while working on another question, so I'm posting it as a second answer, since it goes in a totally different direction than my earlier answer.
This solution requires a couple of steps (so is not as convenient as a final fix in LESS would be), but would give actual functionality of being able to use dynamically generated class names.
First: Define your dynamic classes
This is just as you planned.
#genMarginTop (#name, #size) {
.#{name} { margin-top: #size; }
}
#genMarginTop(mtStandard, 40px);
#genMarginTop(mtHalf, 20px);
Second: Compile that file into CSS
So lets say you compile your dynamicClasses.less into dynamicClasses.css. This causes the dynamic class names to "resolve" to actual classes.
Third: Import that CSS as LESS into a 2nd LESS file that uses the dynamic class names
Using type casting for #import, we do this:
#import (less) dynamicClasses.css;
This takes those resolved class names in the dynamicClasses.css file and imports them as LESS, which makes all the class names now available as mixins. So you can do as you desired:
.someClass {
.mtStandard; // will work
// more stuff
}
I agree. It looks like LESS does not register those classes for mixin purposes.
Incomplete Solution
This LESS code:
#genMarginTop (#name, #size) {
#genMarginTopNameCheck: #name;
.get(#name) when (#name = #genMarginTopNameCheck) { margin-top: #size; }
.#{name} { .get(#name); }
}
#genMarginBot (#name, #size) {
#genMarginBotNameCheck: #name;
.get(#name) when (#name = #genMarginBotNameCheck) { margin-bottom: #size; }
.#{name} { .get(#name); }
}
#genMarginTop(mtStandard, 40px);
#genMarginBot(mbStandard, 20px);
#genMarginTop(mtSpecial, 80px);
.myClass {
.get(mtStandard);
.get(mbStandard);
}
.myClass2 {
.get(mtSpecial);
.get(mbStandard);
}
Generates this CSS
.mtStandard {
margin-top: 40px;
}
.mbStandard {
margin-bottom: 20px;
}
.mtSpecial {
margin-top: 80px;
}
.myClass {
/* NOTE the mtStandard definition is missing here !!! */
margin-bottom: 20px;
}
.myClass2 {
margin-top: 80px;
margin-bottom: 20px;
}
Explanation and Disscussion of Final Issue to Resolve
Each mixin is defining a guarded .get() mixin based off the #name to get the styles, and that is cross checked to a unique NameCheck variable name for that mixin. All your actual code is defined in the .get(), and that mixin is used to actually generate the .#{name} class code.
This works fine every time for generating the actual class name. However, the getter function at present is only working for the class name last defined by a use of the mixin. So as you can see above, my get call for mtStandard is not working because my setting of mtSpecial has apparently overwritten the #genMarginTop .get() mixin with the mtSpecial definition.
Now I assume you are going to want to call #getMarginTop and your other such mixins more than once, so obviously this is still an incomplete solution. I've figured out how you can get the class generated by the top level mixin to be used as a 'mixin' for another class using the .get(), but I haven't figure out how to make the .get() not get overridden when the top level mixin is called again.
For example if I have the following HTML:
<div class="someDiv"></div>
and this CSS:
.opacity {
filter:alpha(opacity=60);
-moz-opacity:0.6;
-khtml-opacity: 0.6;
opacity: 0.6;
}
.radius {
border-top-left-radius: 15px;
border-top-right-radius: 5px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
}
.someDiv {
background: #000; height: 50px; width: 200px;
/*** How can I reference the opacity and radius classes here
so this div has those generic rules applied to it as well ***/
}
Like how in scripting languages you have generic functions that are used often written at the top of the script and every time you need to use that function you simply call the function instead of repeating all the code every time.
No, you cannot reference one rule-set from another.
You can, however, reuse selectors on multiple rule-sets within a stylesheet and use multiple selectors on a single rule-set (by separating them with a comma).
.opacity, .someDiv {
filter:alpha(opacity=60);
-moz-opacity:0.6;
-khtml-opacity: 0.6;
opacity: 0.6;
}
.radius, .someDiv {
border-top-left-radius: 15px;
border-top-right-radius: 5px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
}
You can also apply multiple classes to a single HTML element (the class attribute takes a space separated list).
<div class="opacity radius">
Either of those approaches should solve your problem.
It would probably help if you used class names that described why an element should be styled instead of how it should be styled. Leave the how in the stylesheet.
You can't unless you're using some kind of extended CSS such as SASS. However it is very reasonable to apply those two extra classes to .someDiv.
If .someDiv is unique I would also choose to give it an id and referencing it in css using the id.
You can easily do so with SASS pre-processor by using #extend.
someDiv {
#extend .opacity;
#extend .radius;
}
Ohterwise, you could use JavaScript (jQuery) as well:
$('someDiv').addClass('opacity radius')
The easiest is of course to add multiple classes right in the HTML
<div class="opacity radius">
If you're willing and able to employ a little jquery, you can simply do this:
$('.someDiv').css([".radius", ".opacity"]);
If you have a javascript that already processes the page or you can enclose it somewhere in <script> tags. If so, wrap the above in the document ready function:
$(document).ready( function() {
$('.someDiv').css([".radius", ".opacity"]);
}
I recently came across this while updating a wordpress plugin. The them has been changed which used a lot of "!important" directives across the css. I had to use jquery to force my styles because of the genius decision to declare !important on several tags.
Just add the classes to your html
<div class="someDiv radius opacity"></div>
I had this problem yesterday. #Quentin's answer is ok:
No, you cannot reference one rule-set from another.
but I made a javascript function to simulate inheritance in css (like .Net):
var inherit_array;
var inherit;
inherit_array = [];
Array.from(document.styleSheets).forEach(function (styleSheet_i, index) {
Array.from(styleSheet_i.cssRules).forEach(function (cssRule_i, index) {
if (cssRule_i.style != null) {
inherit = cssRule_i.style.getPropertyValue("--inherits").trim();
} else {
inherit = "";
}
if (inherit != "") {
inherit_array.push({ selector: cssRule_i.selectorText, inherit: inherit });
}
});
});
Array.from(document.styleSheets).forEach(function (styleSheet_i, index) {
Array.from(styleSheet_i.cssRules).forEach(function (cssRule_i, index) {
if (cssRule_i.selectorText != null) {
inherit_array.forEach(function (inherit_i, index) {
if (cssRule_i.selectorText.split(", ").includesMember(inherit_i.inherit.split(", ")) == true) {
cssRule_i.selectorText = cssRule_i.selectorText + ", " + inherit_i.selector;
}
});
}
});
});
Array.prototype.includesMember = function (arr2) {
var arr1;
var includes;
arr1 = this;
includes = false;
arr1.forEach(function (arr1_i, index) {
if (arr2.includes(arr1_i) == true) {
includes = true;
}
});
return includes;
}
and equivalent css:
.test {
background-color: yellow;
}
.productBox, .imageBox {
--inherits: .test;
display: inline-block;
}
and equivalent HTML :
<div class="imageBox"></div>
I tested it and worked for me, even if rules are in different css files.
Update: I found a bug in hierarchichal inheritance in this solution, and am solving the bug very soon
.