Scss modify rules based on topmost parent - sass

Let's propose that we have the following scss code:
.a {
.b {
.c {
// rules here
}
}
}
This will output the following css: .a .b .c
Now, if I would like to modify the ruleset of .c if .a gets another class, I would have to modify my scss like this:
.a {
.b {
.c {
// rules here
}
}
&.d {
.b {
.c {
// rules here
}
}
}
}
If there are multiple modifications, my scss will become very long, and the rules refering to .c will be out and apart. Isn't there an easier syntax, to keep all the rules of .c together in the code, so that when I need to modify something, it will be easier? I am thinking about something along these lines:
.a {
.b {
.c {
// rules here
^^&.d {
// go up 2 selectors (hence twicte the ^), and add to the selector the `.d` class, then keep on modifying the current selector `.c` here
}
}
}
}
EDIT: I am used to specify each element (or most, any case), in the selector path, so my selectors are as explicit as possible. The above example can be reflected for a dashboard button, which is deep inside the dashboard, and with the basic approach, I will have to replicate 5 levels of nesting, to specify some style for a button, based on the status of the dashboard (ie. sidebar-open). I could of course add a class directly to the button and style it like that, but imo that's an overkill, as there are a lot of different things that change in the stylesheet for this specific status.
For a complete example, when I click this specific button, the sidebar should pop out, some other elements that are getting overlapped, should be pushed to the right, etc. My javascript, simply adds the status representing class to the whole dashboard, and I would like to style everything accordingly. But this way, the styles for the different elements are far and apart unfortunately.

You can use the
sass variable to structure your nested classes and styles.
#each rule to iterate over every key/value pair to emit styles.
Let's suppose you can have 3 classes on first level i.e x, y, z, with their child classes a, b.
Then your scss variable code will look like below:
$classes: (
x: (
a: (
color: blue,
font-size: 15px
),
b: (
color: blue,
font-weight: 600
)
),
y: (
a: (
color: red,
),
b: (
color: blue,
font-size:20px
)
),
z: (
a: (
color: black,
overflow: scroll
),
b: (
color: black,
font-size: 20px,
background-color: white
)
)
);
Now we can iterate above variable like below:
#each $key, $value in $classes {
.#{$key} {
#each $key1, $value1 in $value {
.#{$key1} {
#each $key2, $value2 in $value1 {
#{$key2}: $value2
}
}
}
}
}
If in case you want to add more classes in the future, then you need follow the same structure and add class names and styles accordingly.
Result (CSS)
.x .a {
color: blue;
font-size: 15px;
}
.x .b {
color: blue;
font-weight: 600;
}
.y .a {
color: red;
}
.y .b {
color: blue;
font-size: 20px;
}
.z .a {
color: black;
overflow: scroll;
}
.z .b {
color: black;
font-size: 20px;
background-color: white;
}
I hope it solves your problem & beautifies your code as well.

Related

Sass manipulate with #content

Is it possible to manipulate with #content magic variable in SASS?
I would like to replace some stuff in here before output.
Or maybe can I fill some variable with it?
The conclusion is that, I want to make an mixin #important that create both versions. Important, and no-important.
Input
.test {
#include important {
color: red;
text-align: left;
}
}
Expected output
.test {
color: red;
text-align: left;
}
.test-i {
color: red !important;
text-align: left !important;
}
No, you can't. But I quickly wrote you a mixin to make it work. It doesn't accepts multiple properties (yet).
First Note: I changed the mixin it now does accept multiple properties. Here is the Codepen.
Second Note: I updated the mixin adding multiple properties does no longer compile to different classes for each property, instead you get two versions, one without the !important suffix and one with.
This is the mixin:
#function return($state) {
#return if($state == '', '', '-i');
}
#mixin loop($name, $items...) {
#each $item in $items / 2 {
#each $state in ('', '!important') {
$suffix: return($state);
.#{$name}#{$suffix} {
#for $i from 1 through (length($items) / 2) {
#{nth($items, ($i * 2) - 1)}: #{nth($items, ($i * 2))} #{$state};
}
}
}
}
}
This is how you include it:
// #include loop([classname], [property], [value]);
#include loop(whateverClassname, color, red);
This is what it compiles to:
.whateverClassname {
color: red ;
}
.whateverClassname-i {
color: red !important;
}
This is what it now compiles to, when you use multiple properties at once:
#include loop(whateverClassname, color, red, background-color, green, display, flex);
.whateverClassname {
color: red ;
background-color: green ;
display: flex ;
}
.whateverClassname-i {
color: red !important;
background-color: green !important;
display: flex !important;
}
Conclusion: it works as expected and does no longer bloat your CSS.
Hope I could help you at least a little ;-)

