SCSS: Modify child's property when parent adds a class - sass

Using SCSS I was wondering if there if any more elegant way (using the &) to modify a child based on the parent adding a certain class.
The only way I found is this one: CSS - modify parent based on first child
This is what works (based on the answer above):
.parent {
.child {
opacity: 0.5;
}
}
.parent.certain-class {
.child {
opacity: 1.0;
}
}
What I would like to do is something like that:
.parent {
.child {
opacity: 0.5;
collapsed& {
opacity: 1.0;
}
}
}
Is there any way to achieve this in SCSS?

Yes there is a way to do it in scss.
Using the & followed by a classname without space.
Check Example below.
.parent {
.child {
opacity: 0.5;
}
&.certain-class {
.child {
opacity: 1.0;
}
}

Related

SASS select class that was chained to parent

I have the following HTML <div class="parent green"></div>
The green class may or may not be added. It is dynamic. It may also be another name.
In SASS how do I give properties to a .child element of parent when class green is chained to it?
I tried:
.parent {
.child {
.green & {
color: green;
}
}
}
It doesn't work.
I also tried the following which works but I am looking for something similar to the sass above. The code will become repeatable below because I have to add child each time for every dynamic class.
.parent {
&.green {
.child {
color: green;
}
}
}
I'm trying to get a structure like this if possible with sass:
.parent {
.child {
.green & { /* when .parent.green */
color: green;
}
.blue & { /* when .parent.blue */
color: blue;
}
.text-align-right & { /* when .parent.text-align-right */
text-align: right;
}
etc...
}
}
& is treated as parent selector reference in Sass, because of this your code doesn't work since it refers wrong selector.
Use of & directly will not help here, but your goal can be achieved by using mixins, for example:
#mixin child($class) {
&.#{$class} {
.child {
#content;
}
}
}
.parent {
#include child(green) {
color: green;
}
#include child(blue) {
color: blue;
}
#include child(text-align-right) {
text-align: right;
}
}
This piece of code produces result that you want to get, you can check in by yourself on sassmeister.

SASS: Add pseudo-class to grandparent ampersand

This SASS code...
#mixin test
{
#at-root #{selector-replace(&, '.class1', '.class1:nth-child(odd)')}
{
color:red;
}
}
.class1
{
.class2
{
#include test;
}
}
...compiles to:
.class1:nth-child(odd) .class2
{
color: red;
}
Is this possible when not using selector-replace (because I don't know how class1 is called)?
I just want to add a nth-child selector to the grandparent.
I am only allowed to change the mixin, not the original code.
Ok, this will do the trick:
#mixin test
{
// Find the first selector
$parent : nth(nth(&, 1), 1);
// Defines a list for the rest of the selectors
$rest : ();
// For each selector of &, starting from the second
#for $i from 2 through length(nth(&, 1)) {
// Adds the selector to the list of the "rest of the selectors"
$rest: append($rest, nth(nth(&, 1), $i));
}
// Adds the selector at root
#at-root #{$parent}:nth-child(odd) #{$rest} {
color: red;
}
}
.class1
{
.class2
{
#include test;
}
}
This compiles to:
.class1:nth-child(odd) .class2 {
color: red;
}
Hope it helps!

How can I target the syntactical parent when using the ampersand?

I'm trying to remove some duplication in my scss selector.
.container {
.operation {
color: green;
}
.row.is-active &,
.row:hover & {
.operation {
color: inherit;
}
}
}
I tried rewriting it like this:
.container {
.operation {
color: green;
}
.row & {
&.is-active, &:hover {
.operation {
color: inherit;
}
}
}
}
However, this causes .is-active to be applied to .container instead of .row
How can I target the syntactical parent when using the ampersand ?
I took some time to answer the question again, as I mis-understood it initially. Unfortunately there is absolutely no way possible to do this in SASS at the moment. Even when trying to make use of the more advanced SASS functions to manipulate selectors and strings it is not possible.
There is some Good News
It is possible to do using Stylus.
I have created a live Example on codepen.
// Stylus - not SASS
.container {
.operation {
color: green;
}
.row {
^[-1..-1]:is-active ^[0], ^[-1..-1]:hover ^[0] {
.operation {
color: inherit;
}
}
}
}
I hope this helps you in some way, at the very least it might provide you with an option, but unfortunately SASS cannot achieve what you are attempting.

