Reverse states on hover with Less - css

I have the following LESS code:
.favourite-action-link {
&:after {
color:#grey;
content: '\e836';
}
&.is-favourite:after {
color:#red;
content: '\e811';
}
&:hover {
&:after {
color:#red;
content: '\e811';
}
&.is-favourite:after {
color:#grey;
content: '\e836';
}
}
}
With the essential goal being that there is a normal state and a hover state, that are reversed when another class is present. I'll be repeating this for other actions (eg .share-action-link, .review-action-link etc) and this just seems messy the way I have it. Is there a way to create a mixin such that I could provide this like so:
.favourite-action-link {
&:after {
color:#grey;
content: '\e836';
&:hover {
color:#red;
content: '\e811';
}
.reverseOnClass(is-favourite);
}
}
Or something like that? The only way I can think of so far would be to do:
.favourite-action-link {
&:after {
color:#grey;
content: '\e836';
}
&.active:after {
color:#red;
content: '\e811';
}
}
and then to use jQuery instead to do the hover - toggling .active on (isHovering XOR hasClass(is-favourite)) - but turning LESS into LESS + jQuery is the opposite of fixing a mess/maintainability issue.

I would really recommend writing it like below because it keeps the code simple and easy to read.
.favourite-action-link {
&:after, &.is-favourite:hover:after {
color: #grey;
content: '\e836';
}
&:hover:after, &.is-favourite:after {
color: #red;
content: '\e811';
}
}
But if you really want to use a mixin to avoid repeating the selectors then you could write it like below. This mixin takes two rulesets as input and they are applied to the required selectors.
.favourite-action-link {
.rules-gen(
{
color: #grey;
content: '\e836';
};
{
color: #red;
content: '\e811';
}
);
}
.rules-gen(#rule1; #rule2){
&:after, &.is-favourite:hover:after {
#rule1();
}
&:hover:after, &.is-favourite:after {
#rule2();
}
}
In both these methods, the selectors are also grouped and that also means reduced lines of code.
Demo
Or, if the extra class is not always is-favourite and it could also be something else then you could pass it also to the mixin as a parameter like below:
.favourite-action-link {
.rules-gen(
{
color: grey;
content: '\e836';
};
{
color: red;
content: '\e811';
};
~"is-favourite"
);
}
.share-action-link {
.rules-gen(
{
color: yellow;
content: '\e836';
};
{
color: gold;
content: '\e811';
};
~"active"
);
}
.rules-gen(#rule1; #rule2; #addedClass){
&:after, &.#{addedClass}:hover:after {
#rule1();
}
&:hover:after, &.#{addedClass}:after {
#rule2();
}
}
Demo

Related

Nested selectors using Sass

How can I have this output using Sass?
.class.active .class-name1 {}
.class.active .class-name2 {}
Here is what I tried :
.class {
&.avtive {
&-name1 {
}
&-name2 {
}
}
}
You need to keep a reference to the outer class name, and interpolate it for your -name classes. Using color: red as an example of the style to apply:
.class {
$outer-class: &;
&.active {
#{$outer-class}-name1 {
color: red;
}
#{$outer-class}-name2 {
color: red;
}
}
}

SCSS nesting with ampersand not working as expected

I have a JSFiddle, https://jsfiddle.net/khpeek/3gh0r931/43/, in which I'd like to toggle the visibility of up/down Materialize arrow icons based on the class of the parent element (which is set to asc or desc by a jQuery plugin, list.js).
I've noticed that the following nesting pattern doesn't work:
.sort.asc > i.material-icons {
&.upper::after {
content: "arrow_drop_up";
}
&.lower::after {
content: "";
}
}
.sort.desc > i.material-icons {
&.upper::after {
content: "";
}
&.lower::after {
content: "arrow_drop_down";
}
}
However, if I 'write out' the nesting like so,
.sort.asc > i.material-icons.upper::after {
content: "arrow_drop_up";
}
.sort.asc > i.material-icons.lower::after {
content: "";
}
.sort.desc > i.material-icons.upper::after {
content: "";
}
.sort.desc > i.material-icons.lower::after {
content: "arrow_drop_down";
}
Then I get the intended behavior. (This code is commented-out in the JSFiddle). (I still want to position the icons on top of each other to make a 'diamond' similar to https://www.datatables.net/, but that's a separate issue).
What is wrong with the 'nested' form of this SCSS expression?
Your nesting is fine, you just have a syntax error after the font-size line:
i.material-icons {
position: relative;
font-size: $th-font-size * 2;
{
&.upper: margin-top: -$th-font-size/2;
&.lower: margin-bottom: -$th-font-size/2;
}
}
You probably meant this:
i.material-icons {
position: relative;
font-size: $th-font-size * 2;
&.upper { margin-top: -$th-font-size/2; }
&.lower { margin-bottom: -$th-font-size/2; }
}

& in SCSS extends issue

In my scss code I'm trying to make placeholder like:
%input__label {
color: red;
&_content {
color: blue;
}
}
And then extend it:
.input__label {
#extend %input__label;
}
When I'm trying to compile that code only input__label compiles, but input__label_content doesn't:
.input__label {
color: red;
}
(But for example with pseudo elements - &::after everything works fine)
Why is this hapenning? I'm using node v7.10.1 and node-sass v4.5.3.
Use a mixin instead of an extend:
#mixin input__label {
color: red;
&_content {
color: blue;
}
}
.input__label {
#include input__label;
}
The functionality to do it via extends is purposefully disallowed:
https://github.com/sass/sass/commit/face3172930b515ac2040e450d4db3ffe01c31b5

