SASS Map - map-get no longer supporting interpolated values? - css

Previously in SCSS (version 3.4.21), I can use variable interpolation to get a specific item from a map:
$colors: (
color-1: #aaa,
color-2: #bbb,
color-3: #ccc
);
#mixin color($shade) {
color: map-get($colors, #{color-}$shade );
}
.element {
#include color(2);
}
Compiles to:
.element {
color: #bbb;
}
Playground Link
In sass 4 (alpha), it doesn't work (or i'm screwing something up):
#{color-}$shade
It doesn't allow me to do that. Is there a reason this was taken out?

I think your syntax is just a bit backwards.
#mixin color($shade) {
color: map-get($colors, gray-#{$shade} );
}

Related

Themed variables in LESS/SASS

I'm looking to support multiple themes in my app - moreover, I want to be able to dynamically change themes, either by changing a class on the body element, or even have different parts of the app use different themes.
In my previous project, I did it by adding explicit rules every time I need to use a theme-specific variable:
.theme-light & { background-color: #theme-light-background; }
.theme-dark & { background-color: #theme-dark-background; }
However, that approach does not scale well and adds unnecessary bloat to source files.
Now, I'm looking for a more automated approach for this. I.e. the following
.button {
border-radius: 4px;
background-color: #ui-background;
color: #ui-foreground;
border: 1px solid mix(#ui-background, #ui-foreground, 50%);
}
would turn into something like
.button {
border-radius: 4px;
border: 1px solid #808080;
/* normally we wouldn't expect this to appear here, but in our case
both themes have the same border color so we can't tell the difference */
}
.theme-light .button {
background-color: #fff;
color: #000;
}
.theme-dark .button {
background-color: #000;
color: #fff;
}
As far as I can tell, neither LESS nor SASS can do this in a natural way. It seems that it wouldn't be too difficult to implement it as a separate post-processor, that builds stylesheets for every theme, then compares them and scopes the differences into the corresponding "namespaces". I suspect that something like this might already exist, but I can't find anything.
Any suggestions?
Not sure about Less, but in Sass it can be implemented relatively easy by storing theme information into maps and using ability to pass content blocks into mixins using #content. Here is example of how it may look like, quite fast solution but you can get an idea:
// Themes definition
// - First level keys are theme names (also used to construct theme class names)
// - Second level keys are theme settings, can be referred as theme(key)
$themes: (
light: (
background: #fff,
foreground: #000,
),
dark: (
background: #000,
foreground: #fff,
),
);
// Internal variable, just ignore
$_current-theme: null;
// Function to refer to theme setting by name
//
// #param string $name Name of the theme setting to use
// #return mixed
#function theme($name) {
#if ($_current-theme == null) {
#error "theme() function should only be used into code that is wrapped by 'theme' mixin";
}
#if (not map-has-key(map-get($themes, $_current-theme), $name)) {
#warn "Unknown theme key '#{$name}' for theme '#{$_current-theme}'";
#return null;
}
#return map-get(map-get($themes, $_current-theme), $name);
}
// Theming application mixin, themable piece of style should be wrapped by call to this mixin
#mixin theme() {
#each $theme in map-keys($themes) {
$_current-theme: $theme !global;
.theme-#{$theme} & {
#content;
}
}
$_current-theme: null !global;
}
.button {
border-radius: 4px;
#include theme() {
background-color: theme(background);
color: theme(foreground);
}
}
This piece of code will give you this result:
.button {
border-radius: 4px;
}
.theme-light .button {
background-color: #fff;
color: #000;
}
.theme-dark .button {
background-color: #000;
color: #fff;
}
Looks pretty close to what you're trying to achieve. You can play with this snippet at Sassmeister.

sass mixins and css custom properties

aye folks!
i tried to build a sass mixin for css custom properties to make work a little bit easier.
my attempt does look like this:
#mixin mixin($value) {
background: unquote('$')#{$value};
background: var(--#{$value}, unquote('$')#{$value});
}
the output looks like this:
.example {
background: $value;
background: var(--colour, $value);
}
BUT sass doesn't convert my $value into the actual thing. the $value part end up in my final css file and ofc this doesn't work.
i tried to find a solution online but i'm either to dumb to find it or there isn't one. anyone here has an idea what i'm doing wrong?
I have try your code with this example:
#mixin mixin($value) {
background: unquote('$')#{$value};
background: var(--#{$value}, unquote('$')#{$value});
}
.example {
#include mixin(colour)
}
and it works.
The output is:
.example {
background: $colour;
background: var(--colour, $colour);
}
is what you want? Sorry but I don't have the authorization for add comment so I create this answer.
Edit: you can create a different mixin like this:
#mixin mixin($value, $color) {
background: $color;
background: var(--#{$value}, $color);
}
This help you to solve your problem?

