Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

rgb and rgba functions misinterpret CSS variables as a single value instead of their output #2251

Open
jannisborgers opened this issue Feb 14, 2018 · 30 comments

Comments

@jannisborgers
Copy link

jannisborgers commented Feb 14, 2018

Example:

:root { --color--text: 18, 18, 26; }
body { color: rgba(var(--color--text), 0.8); }

Expected outcome:

body {
color: rgba(18, 18, 26, 0.8);
}

Actual outcome:

When using rgba():

{
  "status": 1,
  "file": "/path/to/partial.scss",
  "line": 30,
  "column": 23,
  "message": "argument `$color` of `rgba($color, $alpha)` must be a color\n\nBacktrace:\n\tsrc/scss/elements/_projectTeaser.scss:30, in function `rgba`\n\tsrc/scss/elements/_projectTeaser.scss:30",
  "formatted": "Error: argument `$color` of `rgba($color, $alpha)` must be a color\n\n       Backtrace:\n       \tsrc/scss/elements/_projectTeaser.scss:30, in function `rgba`\n       \tsrc/scss/elements/_projectTeaser.scss:30\n        on line 30 of src/scss/elements/_projectTeaser.scss\n>>     background-color: rgba(var(--color--fadePrimary), 0.8);\n   ----------------------^\n"
}

When using rgb(), without the alpha-value:

{
  "status": 1,
  "file": "/path/to/partial.scss",
  "line": 30,
  "column": 23,
  "message": "Function rgb is missing argument $green.",
  "formatted": "Error: Function rgb is missing argument $green.\n        on line 30 of src/scss/elements/_projectTeaser.scss\n>>     background-color: rgb(var(--color--fadePrimary));\n   ----------------------^\n"
}

Notes:
This works if I wrap the CSS variable values in their own rgb() function and just output those as property values, but in order to add different alpha-values on the fly, storing only the R, G, and B values in variables is a lot more flexible.

As I posted this on Twitter and instantly got a backlash: please note that of course I could use $sass-variables instead, but CSS variables are a lot better to work with for obvious reasons (mutability, element scoping).

Versions:

- NPM version (`npm -v`): 5.6.0
- Node version (`node -v`): 6.10.2
- Node Process (`node -p process.versions`): 
{ http_parser: '2.7.0',
  node: '6.10.2',
  v8: '5.1.281.98',
  uv: '1.9.1',
  zlib: '1.2.11',
  ares: '1.10.1-DEV',
  icu: '58.2',
  modules: '48',
  openssl: '1.0.2k' }
- Node Platform (`node -p process.platform`): darwin
- Node architecture (`node -p process.arch`): x64
- node-sass version (`node -p "require('node-sass').info"`): 
node-sass       4.7.2   (Wrapper)       [JavaScript]
libsass         3.5.0.beta.2    (Sass Compiler) [C/C++]
- npm node-sass versions (`npm ls node-sass`): [email protected]
@nschonni
Copy link
Contributor

var support doesn't come until sass/libsass#2244 is landed in this repo

@jannisborgers
Copy link
Author

@nschonni I see, thank you.

