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;
}
Related
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 :)
Assuming that I have the following HTML:
<div class="navigation__item">
<span class="navigation__item__icon"></span>
</div>
I want to apply some rules to an icon, when hovering an item, which can be described with the following CSS:
.navigation__item__icon {
color: black;
}
.navigation__item:hover .navigation__item__icon {
color: white;
}
I can achieve this using the following SCSS:
.navigation__item {
&:hover {
.navigation__item__icon { <-- here
color: white;
}
}
&__icon {
color: black;
}
}
Here, is there any way to avoid writing navigation__item? Something like "parent rule \ element".
I like Sass for logical structure so that if I want to rename the whole navigation block with elements, I can simply change navigation class name in the root, and everything is renamed. This case breaks this advantage.
Update: Actually, I have found a way to do this without using {} braces. & can be repeated more than once:
.navigation__item {
&:hover &__icon {
color: white;
}
&__icon {
color: black;
}
}
It is great, but it doesn't make much sense if I have many rules and rules for &:hover itself. The question is still open - is this possible to access sibling element definition from within the {} block.
In Stylus there is a Partial reference but I don't know anything similar in SASS. One solution could be using a variable for the parent selector:
.navigation__item {
$selector: &;
&:hover {
#{$selector}__icon {
color: white;
}
}
&__icon {
color: black;
}
}
Is usefull is you change navigation__item class for another.
EDIT: I had used a wrong example, it's OK now.
I recently started using BEM methodology and I'm confused about class inheritance, or rather - when we talk about BEM - some use cases of modifiers.
Let's look at this example, I have a simple element with few children
.b-content { width: 100%; }
.b-content__image { display: block; }
.b-content__date { font-size: 14px; }
.b-content__title { font-size: 30px; }
.b-content__text { font-size: 16px; }
Now I want to reuse my .b-content block with slightly different styles, so I use modifier .m-compact and now I'm not sure what approach is the right one (in BEM).
Whether I should append modifier class to all elements (which I find more valid according to documentation):
.b-content.m-compact { width: 50%; }
.b-content__image.m-compact { display: none; }
.b-content__date.m-compact { font-size: 12px; }
.b-content__title.m-compact { font-size: 24px; }
.b-content__text.m-compact { font-size: 14px; }
or should I append modifier only to the parent element:
.b-content.m-compact { width: 50%; }
.b-content.m-compact .b-content__image { display: none; }
.b-content.m-compact .b-content__date { font-size: 12px; }
.b-content.m-compact .b-content__title { font-size: 24px; }
.b-content.m-compact .b-content__text { font-size: 14px; }
I find this second method more logical, you know, since I'm writing cascading styles and in real world if I want to write e-mail to 10 people, I would write one and just add more recipients, but on the other hand I realize BEM is practically non-cascading approach.
So what should I use and why?
As you point out in the last lines of your question, when doing BEM you should avoid cascading so, as a corollary to this, you don't have to repeat the modifier where it isn't needed.
For your Modifier I'd write something like this:
.b-content--m-compact {
width: 50%;
}
In your example the Block and the Modifier set only the width, so this is a limited use case. In general it comes handy to use some kind of CSS preprocess to ease the code writing, e.g. in SASS:
.my-block
width: 100%
color: red
&--modifier
#extend .my-block
border: 1px solid red
which will results in:
.my-block, .my-block--modifier {
width: 100%;
color: red;
}
.my-block--modifier {
border: 1px solid red;
}
Modifier in BEM looks like this: .block_modName_modValue
You can add additional class - but it's not BEM. And also modifiers have a name and value.
Block in BEM set namespace
So you set default styles for blocks and all unique(that can be changed) place in css with modifiers. This way your styles don't messed up.
To do this you need:
Place common styles in block styles(.portfolio)
Place unique style(with modifiers) like this.(portfolio_theme_list)
In css you don't need to separate this(preprocessor will be needed).
.portfolio {
/* common styles */
&_theme_list {
/* modifiers style */
}
}
In BEM project-stub(template engine) it would look like this:
If you add modifier to block. Then compile(bemjson) to html.
{
block : 'portfolio',
mods : { theme : 'list' },
}
You will see this code
<div class="portfolio portfolio_theme_list">
</div>
You write elements correctly and understand that they need to be separated(without inheritence).
So now you need just define styles for your block with modifier(portfolio_theme_list).
You have 2 options:
1) If you have 2 different blocks - you need separate common and
unique styles. Unique styles place in styles with modified blocks.
2) If you have only 1 different block & you already have styles on
this blocks. Then you can override and don't separate common
styles(but it can cause pain if you add another modifier/instance)
How do I extend a Less class which is dynamically formed using & combinator?
Less which generates expected output:
.hello-world {
color: red;
}
.foo {
&:extend(.hello-world);
font-size: 20px;
}
Expected CSS output:
.hello-world,
.foo {
color: red;
}
.foo {
font-size: 20px;
}
Less does not generate expected output:
.hello {
&-world {
color: red;
}
}
.foo {
&:extend(.hello-world);
font-size: 20px;
}
Unexpected CSS output:
.hello-world {
color: red;
}
.foo {
font-size: 20px;
}
Extending a dynamically formed selector (loosely using the term) like that is currently not possible in Less. There is an open feature request to support this. Till it is implemented, here are two work-around solutions to it.
Option 1:
Write the contents of .hello and .hello-world selectors into a separate Less file (say test.less), compile it to get the CSS. Create another file for the rules of .foo, import the compiled CSS as a Less file (using the (less) directive) and then extend the .hello-world as you had originally intended to.
test.less:
.hello {
&-world {
color: red;
}
}
extended-rule.less:
#import (less) "test.css";
.foo {
&:extend(.hello-world);
font-size: 20px;
}
Compiled CSS:
.hello-world,
.foo {
color: red;
}
.foo {
font-size: 20px;
}
This method would work because by the time the test.css file is imported, the selector is already formed and is no longer dynamic. The drawback is that it needs one extra file and creates dependency.
Option 2:
Write a dummy selector with rules for all properties that need to be applied to both .hello-world and .foo and extend it like:
.dummy{
color: red;
}
.hello {
&-world:extend(.dummy) {};
}
.foo:extend(.dummy){
font-size: 20px;
}
This creates one extra selector (dummy) but is not a big difference.
Note: Adding my comment as an answer so as to not leave the question unanswered and also because the work-around specified in the thread linked in comments doesn't work for me as-is.
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;
}