Part of variable being dynamic

I'm trying to do the following in sass (scss),
$colors-#{$type}
but I get
undefined variable $colors-
I found a source that was refering to the 3.3 release document, they do mention it, however, I can't find the solution to the issue.
Is there a way to do this?
link to scss
I think the post is misleading. Each issue I found on Github (for instance here or here) recommend the use of a map. And on https://github.com/sass/sass/issues/463 one of the contributors wrote:
We've decided to add an association data type (aka map/hash) and it will address this use case.
Use a map and map-get() to get your style definitions dynamically. You can use it within a mixin, a function or directly. The following example
$colors: (
"green": #0c0,
"blue": #00c,
"red": #c00
);
#mixin my_color($color) {
color: map-get($colors, $color);
}
#function my_color_func($color) {
#return map-get($colors, $color);
}
body {
#include my_color("green"); // use a mixin
background-color: my_color_func("blue"); // use of a function
border: 1px solid map-get($colors, "red"); // use directly
}
generates the following css:
body {
color: #0c0;
background-color: #00c;
border: 1px solid #c00;
}

Set a variable in Sass depending on the selector

I’ve got a website that’s using a few different ‘main’ colors. The general HTML layout stays the same, only the colors change depending on the content.
I was wondering if I could set a color variable depending on the CSS selector. This way I can theme my website with a few variables and let Sass fill in the colors.
For example:
$color-1: #444;
$color-2: #555;
$color-3: #666;
$color-4: #777;
body.class-1 {
color-default: $color-1;
color-main: $color-2;
}
body.class-2 {
color-default: $color-3;
color-main: $color-4;
}
/* content CSS */
.content {
background: $color-default;
color: $color-main;
}
I was thinking of using a mixin for this, but I was wondering if there’s a better way to do this—with a function maybe? I’m not that great with Sass, so any help would be appreciated.
I think a mixin is the answer. (As I wrote, variables won’t work.)
#mixin content($color-default, $color-main) {
background: $color-default;
color: $color-main;
}
body.class-1 {
#include content(#444, #555);
}
body.class-2 {
#include content(#666, #777);
}
That SCSS compiles to this CSS:
body.class-1 {
background: #444444;
color: #555555; }
body.class-2 {
background: #666666;
color: #777777; }
If you wanted to group the color values together in your SCSS file, you could use variables in conjunction with the mixin:
$color-1: #444;
$color-2: #555;
$color-3: #666;
$color-4: #777;
body.class-1 {
#include content($color-1, $color-2);
}
body.class-2 {
#include content($color-3, $color-4);
}
as sass documentation explain nicely (https://sass-lang.com/documentation/variables):
Sass variables are all compiled away by Sass. CSS variables are included in the CSS output.
CSS variables can have different values for different elements, but Sass variables only have one value at a time.
Sass variables are imperative, which means if you use a variable and then change its value, the earlier use will stay the same. CSS variables are declarative, which means if you change the value, it’ll affect both earlier uses and later uses.
We may take advantage of that using a combination of sass and css variables to achieve what you want:
//theme colors
$red-cosmo: #e01019;
$green-cosmo: #00c398;
$primary-color: var(--primary-color);
body{
--primary-color: #{$red-cosmo};
}
body.univers-ride{
--primary-color: #{$green-cosmo};
}
So when I call my sass variable $primary-color, it will print as my css variable "var(--primary-color)" that will expand as $green-cosmo only if my body has the "univers-ride" class else it will be $red-cosmo the default color.
If you really want to get hacky you could also define your different color schemes in a single variable like $scheme1: class1 #333 #444, where the first value is always the name, and that is followed by all the colors in that scheme.
You can then use #each:
// Define your schemes with a name and colors
$scheme1: class1 #444 #555;
$scheme2: class2 #666 #777;
$scheme3: class4 #888 #999;
// Here are your color schemes
$schemes: $scheme1 $scheme2 $scheme3;
#each $scheme in $schemes {
// Here are the rules specific to the colors in the theme
body.#{nth($scheme, 1)} .content {
background-color: nth($scheme, 2);
color: nth($scheme, 3);
}
}
This will compile to:
body.class1 .content {
background-color: #444444;
color: #555555; }
body.class2 .content {
background-color: #666666;
color: #777777; }
body.class4 .content {
background-color: #888888;
color: #999999; }
Obviously if you don't want to combine body.class1 and .content in your selectors, you could just specify a mixin content($main, $default) and call it inside the #each using nth just like in the above code, but the point is you don't have to write out a rule for each of your classes.
EDIT There are lots of interesting answers on Creating or referencing variables dynamically in Sass and Merge string and variable to a variable with SASS.
You can also create a mixing that use the ampersand parent selector. http://codepen.io/juhov/pen/gbmbWJ
#mixin color {
body.blue & {
background: blue;
}
body.yellow & {
background: yellow;
}
}
UPDATE: its 2017 and variables does works!
#mixin word_font($page) {
#font-face {
font-family: p#{$page};
src: url('../../static/fonts/ttf/#{$page}.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
.p#{$page} {
font-family: p#{$page};
}
}
// Loop and define css classes
#for $i from 1 through 604 {
#include word_font($i);
}
If you don't want to use a variable for each color, you can use one variable for all kinds of colors. In the mixin you can choose the right color with nth. For instance, if you write the index of the color as 1, then you get the first color in the color variable.
$colors: #444, #555, #666, #777;
#mixin content($color-default-num, $color-main-num) {
background: nth($colors, $color-default-num);
color: nth($colors, $color-main-num);
}
body.class-1 {
#include content(1, 2);
}
For me the definite answer to my problem was creating a map of maps and loopig through them as follows:
$pallettes: (
light-theme: (
container-color: red,
inner-color: blue,
),
dark-theme: (
container-color: black,
inner-color: gray,
),
);
#each $pallette, $content in $pallettes {
.main.#{$pallette} {
background-color: map-get($content, container-color);
.inner-div {
background-color: map-get($content, inner-color);
}
}
}
You can simply override your scss variables inside of the class wrapper:
$color1: red;
$color2: yellow;
header { background: $color1; }
.override-class {
$color1: green;
header { background: $color1; }
}
Seems to work for me.

How write *::selection{} in scss\sass?

I tried to do the following, but it does not work:
* {
&::selection { text-decoration: underline; }
}
That's the way I do it:
// define it
#mixin selection {
::-moz-selection { #content; }
::selection { #content; }
}
// use it
#include selection {
color: white;
background: black;
}
Update
I recommend to just use ::selection {} with autoprefixer instead of a mixin. This will make your code thinner and your brain lighter :)
In this case, autoprefixer will transform this:
::selection {
color: white;
background: black;
}
...(depending on your target browsers/configuration) into something like that:
::-moz-selection {
color: white;
background: black;
}
::selection {
color: white;
background: black;
}
Mixins work with pseudo element selectors ;) see my mixin:
$prefixes: ("-moz-", "");
#mixin selection($color, $background) {
#each $prefix in $prefixes {
::#{$prefix}selection {
color: $color;
background: $background;
}
}
}
how to use:
#include selection(white, black);
of course you can make it far more flexible, but it was sufficient for me ;)
While the ::selection pseudo-element was still in the draft spec, text-decoration was not stated as one of the allowed style properties. Given that browsers implement it anyway, they should be following the rules according to that document, disallowing text-decoration as such.
That said, there's nothing wrong with your selector, although it's worth noting that Firefox uses the vendor-prefixed version ::-moz-selection instead. You'd have to repeat the rule to support that browser, along with Chrome, Safari and Opera (see this answer for info).
So in SCSS, you'd do this:
* {
&::-moz-selection { /* Style any selection */ }
&::selection { /* Style any selection */ }
}
You might be able to reduce that using mixins, but I'm not sure if mixins work with pseudo-element selectors.
Great mixin, I have changed to work inside a rule by adding "&", it works better for me. I have also added a empty prefix to get the rule with no prefix.
#mixin selection($color, $background) {
$prefixes: ("-moz-", "-webkit-", "-o-", "-ms-", "");
#each $prefix in $prefixes {
&::#{$prefix}selection {
color: $color;
background: $background;
}
}
}
With compass you could do it like the following:
#import "compass/css3/selection";
#include selection($highlightBackground, $highlightColor)

Resources