SCSS / Sass: How do i change a mixin to output specific CSS?

I've got a function written in SCSS / Sass which loops every item of a map with the #each function.
$classes-map: ("class-a": "doesntmatter",
"class-b": "doesntmatter",
'class-c': 'doesntmatter');
#mixin function {
#each $class, $property in $classes-map {
%#{$class} {
content: $property;
}
#for $i from 1 to 4 {
&.#{$class}-#{$i} {
#extend %#{$class};
}
}
}
}
On every item of the map is also a #for loop, to make a list of classes with komma's and everytime a different number.
Here's what the output should be:
.class-a-1, .class-a-2, .class-a-3 {
content: "doesntmatter";
}
.class-b-1, .class-b-2, .class-b-3 {
content: "doesntmatter";
}
.class-c-1, .class-c-2, .class-c-3 {
content: "doesntmatter";
}
I'm calling this function by using this piece of code:
.parent {
&.child {
#include function;
}
}
CSS
.parent.child .parent.child.class-a-1, .parent.child .parent.child.class-a-2, .parent.child .parent.child.class-a-3 {
content: "doesntmatter";
}
.parent.child .parent.child.class-b-1, .parent.child .parent.child.class-b-2, .parent.child .parent.child.class-b-3 {
content: "doesntmatter";
}
.parent.child .parent.child.class-c-1, .parent.child .parent.child.class-c-2, .parent.child .parent.child.class-c-3 {
content: "doesntmatter";
}
Desired CSS
.parent.child.class-a-1, .parent.child.class-a-2, .parent.child.class-a-3 {
content: "doesntmatter";
}
.parent.child.class-b-1, .parent.child.class-b-2, .parent.child.class-b-3 {
content: "doesntmatter";
}
.parent.child.class-c-1, .parent.child.class-c-2, .parent.child.class-c-3 {
content: "doesntmatter";
}
How do i change the #mixin function to resolve this problem?
What the &!
Oh so close! As you want to chain / include the parent selector using &, then anything in the mixin should follow suit. The missing piece was the placeholder &%#{$class}.
SCSS:
$classes-map: ("class-a": "doesntmatter",
"class-b": "doesntmatter",
'class-c': 'doesntmatter');
#mixin function {
#each $class, $property in $classes-map {
&%#{$class} {
content: $property;
}
#for $i from 1 to 4 {
&.#{$class}-#{$i} {
#extend %#{$class};
}
}
}
}
.parent {
&.child {
#include function;
}
}
CSS:
.parent.child.class-a-1, .parent.child.class-a-2, .parent.child.class-a-3 {
content: "doesntmatter";
}
.parent.child.class-b-1, .parent.child.class-b-2, .parent.child.class-b-3 {
content: "doesntmatter";
}
.parent.child.class-c-1, .parent.child.class-c-2, .parent.child.class-c-3 {
content: "doesntmatter";
}

Extending a Nested Placeholder in SCSS

Is it possible to #extend a SCSS placeholder with nesting, and have that nesting reflected in the resulting class?
Given a nested placeholder:
%my-form-field {
...
&__label {
...
}
&__feedback {
...
}
}
I currently have to do the following:
.one-of-many-targets {
#extend %my-form-field;
&__label {
#extend %my-form-field__label;
}
&__feedback {
#extend %my-form-field__feedback;
}
}
But I'd like to be able to simplify this to:
.one-of-many-targets {
#extend %my-form-field;
}
... and have it resolve to:
.one-of-many-targets { ... }
.one-of-many-targets__label { ... }
.one-of-many-targets__feedback { ... }
Is there a different way to write my placeholder and #extends to make the SCSS cleaner, as in the 2nd example?
You can use a mixin instead:
#mixin my-form-field() {
width: 10px;
&__label {
width: 20px;
}
&__feedback {
width: 30px;
}
}
.one-of-many-targets {
#include my-form-field();
}
will generate:
.one-of-many-targets {
width: 10px;
}
.one-of-many-targets__label {
width: 20px;
}
.one-of-many-targets__feedback {
width: 30px;
}
You could try use selector.append()
See: https://github.com/sass/sass/issues/2808#issuecomment-574413393
Also see more info why parent selector didn't work as you expect in extend-only selectors: https://github.com/sass/sass/issues/2262#issuecomment-291645428

Resources