SASS/SCSS, how to access a property/method in a dynamic way from a partial file? - sass

Let's say for instance we have the next sass partial file:
//_colors.scss
$foo: red;
And we "use" it on another file:
//test.scss
#use './colors'
.test{
color: colors.$foo;
}
All good, but what if I would like to use/get the value in a dynamic way within a mixin? something like:
//test.scss
#use './colors'
#mixin getColor($type){
color: colors[$type]; //JavaScript example, * don't actually work *.
or
color: #{colors.{$type}; * don't work neither *
//The above returns `color: colors.foo` instead of `color: red` on compilation.
or
color: colors.#{$type}; * doesn't work neither *
}
.test{
#include getColor(foo);
}
Is it possible? thanks for the help!

For a color, I really much prefer a function so it can be used on any property (color, background-color, border, box-shadow...)
I usually declare a string equivalent to variable names, then define them inside a map. Finally this map is accessible via a dedicated function.
Something like
//_colors.scss
#use 'sass:map';
$favoriteRed: "favoriteRed";
$favoriteYellow: "favoriteYellow";
$favoriteBlue: "favoriteBlue";
$MyColors: (
$favoriteRed: #c00,
favoriteYellow: #fc0,
$favoriteBlue: #0cf
);
#function my-color($tone: $favoriteRed) {
#if not map.has-key($MyColors, $tone) {
#error "unknown `#{$tone}` in MyColors.";
}
#else {
#return map.get($MyColors, $tone);
}
}
This _colors.scss generates no code at all, it can be imported anywhere at no cost.
Then, in a specific style file:
//test.scss
#use './colors' as *;
//inside a mixin
#mixin special-hue-component($tone){
div.foo {
span.bar {
border-color: my-color($tone);
}
}
}
//or directly
.foobartest {
color: my-color($favoriteBlue);
}

Related

SCSS check for CSS custom var property

How do I conditionally apply a mixin if --my-custom-var is present? For example:
.test {
#if var(--my-custom-var) {
#include someExampleMixin()
}
#if var(--another-custom-var) {
#include someExampleMixin()
}
}
I don't care what the value of the --my-custom-var is but just want to check its existence.
Sass has introduced the variable-exists() function already in alpha. Be aware that Sass can only check for Sass variables. Therefor if you'd really want to use CSS variables you need to define the content of your CSS variable inside a Sass variable, for example $sassVar: /* content */; --cssVar: $sassVar;. Be also aware that the #if statement must be inside a #mixin or a #function to work. I posted a working example below, but here is aslo my Codepen Example since Stack doesn't compile Sass.
Note:
I used "null" inside my $var which basically expresses that there
is no content within this variable, you can pass whatever you
want it won't affect the outcome unless you remove or change the actual
variable.
You can use multiple #if statements which I commented out in this example, but there should always follow an #else statement.
$var: null;
:root {
--someVar: $var;
}
#mixin checkForVariable {
#if variable-exists(var){
body {
background-color: red;
}
}
// #if variable-exists() {
// ...
// }
// #if variable-exists() {
// ...
// }
#else {
body {
background-color: blue;
}
}
}
#include checkForVariable;

Ampersand and mixins in SCSS

Searched but can't find an answer..
I have an element which gets generated (by an external platform) with the following classes: p-button and button.
Now the SCSS is like this:
.p-button {
&.button {
margin: 10px;
}
}
But I want to refactor using mixin includes (this is a big project so there is no other way of making this code better except using mixins). The mixin takes the given selector and applies a . to it. I can't change the mixin, as it is used by many other teams, so I can't pass the ampersand together with the selector. I tried this:
.p-button {
& {
#include button-appearance("button") {
margin: 10px;
}
}
}
But that doesn't work (puts a space between it). You can't do this:
.p-button {
&#include button-appearance("button") {
margin: 10px;
}
}
Anyone have a clue?
EDIT: Here is the mixin
#mixin button-appearance(
$appearance-class,
$show,
$background-color,
$background-image,
$background-position) {
$sel: $button-selector;
#if $appearance-class {
$sel: $sel + '.' + $appearance-class;
}
#{$sel} {
#include normalized-background-image($background-image);
#include show($show);
background-color: $background-color;
background-position: $background-position;
}
#content;
}
EDIT 2: Here is the $button-selector (I can not edit this in the platform, but maybe overwrite it in my own project?)
$button-class: 'p-button';
$button-selector: '.#{$button-class}';
Everyone, finally found the solution. I just removed the &.button from the .p-button mixin include and now it works:
#include button-appearance ("button") { *styles* }
#include button-appearance () { *styles* }
Edited the answer after the original question was edited adding the used and un modifiable mixin
The original mixin does not append the ‘#content’ passed to the mixin to the generated selector. So if you cannot modify the original mixin, the only way is to add your properties outside the mixin. According to the mixin the selector will match a predefined ‘$button-selector’ variable, so it won’t use your class.
So, if you want to use the same class defined in ‘$button-class’, try the following:
#{$button-selector}.button {
margin: 10px;
}
Will output:
.p-button.button {
margin: 10px;
}

SASS equivalent to LESS Space ("+_") syntax

