I'm trying to use string interpolation on my variable to reference another variable:
// Set up variable and mixin
$foo-baz: 20px;
#mixin do-this($bar) {
width: $foo-#{$bar};
}
// Use mixin by passing 'baz' string as a param for use $foo-baz variable in the mixin
#include do-this('baz');
But when I do this, I get the following error:
Undefined variable: "$foo-".
Does Sass support PHP-style variable variables?
This is actually possible to do using SASS maps instead of variables. Here is a quick example:
Referencing dynamically:
$colors: (
blue: #007dc6,
blue-hover: #3da1e0
);
#mixin colorSet($colorName) {
color: map-get($colors, $colorName);
&:hover {
color: map-get($colors, #{$colorName}-hover);
}
}
a {
#include colorSet(blue);
}
Outputs as:
a { color:#007dc6 }
a:hover { color:#3da1e0 }
Creating dynamically:
#function addColorSet($colorName, $colorValue, $colorHoverValue: null) {
$colorHoverValue: if($colorHoverValue == null, darken( $colorValue, 10% ), $colorHoverValue);
$colors: map-merge($colors, (
$colorName: $colorValue,
#{$colorName}-hover: $colorHoverValue
));
#return $colors;
}
#each $color in blue, red {
#if not map-has-key($colors, $color) {
$colors: addColorSet($color, $color);
}
a {
&.#{$color} { #include colorSet($color); }
}
}
Outputs as:
a.blue { color: #007dc6; }
a.blue:hover { color: #3da1e0; }
a.red { color: red; }
a.red:hover { color: #cc0000; }
Sass does not allow variables to be created or accessed dynamically. However, you can use lists for similar behavior.
scss:
$list: 20px 30px 40px;
#mixin get-from-list($index) {
width: nth($list, $index);
}
$item-number: 2;
#smth {
#include get-from-list($item-number);
}
css generated:
#smth {
width: 30px;
}
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#lists
http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#list-functions
Anytime I need to use a conditional value, I lean on functions. Here's a simple example.
$foo: 2em;
$bar: 1.5em;
#function foo-or-bar($value) {
#if $value == "foo" {
#return $foo;
}
#else {
#return $bar;
}
}
#mixin do-this($thing) {
width: foo-or-bar($thing);
}
Here's another option if you're working with rails, and possibly under other circumstances.
If you add .erb to the end of the file extension, Rails will process erb on the file before sending it to the SASS interpreter. This gives you a can chance to do what you want in Ruby.
For example: (File: foo.css.scss.erb)
// Set up variable and mixin
$foo-baz: 20px; // variable
<%
def do_this(bar)
"width: $foo-#{bar};"
end
%>
#target {
<%= do_this('baz') %>
}
Results in the following scss:
// Set up variable and mixin
$foo-baz: 20px; // variable
#target {
width: $foo-baz;
}
Which, of coarse, results in the following css:
#target {
width: 20px;
}
I came across the need to reference a colour dynamically recently.
I have a _colours.scss file for every project, where I define all my colours once and reference them as variables throughout.
In my _forms.scss file I wanted to setup button styles for each colour available. Usually a tedious task. This helped me to avoid having to write the same code for each different colour.
The only downside is that you have to list each colour name and value prior to writing the actual css.
// $red, $blue - variables defined in _colours.scss
$colours:
'red' $red,
'blue' $blue;
#each $name, $colour in $colours {
.button.has-#{$name}-background-color:hover {
background-color: lighten($colour, 15%);
}
}
I needed to use dynamic color values in sass variables.
After lots of search, I applied this solution:
In application.html.erb:
<style>
:root {
--primary-color: <%= current_client.header_color %>;
--body-color: <%= current_client.footer_color %>;
}
</style>
In variables.sass:
$primary: var(--primary-color);
And boom you are good to go!
Reference: https://medium.com/angular-in-depth/build-truly-dynamic-theme-with-css-variables-539516e95837
To make a dynamic variable is not possible in SASS as of now, since you will be adding/connecting another var that needs to be parsed once when you run the sass command.
As soon as the command runs, it will throw an error for Invalid CSS, since all your declared variables will follow hoisting.
Once run, you can't declare variables again on the fly
To know that I have understood this, kindly state if the following is correct:
you want to declare variables where the next part (word) is dynamic
something like
$list: 100 200 300;
#each $n in $list {
$font-$n: normal $n 12px/1 Arial;
}
// should result in something like
$font-100: normal 100 12px/1 Arial;
$font-200: normal 200 12px/1 Arial;
$font-300: normal 300 12px/1 Arial;
// So that we can use it as follows when needed
.span {
font: $font-200;
p {
font: $font-100
}
}
If this is what you want, I am afraid as of now, this is not allowed
Related
I am trying to set up a color scheme in SCSS where I can have the following HTML:
<div class="swatch" data-bg="green">...</div>
I have a SCSS mixin defined as such:
#function color($key: 'black') {
#return map-get($colors, $key);
}
So, if I pass it background-color: color('green'), it will look at the $colors: ( ... ) map, see 'green': #009900, and return background-color: #009900; as the CSS.
The problem comes when I try to pass the data-bg attribute value into the color() SCSS mixin, like so:
.swatch[data-bg] {
background-color: color(attr(data-bg));
}
This doesn't work. I would expect it to parse the value as such:
color(attr(data-bg)) → color('green') → #009900
However, SCSS won't even render that background-color line in the CSS at all.
I have a Codepen where you can see what I'm trying to go for. It's the "Brown" color swatch here: https://codepen.io/rbrum/pen/axZLxw
Any help would be greatly appreciated.
For anyone else who happens across this question, here is how I ended up resolving my issue.
Instead of relying on data- attributes, I just relied on class names instead. Whenever I want an element with a certain background color, for instance, I use a class name like .bg-amber or .bg-purple. My colors are defined as such:
$colors: (
'black': #000000,
'white': #FFFFFF,
// ...
'amber': #FFBF00,
'purple': #800080,
// ...
);
To make it easier to access a color, I have defined a function that calls any color by name:
#function c($key: 'black') {
#return map-get($colors, $key);
}
I then define a mixin that, given a color name, will apply it as the background color. I can also pass it a prefix that is used in the CSS attribute.
#mixin bg($color-name, $prefix: '') {
.#{$prefix}#{$color-name} {
background-color: c($color-name);
}
}
If I wanted to use it in a one-off situation, I would use it like so:
#include bg('amber', 'bg-');
...which would generate the following:
.bg-amber {
background-color: #FFBF00;
}
Finally, I use an #each loop to do this for all of my colors:
#each $color-name, $color-val in $colors {
#include bg($color-name, 'bg-');
}
I can also define a "foreground" version:
#mixin fg($color-name, $prefix: '') {
.#{$prefix}#{$color-name} {
color: c($color-name);
}
}
And then I can use it in the #each loop right below the bg() usage:
#each $color-name, $color-val in $colors {
#include bg($color-name, 'bg-');
#include fg($color-name, 'txt-');
}
It can also be extended for things like border colors, box shadows, and more.
Is it possible to access an inner variable ?
Given:
.method() {
#variable:100:
}
.class {
z-index:.method.#variable;
}
Is this possible somehow?
Not in the exact way you're thinking, but you can achieve that result. Depending on your styles you may need to adjust things to fit this model. (LESS does a great job at resolving references regardless of the order things appear in a file, but putting dependencies above the things that call them is a helpful organization habit.)
.class(#x) {
z-index: #x;
}
.method() {
#variable: 100;
.class(#variable);
}
Note that if you don't need to use #variable anywhere else inside .method() you can skip the declaration. That is, declare a variable if you want to do something like
.method() {
#variable: 100;
.class(#variable);
padding-right: unit(#variable,px);
}
and if you don't need the variable save yourself that line:
.method() {
.class(100);
color: red;
}
Edit:
If you want to be able to pass a value to .method, let the mixin take an argument rather than defining the variable within the mixin. With this you can do both .example {method(100)} and .example{method(200)}.
.class(#x) {
z-index: #x;
}
.method(#x) {
.class(#x);
}
If .class needs to be able to work without being passed an argument, you can provide it with a default value. Given
.class(#x:100) {
z-index: #x
}
.example{.class} will compile to the same thing as .example{.class(100)} would.
I'm not sure if I understand right your question but I have a solution, I hope that you find it useful:
In Less:
.method(){
#variable:100;
}
.method2(){
#variable:200;
}
.class {
.method();
z-index:#variable;
}
.class2 {
.method2();
z-index:#variable;
}
In SASS
#function method() {
$variable: 100;
#return $variable;
}
#function method2() {
$variable: 200;
#return $variable;
}
.class {
z-index: method();
}
.class2 {
z-index: method2();
}
CSS output for SASS/LESS:
.class {
z-index: 100;
}
.class2 {
z-index: 200;
}
I am trying to put font family for a div if the variable is not equal to null.
my less code is
div.content {
& when (isstring(#contentFont)) {
font-family: #contentFont;
}
}
the output that I get from css is
div.content when (isstring(#contentFont)) {
font-family: Abel;
}
my problem is, the style is not applying for the div.content, not sure what i am doing wrong. Any help would be greatly appreciated.
As discussed in the comments, you're using version 0.4.0 of lessphp – which doesn't seem to support the shorthand guard (when) syntax that you're trying to use.
It looks like it does support guards on mixins, however.
Try splitting your code into a mixin and a usage of this mixin, like this:
/* the mixin */
.fontIfString(#font) when (isstring(#font)) {
font-family: #font;
}
/* usage */
#contentFont: "hello";
div.content {
.fontIfString(#contentFont);
}
How can I overwrite an entire CSS style for a class, id or other CSS selector?
For example:
If in styles1.css I have:
/* also, this file contains a lot of styles used on other pages */
.one-great-class {
background: white
...
/* a lot of properties */
}
... and in styles2.css (that is used only in one web page) I want to overwrite the class one-great-class completely what have I do to write?
.one-great-class {
/* Is possible that a line of code to delete all styles from this class? */
}
It's not possible in CSS at the moment.
But there may eventually be a property that does this: all
It can take three values:
initial | inherited | unset
Taken from the Cascading and Inheritance Module:
"For example, if an author specifies all: initial on an element it will block all inheritance and reset all properties, as if no rules appeared in the author, user, or user-agent levels of the cascade. "
According to the MDN documentation as of June 2017, all is currently supported by Chrome, Firefox/Mobile, and Opera. Safari supports only the CSS4 value revert, which is not supported by the other browsers.
.one-great-class {
border-radius: 50% 35% / 20% 25% 60%;
color: red;
font: 12px/14px Arial, serif;
height: 20em;
width: 20em;
/*... etc. */
}
.one-great-class {
all: initial;
}
Tested to work with IE9, Chrome and Opera. I had a problem with this when I wrote it, so decided that rather than changing existing rules, that I'd just append a new rule after the existing ones. From memory, the problem was with the default browser found in Android 2.3
Altering an existing rule seemed to be a better(cleaner) solution, though appending new rules ultimately proved to be chosen path. (I was changing background images by creating images with a canvas and then setting the background-image property. The images could be quite large, hence the preference for update)
Function
function replaceRuleAttrib(ruleSelector, attribText, newValue)
{
var nSheets, nRules, sheetNum, curSheet, curStyle, curAttrib;
var nSheets = document.styleSheets.length;
if (nSheets == 0)
document.head.appendChild(document.createElement('style'));
else
for (sheetNum = 0; sheetNum<nSheets; sheetNum++)
{
curSheet = document.styleSheets[sheetNum];
nRules = curSheet.cssRules.length;
for (ruleNum=0; ruleNum<nRules; ruleNum++)
{
curRule = curSheet.cssRules[ruleNum];
if (curRule.selectorText == ruleSelector)
{
for (styleI=0; styleI<curRule.style.length; styleI++)
{
styleName = curRule.style[styleI];
styleVal = curRule.style[styleName];
if (styleName == attribText)
{
curRule.style[styleName] = newValue;
return true;
}
}
}
}
}
document.styleSheets[0].insertRule( ruleSelector+'{' + attribText + ": " + newValue + "; }", 0);
}
Sample CSS (before)
<style>
h1
{
color: red;
}
</style>
Usage:
function onHeadingClick()
{
replaceRuleAttrib('h1', 'color', 'green');
}
Sample CSS (after)
<style>
h1
{
color: green;
}
</style>
Browser will apply css that come last.
.class {
font-size: 16px;
font-size: 14px;
}
The class will get font-size value 14px.
You can decleare a css as final.
.class {
font-size: 14px !important;
}
no genarel css rule can alter it.
Browser uses this method to give priority
inline < embeded < external < user-agent.
If you think you need more controll on css then use javascript to directly modfy dom.
I want to create a function in SASS that generates different classes.
Something like this
#function test($class-name) {
#for $i from 1 through $tot-class {
.#{$class-name}-#{$i} {
//some rules
}
}
}
but i can't figure how to call this function.
I've tried with
#test(red);
or
test(red);
but it doesn't seem to work.
Which is the right way?
The main problem here is that you don't actually want to use a function, you want a mixin. The difference is that functions don't contain any CSS rules - they simply return a value (which you can assign to a variable or use in a CSS property declaration). Mixins, on the other hand, have no return value and can contain full-blown CSS rules to be added when that mixin is included into the SASS document. Here's what your example would look like as a mixin:
#mixin test($class-name) {
#for $i from 1 through $tot-class {
.#{$class-name}-#{$i} {
//some rules
}
}
}
You'd then include the mixin later by using:
#include test(red);