Temporary workaround:
Ana Tudor noticed on Twitter that you can use RGB((--var)) or RGBA((--var),0.8) (note the uppercase function name), as sass will ignore it and CSS will parse it just fine.
(see https://twitter.com/anatudor/status/963758591529365504)

Jakob E also noted that wrapping the whole function in #{' '} interpolation will work, although this approach failed in my case.
(see https://twitter.com/anatudor/status/963758591529365504)

@fvsch
Copy link

fvsch commented Feb 16, 2018

Unless I’m mistaken the expected outcome is not possible and will not be possible with what @nschonni mentions.

CSS Custom Properties are dynamic, they inherit through the DOM tree and can be set in JS at run-time, can be set on different DOM nodes by selectors, etc. Some Postcss plugins use the CSS Custom Properties syntax with :root { --some-var: … } and treat them as global variables similar to Sass variables, which is a pretty bad idea IMO.

This code should always throw:

body { color: rgba(var(--color--text), 0.8); }

And this one (a pure CSS feature) should probably be passed as-is by Sass:

body { color: color(var(--color--text) alpha(0.8)); }

But note it’s syntax from a very incomplete draft of CSS Color Module Level 4, so syntax may change and browser support is far off. (It also introduces a bunch of syntax that Sass will probably choke on, like + modifiers before numeric values that have a meaning in CSS and should be left alone by Sass.)

@xzyfer
Copy link
Contributor

xzyfer commented Mar 10, 2018

@fvsch is mostly correct.

The expected output is impossible because rgba(color, alpha) is a Sass function.
The color argument must be a Sass Color type.
Sass does not evaluation css custom property values, because these are run-time variables.

However this input should not throw. Node Sass should pass through values it doesn't understand, but are still valid CSS values. The correct output should be:

:root {
  --color--text: 18, 18, 26; }

body {
  color: rgba(var(--color--text), 0.8); }

This output is obviously jibberish, but the behaviour is correct as far as Sass is concerned.
This will be resolved in v4.8.0.

@xzyfer
Copy link
Contributor

xzyfer commented Mar 10, 2018

Fixed in 4.8.0

@xzyfer xzyfer closed this as completed Mar 10, 2018
@ro0gr
Copy link

ro0gr commented Jul 3, 2018

Unfortunately it doesn't seem to be fixed.

I've just installed [email protected] and the following code snipet:

var sass = require('node-sass');
var result = sass.renderSync({
    data: `body {
        --c: 1,2,3;
        background-color: rgb(var(--c));
    }`
});
console.log(result);

fails for me with the following stack trace:

Ruslans-MBP:sass-test RHR$ node index.js
/Users/RHR/workspace/tmp/sass-test/node_modules/node-sass/lib/index.js:439
  throw assign(new Error(), JSON.parse(result.error));
  ^

Error: Function rgb is missing argument $green.
    at Object.module.exports.renderSync (/Users/RHR/workspace/tmp/sass-test/node_modules/node-sass/lib/index.js:439:16)
    at Object.<anonymous> (/Users/RHR/workspace/tmp/sass-test/index.js:2:19)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:140:18)
    at node.js:1043:3

@xzyfer do I miss something, do I need some special treatment to make it work?

@mrahhal
Copy link

mrahhal commented Jul 19, 2018

This only seems to work when setting css variables. In @ro0gr's example, this still causes problems. But then.. I don't understand, was this only fixed when setting css vars?

Uppercase RGB seems to be working for now though.

@xzyfer
Copy link
Contributor

xzyfer commented Jul 19, 2018

Can you confirm you're using 4.9.0 with npm ls node-sass?

@xzyfer
Copy link
Contributor

xzyfer commented Jul 19, 2018

Looks like we don't have test for RGB with a single argument. https://github.com/sass/sass-spec/pull/1002/files

It's entirely possible this specific case is not handle correctly.

@xzyfer xzyfer reopened this Jul 19, 2018
@mrahhal
Copy link

mrahhal commented Jul 19, 2018

I am using [email protected].

It only works with:

--some-var: rgb(var(...));

Never with:

color: rgb(var(...));

Had to use RGB.

@UstymUkhman
Copy link

I think I'm having a related issue today. My test was:

$green-code: '0, 204, 0';
$green: unquote('rgb(#{$green-code})');

$gray: rgb(33, 33, 33);

.test {
  background-color: rgba($green, 0.5); // This line gives me "$color: rgb(0, 204, 0) is not a color."
  color: rgba($gray, 0.5);
  color: $green;
}

Although, as you can test here, both $green and $gray are RGB variables.

  • NPM version (npm -v): 6.1.0
  • Node version (node -v): v8.11.2
  • Node Process (node -p process.versions):
    {
    http_parser: '2.8.0',
    node: '8.11.2',
    v8: '6.2.414.54',
    uv: '1.19.1',
    zlib: '1.2.11',
    ares: '1.10.1-DEV',
    modules: '57',
    nghttp2: '1.29.0',
    napi: '3',
    openssl: '1.0.2o',
    icu: '60.1',
    unicode: '10.0',
    cldr: '32.0',
    tz: '2017c'
    }
  • Node Platform (node -p process.platform): win32
  • Node architecture (node -p process.arch): x64
  • node-sass version (node -p "require('node-sass').info"):
    node-sass 4.9.0 (Wrapper) [JavaScript]
    libsass 3.5.4 (Sass Compiler) [C/C++]
  • npm node-sass versions (npm ls node-sass): `-- [email protected]

@vasicvuk
Copy link

vasicvuk commented Oct 19, 2018

Same problem with latest version getting:
Argument `$color` of `fade-out($color, $amount)` must be a color
where $color is var(--primary)

@jalovatt
Copy link

jalovatt commented Nov 9, 2018

For what it's worth the same problem exists using the other color functions:

--bg-color: 210, 25%, 15%;
background-color: hsla(var(--bg-color), 0.9);

--> Error: Function hsl is missing argument $lightness.

Running node-sass 4.10.0 / libsass 3.5.4.

@Jordaneisenburger
Copy link

I'm also having the issue described above, is there already a solution ?

@ro0gr
Copy link

ro0gr commented Nov 28, 2018

@Jordaneisenburger definitely not a solution, but defining custom rgb/rgba functions which accepts a single argument allows to work-around the issue for us:

@function rgb($r, $g: null, $b: null ) {
  @if ($g == null) {
    @return unquote('rgb(#{$r})');
  }

  @if ($g and $b) {
    @return unquote('rgb(#{$r}, #{$g}, #{$b})');
  }

  @error "wrong number of arguments";
}

@function rgba($r, $g, $b: null, $a: null ) {
  @if ($b == null) {
    @return unquote('rgba(#{$r}, #{$g})');
  }

  @if ($b and $a) {
    @return unquote('rgba(#{$r}, #{$g}, #{$b}, #{$a})');
  }

  @error "wrong number of arguments";
}

@Jordaneisenburger
Copy link

@ro0gr thanks for the response, unfortunately i didn't get it to work because the rgb() functions are used inside a .css file. I've tried to include a fix.scss file with your function in it at the top of the src code but somehow didn't work. Thanks anyway!

@rezalas
Copy link

rezalas commented Jul 12, 2019

This issue appears to still exist in 4.12 and applies to hsl as well.

html {
    --hue: 210;
    --background: hsl(var(--hue), 20%, 12%);
}

.testClass {
    background: var(--background);
}

Will always attempt to interpret the variable as SASS and throw an error that hue must be a number.

@mattlohkamp
Copy link

mattlohkamp commented Jun 18, 2020

I've gotta admit I'm surprised that this is still an issue, in v4.14.1, almost two and a half years later - using RGBA() instead of rgba() is still a valid workaround though.

@AdrianoCahete
Copy link

I'm migrating back to Sass since Stylus is deprecated and stepped into a problem that I think that it's the same as this issue.

I'm trying to update an old function that I have to determine if background is dark or not and choose the text color, but I found that this not work with CSS variables.

// vars file
:root {
  --primaryColor: rgba(39, 190, 213, 1);

  --textColor: rgba(51, 51, 51, 1);
  --textColorInverted: rgba(255, 255, 255, 1);
}
// Function
@function isDark($color) {
  @if (lightness( $color ) > 40) {
      @return var(--textColor);
    }
  @else {
    @return var(--textColorInverted);
  }
}
// In use
button {
  background-color: var(--primaryColor);
  color: isDark(var(--primaryColor));
}

The error is:

SassError: argument `$color` of `lightness($color)` must be a color

@cpylua
Copy link

cpylua commented Nov 20, 2020

dart-sass doesn't seem to have this issue, so switch to dart-sass if possible.

If you're out of luck with dart-sass and don't like the uppercase RGB workaround, here's another solution for node-sass:

:root { 
    --color--text: 18, 18, 26; 
}

$color-background: var(--background-color, 237, 237, 237);

body { 
    color: unquote("rgba(var(--color--text), 0.8)");
    
    // or with sass variable
    background: unquote("rbg(#{$color-background})");
}

Use a function if you don't want to write unquote every time.

:root { 
    --color--text: 18, 18, 26; 
}

$color-background: var(--background-color, 237, 237, 237);

@function safe-rgb($var) {
    @return unquote("rgb(#{$var})");
}

@function safe-rgb2($css-var) {
    @return unquote("rgb(var(#{$css-var}))");
}

body { 
    background: safe-rgb($color-background);
    color: safe-rgb2(--color-text);
}

@BenRacicot
Copy link

BenRacicot commented Nov 28, 2020

Working within Angular 11.0.2. Can confirm, still a problem.

@midblue
Copy link

midblue commented Dec 30, 2020

Somehow this error doesn't occur locally but then on my Heroku server (running Nuxt) the Nuxt build fails with the same $green error. Can confirm that RGB( instead of rgb( does work, however.

@BenRacicot
Copy link

@midblue I cannot remember how I resolved the green issue but I did witness it in Angular 11. You could check that node-sass version.

See https://stackoverflow.com/a/11973298/1440240

Are you deploying SASS files or part of your CI or something?

@nhhockeyplayer
Copy link

this is a show stopper for me...
someone has to nail this finally its grown and endless threads on color parsing

@Karmalakas

This comment has been minimized.

@nschonni

This comment has been minimized.

@Prajapatisantu
Copy link

simple take rgb value and go to rgb to hex site, then simply convert and put it in your code problem solved!

@sass sass deleted a comment from Prajapatisantu Nov 27, 2021
@sass sass deleted a comment from AdrianoCahete Nov 27, 2021
@ghost
Copy link

ghost commented Jun 25, 2023

It's 2023, this has been a major issue for 5 years. Can this issue please have more attention so it can be fixed, instead of having to specify like 4 separate color variables just to add a little transparency to your website?

@QWERTAL124
Copy link

Which color --bs-bg-opacity var ? I couldn't find this variables value !

@cdwmhcc

This comment was marked as abuse.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests