SCSS append to root selector from child - css

I know in SCSS I can do this:
.foo {
color: blue;
a {
color: red;
.bar & { color: green; }
}
}
And get this:
.foo { color: blue; }
.foo a { color: red; }
.bar .foo a { color: green; }
But is there a way to append that selector, to result in:
.foo { color: blue; }
.foo a { color: red; }
.foo.bar a { color: green; }
Note this is a simple example, in my use case the rule is nested much deeper than this.

The & only works one level up. In your code example, you wouldn't be able to get the code you expect. You'll need to handle it in a different way.
.foo {
color: blue;
a {
color: red;
}
&.bar {
a {
color:green;
}
}
}

Apparently this only renders with phpsass compiler, so it is compiler specific.
Just get rid of your space before the &:
.foo {
color: blue;
a {
color: red;
.bar& { color: green; }
}
}
Compiles to:
.foo {
color: blue;
}
.foo a {
color: red;
}
.bar.foo a {
color: green;
}
Note that .foo.bar and .bar.foo are equivalent selectors (they both make a selection only if both classes are on the item), so the order does not matter in such a case. Now, whether this could work in a more deeply nested structure (which you state you have) all depends on what you are trying to target, but essentially this would add a class to the outmost level (your .foo equivalent) no matter how deep the structure.

There is a way to PREPEND that selector.
.foo {
color: blue;
a {
color: red;
#at-root .bar#{&} { color: green; }
}
}
This would compile to:
.foo { color: blue; }
.foo a { color: red; }
.bar.foo a { color: green; }
The #at-root rule causes everything proceeding it to be emitted at the root instead of using regular nesting.
You can qualify a selector by putting & to the right of the intended parent of the selector. Wrapping it in #{} allows you to escape the space.
If you use both, you can PREPEND the root parent selector.. but unfortunately, you are unable to append it.

