What does an "&" before a pseudo element in CSS mean? - css

In the following CSS taken from Twitter Bootstrap what does the ampersand (&) character mean?
.clearfix {
*zoom: 1;
&:before,
&:after {
display: table;
content: "";
}
&:after {
clear: both;
}
}

That's LESS, not CSS.
This syntax allows you to nest selector modifiers.
.clearfix {
&:before {
content: '';
}
}
Will compile to:
.clearfix:before {
content: '';
}
With the &, the nested selectors compile to .clearfix:before.
Without it, they compile to .clearfix :before.

A nested & selects the parent element in both SASS and LESS. It's not just for pseudo elements, it can be used with any kind of selector.
e.g.
h1 {
&.class {
}
}
is equivalent to:
h1.class {
}

Here is an SCSS/LESS example:
a {
text-decoration: underline;
#include padding(15px);
display: inline-block;
& img {
padding-left: 7px;
margin-top: -4px;
}
}
and its equivalent in CSS:
a {
text-decoration: underline;
#include padding(15px);
display: inline-block;
}
a img {
padding-left: 7px;
margin-top: -4px;
}

'&' is useful feature in both Sass and Less preprocessor. For nesting it's used. It is time saver when we compare to CSS.

Related

Sass Ampersand nesting with pseudo selectors

Trying to utlize the SASS Ampersand to get the following css output. It is not working when we use Ampersand with inside pesudo selector.
CSS
.test:first-child .test-image { display: block; }
SASS
.test {
&:first-child {
display: inline-block;
&-image {
display: block;
}
}
}
Above code basically cascading the -image with first-child.
This is because the ampersand is just concatenating the parent with the child. If you want the compiled CSS to look like your example you need to do this:
.test {
&:first-child &-image{
display: block;
}
}
If you are trying to achieve
.test:first-child .test-image { display: block; }
With your code it is getting compiled as this
.test:first-child-image {
display: block;
}
Instead ,you can simply write it as this .
.test:first-child {
.test-image {
display: block;
}
}
Hope it helps
It sounds like you have mistaken how the ampersand works in Sass. The ampersand is essentially a shorthand for writing each outer selector and then adding the selector after it onto the end. As .test-image is distinct from .test, you should specify it as follows.
.test {
&:first-child {
.test-image {
display: block;
}
}
}
Compiles to
.test:first-child .test-image {
display: block;
}

Referencing parent selectors using the ampersand character, and :not

The article here shows a brilliant example of using an ampersand to reference a parent selector, like so:
h3 {
font-size: 20px;
margin-bottom: 10px;
.some-parent-selector & {
font-size: 24px;
margin-bottom: 20px;
}
}
This works as expected. However, I am following BEM principles and do not wish to have overriding Sass. As you can see in my screenshot below, the styles from .js-tabby overrides the default .tabs code.
I've tried things like:
.tabs {
display: none;
visibility: hidden;
.js-tabby &:not(&) {
display: none;
visibility: hidden;
}
.js-tabby & {
display: block;
visibility: visible;
}
}
But alas, it doesn't work.
As I was writing this question, I had a thought that seemed crazy, but it worked! So, here's my first Q&A ever:
.tabs {
html:not(.js-tabby) & {
display: none;
visibility: hidden;
}
html.js-tabby & {
display: block;
visibility: visible;
}
}
The plugin I'm using attaches the .js-tabby class to the html element, so I targeted that and BOOM! It works.
This has been something that's plagued me for so long, and I hope I can save someone else further frustration.

BEM with SASS and :hover