Using a sass variable in an #each loop

I'm trying to create a little overview for all the colors we use in our corporate identity. All our colors have been defined in _settings-colors.scss, and the only reason I need this bit of css is for the library, where the colors need to be listed.
What I have now is as follows:
$colors-brand: color-brand, color-brand-40, color-brand-60, color-brand-70;
.prfx-color {
display: block;
height: 5rem;
width: 100%;
#each $color in $colors-brand {
&--#{$color} {
background-color: #{'$'+$color};
&::after {
content: '$'+$color;
}
}
}
}
These color-brand variables are set in another file which I'm including in this scss file.
The code above outputs this:
.prfx-color {
display: block;
height: 5rem;
width: 100%;
}
.prfx-color--color-brand {
background: $color-brand;
}
.prfx-color--color-brand::after {
content: "$color-brand";
} [...etc]
What I'm after however, is this:
.prfx-color--color-brand {
background: #00ff11; // don't worry, brand is not actually this color
}
The problem I'm having is that the $color-brand variable isn't interpreted as a sass variable anymore, but is a literal value. I need the #hheexx that this variable refers to!
All the solutions I've found so far consist of using two lists, or a key-value pair. In my situation these variables have already been set once, and I want a solution where I don't want to have to manually edit the library if the colors change.
Is this at all possibe, or am I too greedy here?
And I realized I overcomplicated it. You don't need any extra functions because the #each is designed to work with maps and iterating over multiple values.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $key, $val in $colors {
&--#{$key} {
background-color: $val;
&::after { content: "$#{$key}"; }
}
}
}
You could use a map.
Here's a sassmeister playground for you.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $color in map-keys($colors) {
&--#{$color} {
background-color: map-get($colors, $color);
&::after { content: "$#{$color}"; }
}
}
}

Function to dynamically create classes from lists

Im fairly new to SASS and I'm confused by the way lists work. I have a multidimensional list like this:
$stylethemes: {
(15, bold, red),
(12, normal, blue)
}
I now want a function that makes a class for each list in $stylethemes and then puts the values of that list into that class. The output I want from the above list is this:
.theme1{
font-size: 15;
font-weight: bold;
color: red;
}
.theme2{
font-size: 12;
font-weight: normal;
color: blue;
}
Can anyone show me how I can do this? Thanks in advance.
The code to produce the desired results would look like this:
$stylethemes: (
(15, bold, red),
(12, normal, blue)
);
#for $i from 1 through length($stylethemes) {
.theme#{$i} {
font-size: nth(nth($stylethemes, $i), 1);
font-weight: nth(nth($stylethemes, $i), 2);
color: nth(nth($stylethemes, $i), 3);
}
}
However, you'll find this isn't particularly flexible. You're better off using mappings for the property/values so that you don't have to guarantee a specific order:
$stylethemes: (
(font-size: 15, font-weight: bold, color: red),
(font-size: 12, font-weight: normal, color: blue)
);
#for $i from 1 through length($stylethemes) {
.theme#{$i} {
#each $prop, $val in nth($stylethemes, $i) {
#{$prop}: $val;
}
}
}
Or
$stylethemes: (
theme1: (font-size: 15, font-weight: bold, color: red),
theme2: (font-size: 12, font-weight: normal, color: blue)
);
#each $theme, $properties in $stylethemes {
.#{$theme} {
#each $prop, $val in $properties {
#{$prop}: $val;
}
}
}
Youa re basically asking us to solve your pboelm, but fine, since SASS is very deep and fun to use and can be a bit daunting with its lack of map looping functions. I changed a couple of things but this is essentially it:
// first off, I decided to make your style themes a SASS map. This is useful because your
// your theme will be intricately linked to its name, making it easier to read
// you could to the same with the values, but for now I'll count them.
$stylethemes: (
theme-1 : (15, bold, red),
theme-2 : (12, normal, blue)
);
// first, we need to create a regular list we can loop through with a for loop
// map-keys returns a list we can use for that
$allthemes : map-keys($stylethemes);
// then we can run through all the themes by finding the theme name from the above list
#for $var from 1 through length($allthemes) {
// heres how we get the theme name
$theme : nth($allthemes, $var);
// heres how we get the values stored in your SASS map
$this : map-get($stylethemes, $theme);
// then I assign all your variables to vars, but its not necessary
$size : nth($this, 1);
$style : nth($this, 2);
$color : nth($this, 3);
// now print the theme name as a classname
.#{$theme}{
// then print the values - you could also use the above nth($this, n) to get them.
font-size: $size;
font-weight: $style;
color: $color;
}
}
I got all the function info from the SASS documentation site: http://sass-lang.com/documentation/Sass/Script/Functions.html, so have a look around there, there is a dedicated section for lists and maps. Have a look at lists and maps as they will be very useful for this kind of thing.