LESS has a great little feature called Space that allows mixins to append rules to existing properties. Really useful for transform() mixins, because you can append many transform rules to the same property, just by calling the mixin multiple times, eg.
Example:
.scale() {
transform+_: scale(2);
}
.rotate() {
transform+_: rotate(15deg);
}
.myclass {
.scale();
.rotate();
}
Outputs:
.myclass {
transform: scale(2) rotate(15deg);
}
I'm trying to get into SASS, but I don't understand how to achieve this with the available syntax. Whatever I do, the output only ever applies one of the transformations, not both. What is the best way to achieve this behaviour using SASS alone?
You can use variable arguments in a mixin like so:
#mixin transform($transforms...) {
transform: $transforms;
}
.myclass {
#include transform(scale(0.5) rotate(30deg));
}
this will output:
.myclass {
transform: scale(0.5) rotate(30deg);
}
You can see a working example here:
http://codepen.io/sonnyprince/pen/RaMzgb
A little more info:
Sometimes it makes sense for a mixin or function to take an unknown
number of arguments. For example, a mixin for creating box shadows
might take any number of shadows as arguments. For these situations,
Sass supports “variable arguments,” which are arguments at the end of
a mixin or function declaration that take all leftover arguments and
package them up as a list. These arguments look just like normal
arguments, but are followed by ....
http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_arguments
Sass does not offer such a feature.
You can get reasonably close by using global variables. However, every single mixin you use, including ones provided by a 3rd party, will have to be modified to work this way.
// the setup
$append-property-vals: (); // global variable
$old-append-property-vals: (); // global variable
#mixin append-property($key, $val, $separator: comma) {
$old-val: map-get($append-property-vals, $key);
#if $old-val {
$append-property-vals: map-merge($append-property-vals, ($key: append($old-val, $val, $separator))) !global;
} #else {
$append-property-vals: map-merge($append-property-vals, ($key: $val)) !global;
}
}
#mixin append-properties {
// cache the original value
$old-append-property-vals: $append-property-vals !global;
#content;
// write out the saved up properties
#each $key, $val in $append-property-vals {
#{$key}: $val;
}
// restore the original value
$append-property-vals: $old-append-property-vals !global;
}
// modify the OP's provided mixins to work
#mixin scale {
// if the vals should be comma delimited, write `comma` instead of `space` for the 3rd argument
#include append-property(transform, scale(2), space);
}
#mixin rotate {
#include append-property(transform, rotate(15deg), space);
}
// call the mixins
.myclass {
#include append-properties {
#include scale;
#include rotate;
}
}
Output:
.myclass {
transform: scale(2) rotate(15deg);
}

#function v/s #mixin in Sass-lang. Which one to use?

After searching a lot in difference between #function and #mixin I ended up here.
Is there any advantage of using #mixin over #funcion or vice versa. In what context they'll be different, how to use them interchangeably, please come up with examples.
Functions are useful specifically because they return values. Mixins are nothing like functions--they usually just provide valuable blocks of code.
Usually, there are cases where you might have to use both.
For example, if I wanted to create a long-shadow with SASS, I would call a function like so:
#function makelongshadow($color) {
$val: 0px 0px $color;
#for $i from 1 through 200 {
$val: #{$val}, #{$i}px #{$i}px #{$color};
}
#return $val;
}
Which would then be called with this mixin:
#mixin longshadow($color) {
text-shadow: makelongshadow($color);
}
Which provides us with the actual code.
That gets included in the element:
h1 {
#include longshadow(darken($color, 5% ));
}
#function is useful when you want to reuse it on different CSS properties.
Example, You have dynamic values that you want to use on both height and min-height, then using #function is the one you would use:
#function dynamic-height($height, $padding) {
#return $height + $padding;
}
.foo {
min-height: dynamic-height(300px, 30px);
}
.bar {
height: dynamic-height(300px, 30px);
}
But if you want to reuse it with same CSS properties, then you would use a #mixin:
#mixin dynamic-height($height, $padding) {
min-height: $height;
padding: $padding;
}
.foo {
#include #dynamic-height(300px, 30px);
}
think of #mixin as it is just a #function that returns a block of style rules.
also, you can use placeholders (like %selector) and #extend

Escaping # in SASS

Here's an example of a mixin I'm using:
#mixin gradient($from, $to, $height) {
background-color: #{$to};
background-image: url("/media/img/gradient/4/#{$height}/#{$from}/#{$to}/");
background-repeat:repeat-x;
}
The problem is that the $from and $to colors are passed to the url without the #, so a typical call looks like this:
#include gradient(ff00ff, 00ff00, 600);
and the background-color needs a hash in front of it. I want to write the line in the mixin like this:
background-color: ##{$to};
but that doesn't work... any ideas?
I needed to do something similar, and as this was the first result on Google I thought I'd add how I got around this.
I had to pass a hex colour and use the value in the class name and as a real colour. So I created an addHash function.
Here's a simple example...
#function addhash($input) {
#return unquote("#" + $input);
}
#mixin setColour($colour, $textColour:"0000ff")
{
.headerButtons .colour#{$colour} a{
background:addhash($colour);
color:addhash($textColour);
}
}
#include setColour("00ff00");
This outputs CSS like this...
.headerButtons .colour00ff00 a {
background: #00ff00;
color: #0000ff;
}
Hopefully this will be helpful for somebody.
In the end I decided to rewrite:
#include gradient(ff00ff, 00ff00, 600);
as
#include gradient("#ff00ff", "#00ff00", "600");
which works as expected.
Maybe this will work:
#{"#" + $myVar}

Resources