How can I refer to only the parent element in Sass? - css

Apologies for the vague title; I appreciate suggestions. I've looked for similar questions but none seem (to me) to be asking the exact same thing.
Say I have this piece of CSS:
nav a {
color: blue;
}
nav li.selected a {
color: red;
}
What would be the recommended/preferred/easiest way to achieve this with nesting in Sass? I'd like to target a when it's a child of li.selected (i.e., referring to exactly one level above itself).
nav {
a {
color: blue;
??? {
color: red;
}
}
}
Here's what I've tried to no avail:
nav {
a {
color: blue;
li.selected & {
color: red;
}
}
}
/* results in li.selected nav a, not nav li.selected a */
nav {
$foo: &;
a {
color: blue;
#at-root #{$foo} li.selected & {
color: red;
}
}
}
/* results in nav li.selected nav a, not nav li.selected a */

Sorry if i get it wrong, but is this what you mean?
nav {
a {
color: blue;
}
li.selected a {
color: red;
}
}

Related

How to add a tag in front of a sass rule?

I have this scss:
.nav {
&__item {
color: black;
}
}
This compiles to:
.nav__item
Is it possible to modify the above sccs so that it compiles with a tag in front of it, like the following?
a.nav__item
OR
li.nav_item
Here is one way using #at-root. This way avoids having to declare &__item twice.
.nav {
&__item {
color: black;
#at-root {
ul#{&} {
display: block;
}
}
}
}
Complies to
.nav__item {
color: black;
}
ul.nav__item {
display: block;
}
Try this:
.nav {
a#{&}__item {
color: black;
}
}
Output
.nav a.nav__item {
color: black;
}
Even though this is possible using
.nav {
a#{&}__item {
color: black;
}
}
I would highly encourage you to write it like this
a,
li {
&.nav {
&__item {
color: black;
}
}
}

Target immediate parent in LESS from within nested classes

So I have a bunch of nested classes. I'd like to target the IMMEDIATE parent from deep within the nest. So for example, I have:
.navbar {
ul {
li {
a {
.ACTIVE & {
background: red;
}
}
}
}
}
The above results in:
.ACTIVE .navbar ul li a {
background: red;
}
But what I am trying to achieve is:
.navbar ul li.ACTIVE a {
background: red;
}
Is this possible?
Do get to that path using LESS, you'd do something like
.navbar {
& > ul {
& > li {
& > a {
&.active {
background: red;
}
}
}
}
}
This will result the following selector:
.navbar > ul > li > a.active { background: red; }
EDIT:
Sorry, totally missed the most important part.... So, to get what you're trying to achieve :
.navbar {
ul {
li {
&.ACTIVE{
a {
background: red;
}
}
}
}
}

Sass only selecting parent [duplicate]

I have the following SCSS for styling links in my menu:
nav {
ul {
li {
a {
color: red
}
}
}
ul.opened {
li {
a {
color: green
}
}
}
}
Which generates the following (correct) CSS:
nav ul li a {
color: red;
}
nav ul.opened li a {
color: green;
}
I tried modifying my JavaScript to apply the class to the nav element instead, and use selector-append() in Sass to append the class. But that seems to do the appending in the wrong order (and if the arguments are reversed, the class is appended to the last element!):
nav {
ul {
li {
a {
color: red;
#at-root #{selector-append('.opened', &)} {
color: green;
}
}
}
}
}
Output (incorrect!):
nav ul li a {
color: red;
}
.openednav ul li a {
color: green;
}
Is there a way the SCSS can be rewritten so that the class can be correctly appended without having to duplicate selectors (similar to the selector-append() method)?
The short answer
Since the element we want to replace has a unique name, what we're looking for is this:
nav {
ul {
li {
a {
color: red;
#at-root #{selector-replace(&, 'ul', 'ul.opened')} {
color: green;
}
}
}
}
}
The long answer
Manipulating selectors is extremely dirty, and I would advise against it unless you absolutely had to. If you're overqualifying your selectors by specifying things like table tr td or ul li, then start by simplifying: tr and ul are both redundant in these selectors (unless you're trying to avoid styling elements under an ordered list). Adjust your nesting to be simpler, etc.
Starting with Sass version 3.4, there are 2 important features that allow you to modify selectors.
Selector functions
The parent selector can be stored in a variable
Example:
.foo ul > li a, .bar {
$sel: &;
#debug $sel;
}
You'll always get a list of list of strings because selectors can be chained together with a comma, even when you have only one selector.
.foo ul > li a, .bar { ... }
(1 2 3 4 5), (1)
You'll note that the descendant selector is being counted here (lists in Sass can be either space or comma delimited). This is extremely important to remember.
When selector-replace() doesn't work
The selector-replace() function does not work in the following cases:
The selector you want to replace is not unique (eg. ul ul li)
You want to insert one or more selectors (eg. ul ul li -> ul ul ul li)
You want to remove a selector (eg. ul > li -> ul li)
In this case, you'll need to loop over the selectors and you'll need to know which position you want to modify. The following function will take a function and apply it to a specific position in your selector using the magic of the call() function.
#function selector-nth($sel, $n, $f, $args...) {
$collector: ();
#each $s in $sel {
$modified: call($f, nth($s, $n), $args...);
$collector: append($collector, set-nth($s, $n, $modified), comma);
}
#return $collector;
}
Append a class (when the selector isn't unique or you don't know its name)
The function we need here takes 2 arguments: the original selector and the selector you'd like to append to it. Uses simple interpolation to do the job.
#function append-class($a, $b) {
#return #{$a}#{$b};
}
.foo, .bar {
ul > li a {
color: red;
#at-root #{selector-nth(&, -2, append-class, '.baz')} {
color: blue;
}
}
}
Output:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > li.baz a, .bar ul > li.baz a {
color: blue;
}
Insert a selector
This function also takes 2 arguments: the original selector and the selector you'd like to insert before it.
#function insert-selector($a, $b) {
#return $b $a;
}
.foo, .bar {
ul > li a {
color: red;
#at-root #{selector-nth(&, -2, insert-selector, '.baz')} {
color: blue;
}
}
}
Output:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > .baz li a, .bar ul > .baz li a {
color: blue;
}
Remove a selector
Removing a selector is as simple as replacing your selector with an empty string.
#function remove-selector($sel) {
#return '';
}
.foo, .bar {
ul > li a {
color: red;
#at-root #{selector-nth(&, -2, remove-selector)} {
color: blue;
}
}
}
Output:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > a, .bar ul > a {
color: blue;
}
TL;DR
Selectors are just a lists. Any list manipulation functions will work on it and you can loop over it to modify it as necessary.
So yeah, don't do it unless you really really really need to. If you've decided you still need it, I've packaged these functions up into the selector-nth library.
I made a mixin that solves this problem.
Github: https://github.com/imkremen/sass-parent-append
Example: https://codepen.io/imkremen/pen/RMVBvq
Usage (scss):
.ancestor {
display: inline-flex;
.grandparent {
padding: 32px;
background-color: lightgreen;
.parent {
padding: 32px;
background-color: blue;
.elem {
padding: 16px;
background-color: white;
#include parent-append(":focus", 3) {
box-shadow: inset 0 0 0 8px aqua;
}
#include parent-append(":hover") {
background-color: fuchsia;
}
#include parent-append("p", 0, true) {
background-color: green;
}
}
}
}
}
Result (css):
.ancestor {
display: inline-flex;
}
.ancestor .grandparent {
padding: 32px;
background-color: lightgreen;
}
.ancestor .grandparent .parent {
padding: 32px;
background-color: blue;
}
.ancestor .grandparent .parent .elem {
padding: 16px;
background-color: white;
}
.ancestor:focus .grandparent .parent .elem {
box-shadow: inset 0 0 0 8px aqua;
}
.ancestor .grandparent .parent:hover .elem {
background-color: fuchsia;
}
.ancestor .grandparent .parent p.elem {
background-color: green;
}

