How to reference SCSS sibling? - css

Assuming that I have the following HTML:
<div class="navigation__item">
<span class="navigation__item__icon"></span>
</div>
I want to apply some rules to an icon, when hovering an item, which can be described with the following CSS:
.navigation__item__icon {
color: black;
}
.navigation__item:hover .navigation__item__icon {
color: white;
}
I can achieve this using the following SCSS:
.navigation__item {
&:hover {
.navigation__item__icon { <-- here
color: white;
}
}
&__icon {
color: black;
}
}
Here, is there any way to avoid writing navigation__item? Something like "parent rule \ element".
I like Sass for logical structure so that if I want to rename the whole navigation block with elements, I can simply change navigation class name in the root, and everything is renamed. This case breaks this advantage.
Update: Actually, I have found a way to do this without using {} braces. & can be repeated more than once:
.navigation__item {
&:hover &__icon {
color: white;
}
&__icon {
color: black;
}
}
It is great, but it doesn't make much sense if I have many rules and rules for &:hover itself. The question is still open - is this possible to access sibling element definition from within the {} block.

In Stylus there is a Partial reference but I don't know anything similar in SASS. One solution could be using a variable for the parent selector:
.navigation__item {
$selector: &;
&:hover {
#{$selector}__icon {
color: white;
}
}
&__icon {
color: black;
}
}
Is usefull is you change navigation__item class for another.
EDIT: I had used a wrong example, it's OK now.

Related

Set a SASS variable depending

I'm searching a way to use a particular color depending on a class on the body tag.
I have a main scss file like this
// variables.scss
$bg-main: white;
$color-first: red;
$color-second: green;
And in my other files, I use the colors
// content.scss
.content {
.some-selector: {
// some styles
color: $color-second;
}
a:hover {
// some styles
color: $color-second;
}
}
// and same goes for menu.scss etc.
Now I have a dynamic class on the body, that changes depending on the current selected menu. I would like $color-second to be different for each body classes, and I don't know how to do that. The only solution I found was to move all the $color-second from each files into one single file, like this:
.body-1 {
.content a:hover, .content .some-selector {
color: green;
}
}
.body-2 {
.content a:hover, .content .some-selector {
color: blue;
}
}
.body-1 {
.content a:hover, .content .some-selector {
color: black;
}
}
So I don't need to write the color in each files. This works well, but if I need to set this $color-second to some other selector, I need to put that in this big file.
Is this possible to do this an other way?
I already checked these answers, but it didn't helped me much:
SASS set variable depending on CSS class
Creating or referencing variables dynamically in Sass
Merge string and variable to a variable with SASS
There are multiple ways to do this. The most obvious two which come to mind are mixins and loops:
Mixins
Just put everything you want into a single mixin, and then use it for every body class:
#mixin colored-content($color) {
.content a:hover, .content .some-selector {
color: $color;
}
/* Any other rules which use $color here */
}
.body-1 {
#include colored-content(green);
}
.body-2 {
#include colored-content('#FF0000');
}
.body-3 {
#include colored-content(darken(red, 20));
}
You can extend this example with any number of arguments (for example, $textColor and $bgColor), conditions or rules.
With this approach you will not have SCSS code repetitions, and any updates will be introduced easily.
Loop
Another way is to use a simple loop:
$body_themes: (
"body-1": green,
"body-2": #FF0000,
"body-3": darken(red, 2)
);
#each $body_class, $color in $body_themes {
.#{$body_class} {
.content a:hover, .content .some-selector {
color: $color;
}
/* Any other rules which use $color here */
}
}
It is even shorter, but imho it is less readable.
P.S. It is possible to combine mixins and loops, by the way :)

SCSS. Reference second level ascending selector

One of the techniques to organise classes in the scope of avoiding collisions, is to extend a parent's class + add some suffix. For example:
<div class="a">
<div class="a__b">
<div class="a__c">
<span class="a__d">
From considerations of not duplicating code, in sass/scss files, one can refer a parent with the help of an ampersand - &, so above structure can be achieved like this:
.a{
&__b{}
&__c{}
&__d{}
Which is transfomed into:
.a__b {}
.a__c {}
.a__d {}
But difficulties appear when one needs to get such a css as the result:
.a:hover{
color: red;
}
.a:hover .a__b{
color: blue;
}
Since the main idea is not to duplicate selectors, a question appears - is there a way to reference second level parent? I know that && ins't an issue but is there a way to simulate double ampersand behaviour?
.a{
&:hover{
color: red;
& __b { /* & -> .a:hover, but I need just .a */
color: blue;
}
}
}
Not an issue, .a is duplicated:
.a:hover { //here
color: red;
.a__b { //here
color: blue;
}
}
Also not an issue:
.a { //ok
&:hover {
color: red;
.a__b { //oops, duplicated selector
color: blue;
}
}
}
So, from the considerations of avoiding collisions many times classes have long names. And that is when duplicated selectors make code look scary. Imagine, that instead of .a selector there would be: .custom-layers-list-panel-conatiner. Another reason of avoiding duplicated classes is that if parent class is changed, it should be changed everywhere. Yes, nowadays it's quite trivial task with some specific tools, but it's still remains a place where mistakes can appear.
Update: better than Original
.a{
$grandparent: &;
&:hover{
color: red;
& #{$grandparent}__b {
color: blue;
}
}
}
and
Original:
#function r-pseudo($s) {
$string: nth(nth($s, 1), 1);
#return str-slice($string, 0, str-index($string, ':') - 1);
}
.a{
&:hover{
color: red;
& #{r-pseudo(&)}__b {
color: blue;
}
}
}
both generate
.a:hover {
color: red;
}
.a:hover .a__b {
color: blue;
}
Your idea was right, but you've to put the a:hover to the top-level to get the result you wanted. It is nothing what you wanted, but the only way that SCSS will give you your target-result.
I think you looking for this:
.a:hover {
color: red;
.a__b {
color: blue;
}
}
Second try, like this?
.a {
&:hover {
color: red;
.a__b {
color: blue;
}
}
}