What's the proper way of declaring active/focus/hover states using BEM with SASS? For example, I have this structure:
<div class="card">
<img class="card__image" src="..." alt="">
<div class="card__overlay">
<div class="card__title"></div>
</div>
</div>
And the SCSS:
.card {
&__image {
}
&__overlay {
}
&__title {
}
}
And I want to modify the elements when hovering on the block.
This doesn't work:
.card {
&__overlay {
display: none;
}
&:hover {
&__overlay {
display: block;
}
}
}
And having to write the whole .project__image just for this one instance seems wrong.
Is there any other way to accomplish this?
You can achieve the desired result using the Sass ampersand selector, without using variables or interpolation.
Referencing parent selectors by using the ampersand (&) can be a
powerful tool, if used right. There are simple uses of this feature as
well as some very complex uses of this feature.
For example:
.card {
&__overlay {
display:none;
}
&:hover & {
&__overlay {
display: block;
}
}
}
Results in:
.card__overlay {
display: none;
}
.card:hover .card__overlay {
display: block;
}
This code uses fewer language constructs (e.g. no use of variables or interpolation) and so is arguably a cleaner implementation.
Read more about interpolation:
http://sass-lang.com/documentation/file.SASS_REFERENCE.html#interpolation_
SCSS:
.card {
$root: &;
&__overlay {
display: none;
#{$root}:hover & {
display: block;
}
}
}
RESULT:
.card__overlay {
display: none;
}
.card:hover .card__overlay {
display: block;
}
PS. It is similar to #alireza safian post, but with this way you don't need to duplicate class name. Variable $root do it for you :)
Alternative way:
Use variable instead of ampersand for third level.
Link
SASS:
$className: card;
.card {
&__overlay {
display: none;
}
&:hover {
.#{$className}__overlay {
display: block;
}
}
}
CSS:
.card__overlay {
display: none;
}
.card:hover .card__overlay {
display: block;
}

Sass to css output

I think this is the way to do it but my css output is not what I was expecting:
SOURCE CODE
This is my scss file:
footer.page-footer
{
margin-top: 0;
&,
nav
{
background-color: $blue;
}
}
This is the css output:
footer.page-footer
{
margin-top: 0;
}
footer.page-footer,
footer.page-footer nav
{
background-color: #50a4b1;
}
How can I make the second outputted css's selector be simply footer.page-footer, nav instead of footer.page-footer, footer.page-footer nav?
You can use the #at-root directive to produce a rule that is generated outside its definition scope but that retains the value of its parent (&)
footer.page-footer {
margin-top: 0;
#at-root {
#{&},
nav {
background-color: blue;
}
}
}
Output:
footer.page-footer {
margin-top: 0;
}
footer.page-footer,
nav {
background-color: blue;
}
<p class="sassmeister" data-gist-id="93b3b22a2888f2f5f86b" data-height="480" data-theme="tomorrow">Play with this gist on SassMeister.</p><script src="http://cdn.sassmeister.com/js/embed.js" async></script>
Sassmeister

Selecting a class in Sass

I have a form which has a label on each input:
<label>My Label</label>
I style it with:
label {
&:before {
background-color: red;
}
}
I add a class to each label:
<label class="blue">My Label</label>
<label class="yellow">My Label</label>
How can I select the before for each class in Sass?
label {
&:before {
background-color: red;
&.blue {
background-color: blue; //???????
}
}
}
Please note, the reason I use the ::before selector is for something more complex than changing a labels background colour, I have just used this as a simple example.
Here are a couple ways of writing SASS that will generate label:before, label.blue:before, label.yellow:before
label {
&:before{
background-color:red;
}
&.blue:before{
background-color: blue;
}
&.yellow:before{
background-color:yellow;
}
}
label {
&:before{
background-color:red;
}
&.blue{
&:before{
background-color: blue;
}
}
&.yellow{
&:before{
background-color:yellow;
}
}
}
Generally a pseudo element needs to be at the end of a selector, and don't themselves have classes. Sass will render it as you write it. I am not to sure what the browser will do with.
Pseudo elements also usually have a content property, and it is that the styles are applied. The css above will not be applied unless a 'content' property is set somewhere else in your css.
It is the manipulation of the ::before and ::after that you get your standard clearfix solution you'll find in bootstrap[http://getbootstrap.com/css/#helper-classes-clearfix]
// Mixin itself
.clearfix() {
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
}
// Usage as a Mixin
.element {
.clearfix();
}
You need to write it like that :
label {
&:before {
background-color: red;
}
&.blue{
background-color:blue;
}
}
If you use your version you will have label:before.blue instead of what you want label.blue

Resources