LESS nesting multuple psuedo elements/classes not working

I'm trying to get the output:
#parent a:hover,
#parent a:link,
#parent a:visited {
color: #000;
}
I am using this LESS:
#parent {
a {
:link, :hover, :visited {
color: #000;
}
}
}
it's not working.
You need to use the LESS parent selector &:
#parent a {
&:link, &:hover, &:visited {
color: #000;
}
}

Sass referencing parent selectors using the ampersand character within nested selectors

Just when I thought Sass was the coolest thing since sliced bread, it had to go and let me down. I'm trying to use the ampersand to select a parent of a nested item. It's a complex selection and its returning some unexpected results...
My sass:
.page--about-us {
a {
text-decoration:none;
}
.fa-stack {
.fa {
color:pink;
}
a & {
&:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
}
}
Outputted CSS:
.page--about-us a {
text-decoration: none;
}
.page--about-us .fa-stack .fa {
color: pink;
}
a .page--about-us .fa-stack:hover .fa-circle-thin {
color: red;
}
a .page--about-us .fa-stack:hover .fa-twitter {
color: blue;
}
Expected Output (Note the placement of the a tag):
.page--about-us a {
text-decoration: none;
}
.page--about-us .fa-stack .fa {
color: pink;
}
.page--about-us a .fa-stack:hover .fa-circle-thin {
color: red;
}
.page--about-us a .fa-stack:hover .fa-twitter {
color: blue;
}
Demo:
http://sassmeister.com/gist/8ed68bbe811bc9526f15
You can store the parent selector in a variable!
Take the following BEM-like SASS:
.content-block {
&__heading {
font-size: 2em;
}
&__body {
font-size: 1em;
}
&--featured {
&__heading {
font-size: 4em;
font-weight: bold;
}
}
}
The selector inside of .content-block--featured is going to be .content-block--featured .content-block--featured__heading which might not be what you're after.
It's not as elegant as the single ampersand but you can stash the parent selector into a variable! So to get what you might be after from the above example without hard-coding the parent selector:
.content-block {
$p: &; // store parent selector for nested use
&__heading {
font-size: 2em;
}
&__body {
font-size: 1em;
}
&--featured {
#{$p}__heading {
font-size: 4em;
font-weight: bold;
}
}
}
So, OP, in your case you might try something like this:
.page--about-us {
$about: &; // store about us selector
a {
text-decoration:none;
}
.fa-stack {
.fa {
color:pink;
}
#{$about} a & {
&:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
}
}
This is the normal behavior, as described in Sass documentation (link):
& will be replaced with the parent selector as it appears in the CSS. This means that if you have a deeply nested rule, the parent selector will be fully resolved before the & is replaced.
Meaning:
.foo {
.bar {
.baz & {
color: red;
}
}
}
Will render as:
.baz .foo .bar {
color: red;
}
And not:
.baz .bar {
color: red;
}
The right way to get your expected result is this one:
.page--about-us {
a {
text-decoration:none;
.fa-stack:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
.fa-stack {
.fa {
color:pink;
}
}
}

Resources