Any way to use CSS Variables in SASS functions for hsl() transparency?
Asked Answered
B

8

46

I have defined a color in my root:

:root {
--purple: hsl(266, 35%, 70%);
}

And I'm trying to use it in a SASS function to give it transparency:

.purple {
  background: transparentize(#{"var(--primary-color)"}, 0.7)
}

Does anyone know of a way to get this to work? Or is it just not possible?

Bedlam answered 17/8, 2018 at 3:58 Comment(2)
Give a read : codepen.io/jakealbaugh/post/css4-variables-and-sassPansy
Good link! OP: not possible in general because CSS Variables are dynamic: their values are calculated each time they're used. No way Sass can know for sure which value they may have at compile time. Sass variables have values known at compile time. If you're defining constants in Sass and using them to define CSS Variables in :root and not playing with latter in children node, then you can use them in both.Militant
N
16

Global variables can be defined outside of an element in a :root pseudo-class:

:root {
  --color-primary: #FFFFFF;
}

you can define a function like this:

@function color($color-name) {
  @return var(--color-#{$color-name});
}

and call it into your sass:

body { 
  color: color(primary); 
}

compiled sass code is:

body { 
  color: var(--color-primary); 
}
Neoarsphenamine answered 17/8, 2018 at 10:9 Comment(6)
@orionrush no it's not. transparentize() expects a hex code. this function outputs a var() string which is not valid.Faery
this does not answer the original questionVitta
This does not work on a function that needs to have valid color, so this cannot be part for the argument of lightenCahilly
This doesn't answer the question at all. I don't know why it's upvoted. Maybe I am wrong, can you give an example with transparentize as in the question?Appendix
this doesn't answer the original questionDunham
As the other comments says, the sass function expects a color not another function thus this not resolver the issue.Alainaalaine
D
12
#{var(--variablename)}

This is how you use CSS variables in SCSS

Daladier answered 11/1, 2022 at 19:37 Comment(2)
It should not. You can use CSS variables in SCSS like that, but not in SCSS functions. And the question is specifically about functions. transparentize(#{var(--someColor)}, 0.7) will not work (for the reasons mentioned in the comments and other answers)Bassesalpes
This answer is plain wrong. SASS is evaluated during build time, CSS variables are evaluated during runtime, using SASS string interpolation doesn't change the fact. So SASS treats var(--foo) as a string anyway and just passes it through.Sulemasulf
B
9

If you are looking for a way to adjust colors using sass color functions (I feel like this is the main reason why people get here), here is a solution for you. The idea is the same as in Juanete Montoya's answer, but it's pure sass/scss (no php):

First, we need to split a color into hsla values and save each one in a separate custom property.

@mixin define-color($title, $color) {
    --#{$title}-h: #{hue($color)};
    --#{$title}-l: #{lightness($color)};
    --#{$title}-s: #{saturation($color)};
    --#{$title}-a: #{alpha($color)};
}

Now we can put it back together, making some adjustments on the way.

@function color($title, $hue: 0deg, $lightness: 0%, $saturation: 0%, $alpha: 0) {
    @return hsla(
        calc(var(--#{$title}-h) + #{$hue}), 
        calc(var(--#{$title}-s) + #{$saturation}),
        calc(var(--#{$title}-l) + #{$lightness}),
        calc(var(--#{$title}-a) + #{$alpha}),
    );
}

Now we are ready to define some color variables...

:root {
    @include define-color("primary", #696969);
    @include define-color("secondary", blue);
}

use and adjust them!

.example-class {
    color: color("primary");
    background: color("secondary", $lightness: +20%, $alpha: -0.3);
    border: 1px solid color("primary", $hue: -30deg, $saturation: 5%);
}
Bassesalpes answered 16/2, 2022 at 21:37 Comment(0)
G
6

Unfortunately, this is impossible. CSS variables are subsituted in for their values at runtime. The SASS compiler is just that -- a compiler, making it unable to read the value of CSS variables, since CSS variables can be defined in other files.

Geopolitics answered 6/2, 2022 at 1:13 Comment(0)
S
4

Simple solution, needs two variables instead of one

Similar to Romalex's answer there is an elegant solution explained in this article: How to use CSS variables with Sass Mixins

You can define your color variable with RGB 3-number list, then you can manipulate that with Sass transparentize and other color functions.

:root {
  --color-primary: #16498a;
  --color-primary-rgb: 22, 73, 138;
}

h1 {
  color: var(--color-primary);
}

h2 {
  color: rgba(var(--color-primary-rgb), 0.4);
}

You can write your own Sass functions to deal with the doubling of the variables, like in the following example.

Complete solution with custom Sass functions

In this article you have such an example:

How to combine SASS color functions and CSS Variables

Example use:

:root {
  @include defineColorHSL(--color-primary, 220, 90%, 56%);
}

.component {
  color: alpha(var(--color-primary), 0.2); // it works 🎉
}

Needed resources:

// return css color variable with different opacity value
@function alpha($color, $opacity){
  $color: str-replace($color, 'var(');
  $color: str-replace($color, ')');
  $color-h: var(#{$color+'-h'});
  $color-s: var(#{$color+'-s'});
  $color-l: var(#{$color+'-l'});
  @return hsla($color-h, $color-s, $color-l, $opacity);
}

// replace substring with another string
// credits: https://css-tricks.com/snippets/sass/str-replace-function/
@function str-replace($string, $search, $replace: '') {
  $index: str-index($string, $search);
  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
  }
  @return $string;
}


@mixin defineColorHSL($color, $hue, $saturation, $lightness){
  #{$color}: unquote("hsl(#{$hue}, #{$saturation}, #{$lightness})");#{$color}-h: #{$hue};#{$color}-s: #{$saturation};#{$color}-l: #{$lightness};
}
Sacculus answered 25/8, 2022 at 20:2 Comment(0)
C
3

SASS transpiling is executed first, so it is better to set the SASS variable first and then connect it with the --css_variables.

To achieve that you need to @use "sass:meta"; like in this example:

@use "sass:meta";


//sass variable
$primarySassVariable: #4252af;


//css variable
:root {
  --primary: #{meta.inspect($primarySassVariable)};
}
Camire answered 23/7, 2021 at 13:47 Comment(2)
this seems like the right way to go, but your example also does not include the original questions intent: to use the variable in the transparentize function. It probably works since its just a SCSS variable now? But would be good to put it in the answer. But yes, if setting the css variable from SCSS is an option for people then this seems to bypass all the need to transform the variable.Laruelarum
Also, just thinking this through, if you go this route and you want to use the CSS variables in your components (again like in the OPs question) so that you can change it through JS... then the code that sets the CSS variable should be executed after any SCSS code that may want to change the SCSS variables (when importing a theme for example and changing one value), but before any SCSS code for your actual UI if that UI uses the CSS variables. Correct? Because once the bit that sets CSS variable is read & parsed, changing the SCSS variable after that would have no effectLaruelarum
C
0

I realize that this isn't quite what the original question is asking, but I had a similar issue when trying to use a SASS variable to define a CSS variable. Based on initial research, I tried transparentize(#{$purple}, 0.7), which failed. Instead, the #{ } needed to surround the function:

$purple: #abc123;

:root {
  --purple: #{transparentize($purple, 0.7)};
}

Just wanted to share this given that I have yet to find any examples of CSS variables defined using functions and SASS variables in this way. For a moment, the answers here made me think it was not possible, but I misunderstood the question.

Cramoisy answered 8/12, 2022 at 7:54 Comment(1)
SCSS compiles styles to CSS, so whatever is in the SCSS file, will be coded in the final file. Any SASS functions are calculated and applied during that compilation, so when it finishes, that's it. CSS variables are dynamically applied through CSS itself. This is why you cannot combine it in this manner, from your example.Austin
N
-3

My solution was transform HEX code color to HSL. First i've created a PHP Function.

function hexToHsl($hex) {
    $hex = str_split(ltrim($hex, '#'), 2);
    // convert the rgb values to the range 0-1
    $rgb = array_map(function($part) {
        return hexdec($part) / 255;
    }, $hex);

    // find the minimum and maximum values of r, g and b
    $min = min($rgb);
    $max = max($rgb);

    // calculate the luminace value by adding the max and min values and divide by 2
    $l = ($min + $max) / 2;
    if ($max === $min) {
        $h = $s = 0;
    } else {
        if ($l < 0.5) {
            $s = ($max - $min) / ($max + $min);
        } elseif ($l > 0.5) {
            $s = ($max - $min) / (2 - $max - $min);
        }
        if ($max === $rgb[0]) {
            $h = ($rgb[1]- $rgb[2]) / ($max -$min);
        } elseif ($max === $rgb[1]) {
            $h = 2 + ($rgb[2]- $rgb[0]) / ($max -$min);
        } elseif ($max === $rgb[2]) {
            $h = 4 + ($rgb[0]- $rgb[1]) / ($max -$min);
        }
        $h = $h * 60;
        if ($h < 0) {
            $h = $h + 360;
        }
    }

    return array($h, $s*100, $l*100);
}

it will receive the hex code color por example #cc66cc and will return an array of 3 values for H, S and L.

$color_main_hsl = hexToHsl($color_main);
$color_main_h = $color_main_hsl[0];
$color_main_s = $color_main_hsl[1] . '%';
$color_main_l = $color_main_hsl[2] . '%';

And then assign to a variable CSS.

<style>
    :root {
        --color_1: <?php echo $color_main; ?>;
        --color_1_h: <?php echo $color_main_h; ?>;
        --color_1_s: <?php echo $color_main_s; ?>;
        --color_1_l: <?php echo $color_main_l; ?>;
    }
</style>

Then i create 2 functions in SASSS, 1 for darken and other for lighten:

// hsl css variable darken
@function hsl_d($color_num, $percentage) {
    $color_h: var(--color_#{$color_num}_h);
    $color_s: var(--color_#{$color_num}_s);
    $color_l: var(--color_#{$color_num}_l);
    @return hsl($color_h, $color_s, calc(#{$color_l} - #{$percentage}#{'%'}));
}

// hsl css variable lighten
@function hsl_l($color_num, $percentage) {
    $color_h: var(--color_#{$color_num}_h);
    $color_s: var(--color_#{$color_num}_s);
    $color_l: var(--color_#{$color_num}_l);
    @return hsl($color_h, $color_s, calc(#{$color_l} + #{$percentage}#{'%'}));
}

And finally I use my sass function:

.element-to-dark {
    background-color: hsl_d(1, 12);
}

.element-to-light {
    background-color: hsl_d(1, 22);
}

The number "1" is because my variables ara called --color_1, --color_1_h, --color_1_s, --color_1_l and my function interpolate this number. The second parameter is the percentage for light or darken.

I hope I have contributed :)

Nidus answered 15/6, 2019 at 4:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.