I'm confused about double ampersand behaviour in LESS compiler.
Look:
.heading {
&&--type-small {
font-size: 15px;
}
}
Will be compiled to:
.heading.heading--type-small {
font-size: 15px;
}
And thats good.
But.
.wrapper {
.heading {
&&--type-small {
font-size: 15px;
}
}
}
Will produce:
.wrapper .heading.wrapper .heading--type-small {
font-size: 15px;
}
It looks weird. No?
Is there any advice to make this code works like:
.wrapper .heading.heading--type-small {
font-size: 15px;
}
Thanks =)
What happens when you use an ampersand in a nested rule is that the default nested structure gets ignored in the output selector and the ampersand acts as a placeholder for the complete list of outer selectors and will just insert all the parent rules all the way to the top of the hierarchy (the "path" for all nesting levels above) ... no way around that.
So using the first one - & will just join (concatenate) the nested selector to the whole list of outer selectors (appearing as if it just added it to the parent selector) and act as a combinator - see "Nested rules" at lescss.org. But then when you use the second ampersand - your selector will end up including all outer rules once again - the .wrapper and all rules in between will be added twice now. So, the behavior is not really strange. See also my answer to this question: "How to refer to two previous elements / tags / classes with LESS?" and for some more functionality of & see also seven-phases-max's comments below. Or find some examples of & being used as a "path" placeholder under "Advanced Usage of &" at lescss.org.
And to your concrete example:
I am not completely sure why you want to repeat the word "header" in the class name .header--type-small, if you are using it in addition to a class called .header ... I would just use additional classes such as .type-small, like so:
.wrapper {
//style for the wrapper
.heading{
//general style for the heading
&.type-small {
//style for the heading with class .type-small
font-size: 15px;
}
&.type-large {
//style for the heading with class .type-large ... and so on
}
}
}
with output CSS:
.wrapper .heading.type-small {
font-size: 15px;
}
but if you really really need the whole long string with the repeated names for some particular reason ... you could just do something like this:
.wrapper {
//style for the wrapper
.heading {
//general style for the heading
&.heading--type{
&-small {
//style for the heading with class .type-small
font-size: 15px;
}
}
}
}
with output CSS:
.wrapper .heading.heading--type-small {
font-size: 15px;
}
Related
I'm trying to slightly modify the styles of a block formatting plugin in Wordpress by overriding them in my own theme stylesheet. I use Sass but I'm new to it.
Pasting all of the selectors right out of Developer Tools works, but I know that's not the elegant/modular way to do it:
.an-accordion.an-accordion--v2.an-accordion.an-accordion--design-basic .an-accordion__heading {
color: gold
}
What's the right way to do this in Sass? I've tried something like this:
.an-accordion {
&--v2 {
&--design-basic {
&__heading {
color: gold;
}
}
}
}
but it doesn't work. I can tell I'm missing something about the way .an-accordion repeats.
You can use the power of local scoped string variables $something:... combined with the power of string interpolation #{...} and combine it with the current selector string & to create a compound selector for any combination of block, element, and modifier. Which I think is quite nice and readable:
.an-accordion {
$modifier-v2: #{&}--v2;
$modifier-design-basic: #{&}--design-basic;
$element-heading: #{&}__heading;
&#{$modifier-v2}#{$modifier-design-basic} {
#{$element-heading} {
color: gold;
}
}
}
which will result in:
.an-accordion.an-accordion--v2.an-accordion--design-basic .an-accordion__heading {
color: gold;
}
I tried it out on sassmeister.com
Note that I omitted the duplicated .an-accordion class in the selector; if this is important for you to increase the specifity you can insert it with #{&}.
BEM is about blocks, elements, and modifiers. Block scope is the biggest one, the element is some part inside the block and the modifier is optional and represents the status of your block-element. In Sass you can nest elements if they are parent and children and you don't need to repeat the parent element, in your stlesheet, if the beginning of your property is the same for both parent and child, but if the beginning is different you must repeat.
In a html like this:
<div class=" an-accordion an-accordion--v2 .an-accordion--design-basic .an-accordion__heading"></div>
You could have some scss code like this:
.an-accordion{
color: #000;
&__heading{
background-color: tomato;
}
&--v2{
font-weight: bold;
}
&--design-basic{
border: none;
}
}
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)
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;
}
I have this html markup:
<figure class="about half">
<figcaption class="head_section">
<h3>About</h3>
</figcaption>
</figure>
and this LESS set up:
figure {
//some style
&.half {
width: 48%;
}
figcaption {
h3 {
.about & & {
// what i need to do but isn't working
}
}
}
&.about {
h3 {
// some style i have which is working
}
}
}
So, my question is how to refer two previus class of figure.about so as not to be out of style h3. I hope that is clear... Much thx.
I'm still not sure I understand what you are trying to achieve here, as you haven't supplied a desired css output. The idea I kinda managed to extract from your question would be a css that looks like this:
figure {
/* some style */
}
figure.half {
width: 48%;
}
figure.about figcaption h3 {
/* what i need to do but isn't working */
}
is this correct?
Here is a way to get this to work with &:
figure {
/* some style */
&.half {
width: 48%;
}
}
figcaption {
h3 {
.about & {
/* what i need to do but isn't working */
}
}
}
The thing here is that the & returns the "path" for all nesting levels above, so you need to construct the nesting rules with exactly the ones you want to use (in our case I moved the figcaption out of the figure rule). So this should give you the selector appearance you want. However, the structure of the nested rules will often depend on the way you want to use #variable inheritance and scope referencing and in some cases it just makes it hard to build the structural selector exactly the way you want, but then there will almost never be the need for including the whole html structure in your selector - and just as in your case figure.about h3 will work just as well as figure.about figcaption h3, as you probably will not add more h3 elements into the figure container.
Is it possibile to add span3 class in a mixin to avoid putting it in every element in my HTML?
Something like:
.myclass {
.span3;
// other rules...
}
EDIT
I apologize I forgot to specify an important detail: span3 is a standard class of Bootstrap.
I didn't find its definition in any file of the Bootstrap framework.
New Answer (requires LESS 1.4.0)
What you actually desire is something known as extending in LESS and SASS terminology. For example, you want an HTML element (just an example)...
<div class="myclass"></div>
...to fully behave as if it had a span3 class from bootstrap added to it, but without actually adding that class in the HTML. This can be done in LESS 1.4.0 using :extend(), but still not easily, mainly because of the dynamic class generation of bootstrap will not be picked up by :extend().
Here is an example. Assume this initial LESS code (not dynamically generated .span3 classes as bootstrap does):
.span3 {
width: 150px;
}
.someClass .span3 {
font-size: 12px;
}
.someOtherClass.span3 {
background: blue;
}
You add this LESS code in 1.4.0:
.myclass {
&:extend(.span3);
}
Which produces this CSS:
.span3,
.myclass {
width: 150px;
}
.someClass .span3 {
font-size: 12px;
}
.someOtherClass.span3 {
background: blue;
}
NOTE how it did not automatically extend the other instances of .span3. This is different than SASS, but it only means you need to be a bit more explicit in extending. This has the advantage of avoiding excessive CSS code bloat.
To fully extend, simply add the all keyword in the extend() (this is updated from my original code, as I was unaware of the all option):
.myclass {
&:extend(.span3 all);
}
Which produces this:
.span3,
.myclass {
width: 150px;
}
.someClass .span3,
.someClass .myclass {
font-size: 12px;
}
.someOtherClass.span3,
.someOtherClass.myclass {
background: blue;
}
That makes your .myclass fully equivalent (in my example) to the .span3 class. What this means in your case, however, is that you need to redefine any dynamic class generations of bootstrap to be non-dynamic. Something like this:
.span3 {
.span(3);
}
This is so the :extend(.span3) will find a hard coded class to extend to. This would need to be done for any selector string that dynamically uses .span#{index} to add the .span3.
Original Answer
This answer assumed you desired to mixin properties from a dynamically generated class (that is what I thought your issue was).
Okay, I believe I discovered your issue. First of all, bootstrap defines the .spanX series of classes in the mixins.less file, so you obviously need to be sure you are including that in your bootstrap load. However, I assume it is a given that you have those included already.
Main Problem
The main issue is how bootstrap is generating those now, through a dynamic class name in a loop. This is the loop that defines the .spanX series:
.spanX (#index) when (#index > 0) {
.span#{index} { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
Currently, because the class name itself is being dynamically generated, it cannot be used as a mixin name. I don't know if this is a bug or merely a limitation of LESS, but I do know that at present time of writing, any dynamically generated class name does not function as a mixin name. Therefore, .span3 may be in the CSS code to put as a class in your HTML, but it is not directly available to access for mixin purposes.
The Fix
However, because of how they have structured the code, you can still get what you need, because as you can see above in the loop code, they use a true mixin itself to define the code for the .spanX classes. Therefore, you should be able to do this:
.myclass {
.span(3);
// other rules...
}
The .span(3) code is what the loop is using to populate the .span3 class, so calling it for your classes will give the same code that .span3 has. Specifically bootstrap has this defined in mixins.less for that mixin:
.span (#columns) {
width: (#fluidGridColumnWidth * #columns) + (#fluidGridGutterWidth * (#columns - 1));
*width: (#fluidGridColumnWidth * #columns) + (#fluidGridGutterWidth * (#columns - 1)) - (.5 / #gridRowWidth * 100 * 1%);
}
So you will get the width properties for the .span3 in your .myclass.
This is easy to accomplish with Less.js, but the real question is: "Should I mix structural grid classes with my non-structural classes?"
And the answer is no.
This is a bad idea, the advantage of a grid system is that it creates a separation of concerns between structural styling and other styling. I'm not saying "it should never, ever, ever be done". But in general, it shouldn't. I don't even like seeing this:
<div class="span3 sidebar">
<ul class="nav">
...
</ul>
</div>
Where the span3 is in the same div as the .sidebar class. The problem with this is that now your sidebar is not just "floating around" inside a column of the grid, it has become part of the grid - which (in general) makes it even more difficult to maintain your styles because of the workarounds you need to create to force this kind of styling to be responsive.
Well you can do it but you have to define .somethign first, in this example I will do it font-wight: bold; and font-size 20px. As you can see in second class .body I didn't have to define font-weight: bold; and font-size: 20px; I just added .something into it
.something {
font-weight: bold;
font-size: 20px;
}
.body {
background-color: gray;
border: ridge 2px black;
.something
}
You can see example here. http://jsfiddle.net/7GMZd/
YOU CAN'T DO IT THIS WAY
.body {
background-color: gray;
border: ridge 2px black;
.something {
font-weight: bold;
font-size: 20px;
}
}