You're having an XY problem. You need to override the color of a, but instead of asking for an optimal way to do that, you invent some weird construction and ask why it is not working.
The matter is that you don't need to put a inside .foo to override the color. An a gets it's color not from .foo directly, but from a's own color property.
By default color is set to inherit, which naturally inherits from parent. So to override a's color, you change it's color property from inherit to whatever you want. To do that, you don't need to put parent into the selector!
UPD: As cimmanon has corrected me below, the color of a is not inherited from parent, it uses its own default value instead.
So an optimal solution would be this:
.foo {
color: blue; }
a {
color: red;
.foo.bar & {
color: green; } }
Also, the fact that you want to override exactly for .foo.bar a indicates that you're overcomplicating things.
You only need .foo.bar a if you need to handle four different situations: a, .foo a, .bar a and .foo.bar a. Do you really have that much different colors for the links? Why?
Most certainly you could refactor your HTML structure, make it more semantic and be happy with code like this:
a {
color: red; }
#header {
color: blue; }
.cart-item
& a {
color: green; } }
With this code, the link can only be two different colors: green for cart items and red for everything else.
If you do have a good reason to have many colors, consider using a mixin:
#mixin link-with-color($color: red) {
a {
color: $color; } }
#header {
#include link-with-color(blue); }
.cart-item {
#include link-with-color(green); }

Related

Angular: SCSS / SASS compiler produces unwanted whitespaces [duplicate]

This question already has answers here:
Sass Nesting for :hover does not work [duplicate]
(2 answers)
Closed 3 years ago.
I have *.scss file in an Angular 7 project.
After compiling it, the compiler adds unwanted whitespace to the css, which leads to wrong results in the UI.
To reproduce the error go to...
https://www.sassmeister.com/
...copy and paste the following code.
$color-background-default: white;
$color-foreground-default: black;
$color-background-disabled: #d3d3d3;
$color-foreground-disabled: #808080;
$color-background-mouseover: #00a7dc;
$color-foreground-mouseover: white;
$color-background-mousedown: #00467F;
$color-foreground-mousedown: white;
.Tab
{
background-color: $color-background-default;
color: $color-foreground-default;
:hover
{
background-color: $color-background-mouseover;
color: $color-foreground-mouseover;
}
:active
{
background-color: $color-background-mousedown;
color: $color-foreground-mousedown;
border-color: $color-background-mousedown;
}
}
In the CSS box of Sassmeister you should see, that there are whitespaces between ".Tab" and "hover" and "active" that look like this:
.Tab {
background-color: white;
color: black;
}
//WHITESPACE AFTER Tab
.Tab :hover {
background-color: #00a7dc;
color: white;
}
//WHITESPACE AFTER Tab
.Tab :active {
background-color: #00467F;
color: white;
border-color: #00467F;
}
Now when I remove the whitespaces between Tab and hover and active it looks like this:
.Tab {
background-color: white;
color: black;
}
//NO WHITESPACE AFTER Tab!
.Tab:hover {
background-color: #00a7dc;
color: white;
}
// NO WHITESPACE AFTER Tab!
.Tab:active {
background-color: #00467F;
color: white;
border-color: #00467F;
}
The second option without whitespaces gives me the correct UI result.
My question: How can I avoid these whitespaces in Angular 7?
The parent selector, &, is a special selector invented by Sass that’s
used in nested selectors to refer to the outer selector. It makes it
possible to re-use the outer selector in more complex ways, like
adding a pseudo-class or adding a selector before the parent.
(from SASS official documentation)
So when you write rules for pseudo-class (before, after, hover, active etc.), to refer to the outer selector (only one level higher), put the ampersand like this:
.link {
color: blue;
&:hover {
color: green;
}
}
So, your SCSS code can be rewritten as:
$color-background-default: white;
$color-foreground-default: black;
$color-background-disabled: #d3d3d3;
$color-foreground-disabled: #808080;
$color-background-mouseover: #00a7dc;
$color-foreground-mouseover: white;
$color-background-mousedown: #00467F;
$color-foreground-mousedown: white;
.Tab
{
background-color: $color-background-default;
color: $color-foreground-default;
&:hover
{
background-color: $color-background-mouseover;
color: $color-foreground-mouseover;
}
&:active
{
background-color: $color-background-mousedown;
color: $color-foreground-mousedown;
border-color: $color-background-mousedown;
}
}
You're looking for the sass ampersand.
.Tab {
:hover {
...
}
}
...should be:
.Tab {
&:hover {
...
}
}
& means: "current selector". You use &:hover to specify
#{currentSelector}:hover.
Without the ampersand, it results into #{currentSelector} :hover and that's the way you want it to work for constructs like
.a {
.b {
...
}
}
... which parses as .a .b {...}.
A more ample explanation here.
Note: the ampersand also allows specifying a prefix to current selector. For example:
.a {
.b {
prop: value;
.c & {
prop: otherValue;
}
}
}
will parse into:
.a .b { prop: value; }
.c .a .b { prop: otherValue; }

Why does SASS modify chained selectors of the same name when using extend?

Given the following HTML / SASS code:
<div class="a">hello</div>
%color {
color: blue;
}
.a {
color: red;
}
.a.a {
#extend %color;
}
I was expecting the resulting color to be blue (due to the more specific .a.a selector1) with output something like this:
.a.a {
color: blue;
}
.a {
color: red;
}
But actually, the resulting color is red, with SASS output:
.a {
color: blue;
}
.a {
color: red;
}
I find this quite counter-intuitive!
Why does SASS refactor my .a.a selector to .a?
Just in case you don't believe me, here's a codepen demo (click view compiled css to see the CSS output)
NOTE:
This 'refactoring' of the selector only occurs to the declarations within the extend.
So in the following SASS:
%color {
color: blue;
}
.a.a {
#extend %color;
position: relative;
}
The output is:
.a {
color: blue;
}
.a.a {
position: relative;
}
(Codepen demo)
1See the spec:
Note: Repeated occurrences of the same simple selector are allowed and
do increase specificity.
By the looks of it, the result depends on the parsing engine. If you use DartSass v1.6.2 (default on sassmeister.com), it outputs your expected result:
.a.a {
color: blue;
}
.a {
color: red;
}
Check on sassmeister.com (you can also switch parsing engines there).
LibSass v3.5.2 creates the result you complained about:
.a {
color: blue;
}
.a {
color: red;
}

Compass (SASS) rule with pseudo class

Is it possible to generate css rule for element for normal state and some other pseudo state like this:
.heading-link, .heading-link:hover {
color: red;
}
with
.heading-link {
color: $state-info-text;
&:hover {
color: $state-info-text;
}
}
I got
.heading-link {
color: #538DA7;
}
.heading-link:hover {
color: #538DA7;
}
What is not as expected, plus I have to write rule for color twice.
You can use the parent selector (&) by itself, in addition to other selectors inside nested blocks.
DEMO
$state-info-text: #538DA7;
.heading-link {
&, &:hover {
color: $state-info-text;
}
}
Compiles to
.heading-link, .heading-link:hover {
color: #538DA7;
}
Yes. You can use Sass's built in #extend function for this.
$state-info-text: red;
.heading-link {
color: $state-info-text;
&:hover {
#extend .heading-link;
}
}
Gives the output:
.heading-link, .heading-link:hover {
color: red;
}

CSS-Less class extend class with pseudo class

I was wondering how I could do something like the following with less css:
.btn {
color : black;
}
.btn:hover {
color : white;
}
.btn-foo {
.btn;
&:hover {
.btn:hover;
}
}
Of-course this is just an example, what need to point is if there is any way to extend the pseudo-class in order to avoid re-type the properties of :hover pseudo class everywhere I need them. I know I could create a mixin for that but I'm wondering if I could avoid it.
Thanks
UPDATE:
If you can't modify external files just redefine the selectors, and add missing states:
.btn {
// not adding anything here, won't affect existing style
&:hover {
// adding my own hover state for .btn
background: yellow;
...
}
}
// this will make your foo button appear as in external style
// and have the :hover state just as you defined it above
.btn-foo {
.btn;
}
Better now? :)
You don't need pseudo class. It will just work :)
Try this:
.btn {
background: yellow;
&:hover { // define hover state here
background: green;
}
}
button {
.btn;
}
Each <button class='btn'> element you create will inherit whatever was defined, including hover state. I think it's one of the main amazing features of LESS.
Hope this helps.
In Less 1.4.0(1.4.1?)
This:
.btn {
color : black;
}
.btn:hover {
color : white;
}
.btn-foo:extend(.btn all) {
}
Expands to this:
.btn,
.btn-foo {
color: black;
}
.btn:hover,
.btn-foo:hover {
color: white;
}
Be cautious though, this:
.btn {
color : black;
}
.btn:hover {
color : white;
}
.abc .btn {
margin: 2px;
}
.btn-foo:extend(.btn all) {
}
Will output this:
.btn {
color : black;
}
.btn:hover {
color : white;
}
.abc .btn {
margin: 2px;
}
.btn-foo:extend(.btn all) {
}
I have not looked into SASS more than half an hour, but I believe the later case is its default (or only) #extend behavior.

SCSS selector pick

I just started to using sass/scss and i have a small issue. Let's assume this code:
.button {
color:#c00;
&:hover {
color:#000;
}
}
Everything is awesome and works as it supposed to. But.. Let's say I want to do different hovers depending of tag. So, if the tag is a span to show a color and if the tag is a a to show another color.
Is this possible without repeating some part of the selector?
Thanks!
No. Remember that in the end everything compiles to CSS.
The way to do it would be the following:
.button {
.green {
color:green;
&:hover { color:black; }
}
.red {
color:red;
&:hover { color:black; }
}
}
You would need to add a class though.
You could use the mixin approach but it's going to be more verbose.
I would do it like this:
.button {
color: red;
&:hover { color: black; }
}
span.button:hover { color: green; }
a.button:hover { color: blue; }
Have a play yourself here: http://tinkerbin.com/CBuHSGfV

Resources