In Sass, how to reference two parent classes with ampersand to compound with an element?

Using the method found here, it works, but not for two parent classes.
For instance:
.one, .two {
#at-root a#{&} {
color: blue;
}
}
Produces:
a.one, .two {
color: blue;
}
Rather than the intended:
a.one, a.two {
color: blue;
}
Is there any way to get the intended result using a similar method?
You want to use the selector-append() function instead:
.one, .two {
#at-root #{selector-append(a, &)} {
color: blue;
}
}
Using interpolation on the parent selector causes Sass to evaluate it as a string (because that's what interpolation does). This only makes it acceptable to use when you have a single selector. The selector-append (and all other selector-* functions) will evaluate the selector as a list of selectors, appending your desired item to each selector in the list.
This is now possible in pure CSS with the :is() matching pseudo-class:
.one, .two {
&:is(a) {
color: blue;
}
}
Or you simply switch to stylus:
.one,
.two {
a& {
color: blue;
}
}
Codepen Example

In Sass, How do you reference the parent selector and exclude any grandparent?

I have the following sass code:
.class{
label{
color:#fff;
.disabled &{color:#333; }
}
}
which outputs
.disabled .class label
Is there a way to output the parent selector without any grandparent selectors being included? Like so:
.disabled label
There's no way I know of in SASS to pick and choose from ancestor selectors when using a parent reference. With your code, though, a little reorganization can get you the same result:
label {
.class & {
color: #fff;
}
.disabled & {
color:#333;
}
}
Compiles to:
.class label {
color: #fff; }
.disabled label {
color: #333; }
Even though hopper is not enterly wrong, you can actually select grand-parent with variables.
You can achieve what you want with this:
.class{
label{
color:#fff;
$selector: nth(&,1);
$direct-parent: nth($selector, length($selector));
#at-root #{$direct-parent} {
.disabled &{color:#333; }
};
}
}
Which will generate this css:
.class label {
color: #fff;
}
.disabled label {
color: #333;
}
The parent selector is always a reference to the entire resolved selector from the previous level of nesting. There is no concept of "parent" or "grandparent", especially when concatenating selectors or using the parent selector at the end muddies the water.
Disclaimer: I do not recommend doing this unless you really really need to.
Starting with Sass 3.4, you can extract portions of a selector by using & as a variable. When used this way, you'll get a list of list of strings (which can be looped over, etc.).
Extracting a part or slice of a selector
This function here uses the same style of arguments as the string-slice function:
#function selector-slice($sel, $start: 1, $end: -1) {
$collector: ();
#each $s in $sel {
// calculate our true start and end indices when given negative numbers
$_s: if($start > 0, $start, length($s) + $start + 1);
$_e: if($end > 0, $end, length($s) + $end + 1);
$c: ();
#for $i from $_s through $_e {
$c: append($c, nth($s, $i));
}
// prevent duplicates from creeping in
#if not index($collector, $c) {
$collector: append($collector, $c);
}
}
#return $collector;
}
/* complex example */
.one-a, .one-b {
two {
three {
color: red;
&:before {
#at-root #{selector-slice(&, 2, 3)} {
color: green;
}
}
}
}
}
/* your example */
.class {
label {
color:#fff;
#at-root #{selector-slice(&, -1, -1)} {
.disabled & {
color:#333;
}
}
}
}
Output:
/* complex example */
.one-a two three, .one-b two three {
color: red;
}
two three:before {
color: green;
}
/* your example */
.class label {
color: #fff;
}
.disabled label {
color: #333;
}
As an added bonus, you can use this function to reverse the order of the selectors by passing in the larger index before the smaller one.
.one-a, .one-b {
two {
three {
color: red;
&:before {
#at-root #{selector-slice(&, 3, 2)} {
color: green;
}
}
}
}
}
Output:
.one-a two three, .one-b two three {
color: red;
}
three:before two {
color: green;
}
Related: Modifying the middle of a selector in Sass (adding/removing classes, etc.)
Replacing one class with another
Alternately, you could just use the selector-replace function from the standard library if what you're looking to do is replace one class with another.
.class {
label {
color:#fff;
#at-root #{selector-replace(&, '.class', '.disabled')} {
color:#333;
}
}
}
Output:
.class label {
color: #fff;
}
.disabled label {
color: #333;
}

Resources