Sass/Compass Getting variable name from variable

I'm trying to make a mixin that will let me create adapted blocks of code depending on what variable name you up in.
$foo: #00A9EC;
#mixin menu-color($color) {
.color-#{$color} a.level2,
.color-#{$color} a.level2:visited {
color: $color;
&:hover {
color: adjust-lightness($color, 10); }
&:active {
color: adjust-lightness($color, -10); } } }
#include menu-color($foo);
outputs:
.color-foo a.level2,
.color-foo a.level2:visited {
color: #00A9EC; }
.color-foo a.level2:hover,
.color-foo a.level2:visited:hover {
color: #20C0FF; }
.color-foo a.level2:active,
.color-foo a.level2:visited:active {
color: #0084B9; }
In sass you can do this using map, you just pass the variable name instead of the variable itself:
$colors: (
-black: #000,
-blue: #088DC6
);
#mixin generateBgColor($colorName) {
.bg-color#{$colorName} {
background-color: map-get($colors, $colorName);
}
}
#include generateBgColor("-blue");
This will generate class:
.bg-color-blue {
background-color: #088DC6;
}
You achieve this also in less with standard variables, just by using curly brackets and double at character:
#blue: #088DC6;
.generate-bg-color(#color) {
.bg-color-#{color} {
background-color: ##color;
}
}
.generate-bg-color(~"blue");
You should not name CSS classes after specific colors. You would regret that. Just think, if you want to make the color red later on, you would need to go back over all your html and change the classes.
The reason we have CSS is so that you don't have to embed style information in the markup.
Use a semantic class the describes the data, not how it is displayed:
$foo: #00A9EC;
#mixin menu-color($name, $color) {
.custom-#{$name} a.level2,
.custom-#{$name} a.level2:visited {
color: $color;
&:hover {
color: adjust-lightness($color, 10); }
&:active {
color: adjust-lightness($color, -10); } } }
#include menu-color(profile, $foo);
And then in your HTML <div class="custom-profile">.
That way, two years from now when you want to make it black, and underlined (or whatever), you don't have to dig through your html and add a new '.underlined-and-black-color` class to all of those elements. You just change your SCSS in one place.

Passing a list to a mixin in SASS as parameters?

I'm trying to make a SCSS stylesheet easily configurable by defining a set of constants that will be used in a number of mixins and with the Compass library. Ideally, I'd like to be able to do the following:
$item-bgs: linear-gradient(white, black), #ccc;
#mixin some-mixin() {
#include background-with-css2-fallback($item-bgs*);
}
The background-with-css2-fallback is a Compass mixin that accepts up to 10 params. I'm assuming that SASS does not currently support passing a list parameter as the argument list, otherwise Compass would probably use it, but I'm wondering if I can get the $item-bgs list to be the first 2 arguments to the background-with-css2-fallback mixin. Is there a way to do this currently, or is it even planned for SASS in the future?
It may not be supported by SASS natively, but Compass does support passing a list as the first argument to the background-with-css2-fallback mixin. If you look at the source for the mixin, you'll see that it uses a compact function that handles the logic for collapsing the arguments into a single list, whether passed individually or in a single list parameter.
For example, this works fine for me:
#import "compass";
$item-bgs: (linear-gradient(white, black), #ccc);
.test {
#include background-with-css2-fallback($item-bgs);
}
Examples of useing maps as arguments:
Example 1 (list)
#mixin transition($property...){
#if $property {
transition-property: $property;
}
#else {
transition-property: all;
}
transition-timing-function: ease-in-out;
transition-duration: .3s;
transition-delay: 0;
}
.btn {
color: black;
border: 1px solid black;
#include transition(color, border-color);
&:hover {
color: red;
border-color: red;
}
}
Example 2 (Custom params)
#use 'sass:meta';
#mixin example2($args...) {
#each $key, $value in meta.keywords($args) {
#{$key}: #{$value};
}
}
.shape {
#include example2($width:200px, $height:100px);
}
Example 3 (map)
#mixin colors($args:()) {
#if length($colors) > 0 {
#each $key, $val in $args{
.txt-#{$key} {
color: #{$value};
}
.bg-#{$key} {
background-color: #{$value};
}
}
}
}
$colors_map: (
primary: blue,
secondary: green,
accent: red,
light: white,
dark: black
);
#include colors($colors_map);

Resources