Scoping CSS / Prepend selector with LESS

I have a chunk of CSS that I want to "scope" to a specific block of HTML. I'm generating a unique ID and then setting it on the block of HTML and then would like to wrap the chunk of CSS with the same ID so that those selectors can't match sibling or parent elements. I don't know the contents of the chunk of CSS. Given a chunk of CSS:
.container {
background-color: black;
}
.container .title {
color: white;
}
.container .description {
color: grey;
}
I need it to come out like this:
.theme0 .container, .theme0.container {
background-color: black;
}
.theme0 .container .title, .theme0.container .title {
color: white;
}
.theme0 .container .description, .theme0.container .description {
color: grey;
}
Is there any way to do this with LESS? The first selector is easy, just wrap the CSS chunk with '.theme0 {' + cssChunk + '}'. But I haven't been able to figure out a way to prepend '.theme0' to all of the selectors without the space.
EDIT:
So I should clarify that our intentions are to build such a system into our build process / dependency system. We're attempting to scope a chunk of css to a react component. We have a couple different approaches we're trying out, this is just one of them. Point is, the CSS and HTML we're trying to scope could be anything, we have no control or knowledge of it. The first pattern can easily be achieved by prepending .uniqueID { and appending }. This gives .uniqueID .someSelector {}. I'm wondering if it's possible to do a similar thing but get .uniqueID.someSelector {}? Ideally without having to write the original chunk of CSS with knowledge of our scoping system.
Assuming the component styles are in a separate CSS file, i.e.:
// component.css
.container {
background-color: black;
}
.container .title {
color: white;
}
.container .description {
color: grey;
}
The wrapper code could be:
.theme0 {
#import (less) "component.css";
&.container:extend(.theme0 .container all) {}
}
in less you can nest selectors for selecting inside that element like:
.theme {
color: black;
.container {
color: blue;
}
}
This wil generate:
.theme {
color:black;
}
.theme .container {
color:blue;
}
Creating elements that are connected is easy enof:
.test#badge will select a class test width an id badge
In less this is dont with the & symbol. (this selects the starting property)
.test {
color: blue;
&#badge {
color:black;
}
}
Compiles to:
.test {
color: blue;
}
.test#badge {
color: black;
}
And for the final selector:
To get the output of .test, .container use the function: .test:extends(.container);
.test {
color: black;
&:extends(.conatiner);
}
.container {
color: pink;
}
Compiles to:
.test {
color: black;
}
.test, .container {
color: pink;
}
You can even extend multiple ones in a single line:
.test:extends(.oclas, .tclss);
and its wil work as abose only for both classes. So outputed selectors would be .test, .oclass and .test, .tclass

Name clash between Less mixin and CSS selector

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;
}

Focused selector in variable . How to do that stuff in LESS like in SCSS

In SCSS i can do so:
and then
$selector-active: "&:hover, &:focus, &:active";
.class {
color: red;
#{$selector-active} {
color: green;
}
}
And its working.
How can i do this in LESS?
Hmm, interesting. Currently LESS does not expand its "&" within a selector interpolation, i.e. the straight-forward conversion DOES NOT work:
#selector-active: &:hover, &:focus, &:active;
.class {
color: red;
#{selector-active} {
color: green;
}
}
So you'll need some more tricky code... Using a callback/hook technique for example:
.selector-active() {
&:hover, &:focus, &:active {
.selector-active-properties();
}
}
.class {
color: red;
.selector-active();
.selector-active-properties() {
color: green;
}
}
You can get it even shorter:
.selector-active() {&:hover, &:focus, &:active {.-}}
.class {
color: red;
.selector-active;.-() {
color: green;
}
}
However there's important thing to remember when using hackish names for a hook/callback mixins.
If at some point you need another mixin with the same technique then you'll also need another name
for its callback (not the one you used for .selector-active()). Otherwise you get into problems if you try to use both "utilities" in the same scope. More over if you define some .inside() or .-() in the global scope they will override those coming from within .class and the trick becomes broken...
In other words, using "long/descriptive/unique" hook/callback names are just "safer" in a long run.
Btw. there's also a shorter syntax for the "hover specialization":
// same as .selector-active(#arg) when (#arg = hover):
.selector-active(hover) {
&:hover {
.inside();
}
}
I liked #Max nice solution. And this give me a way to move further. So i did a tweek with words for my self.
.selector-active() {
&:hover, &:focus, &:active {
.inside();
}
}
.selector-active(#type) when (#type = hover) {
&:hover {
.inside();
}
}
In use:
.class {
color: red;
.selector-active(); .inside() {
color: red;
}
}
I also tried to work with classes. LESS is prety owkward in this stuff, in 1.4.1 i must use:
.smthElse(#string) {
&.class-#{string}-small,
&.class-#{string}-big,
&.class-#{string}-tall {
.inside();
}
}
in 1.3.1 i must to use:
(~".myclass_#{index}") {...
#see http://lesscss.org/
Enough compact, and could be in use. So i can still work with LESS :) yey.
P.S.: All above is for less.js v1.4.1

Resources