ChatGPT解决这个技术问题 Extra ChatGPT

How do I apply opacity to a CSS color variable?

I am designing an app in electron, so I have access to CSS variables. I have defined a color variable in vars.css:

:root {
  --color: #f0f0f0;
}

I want to use this color in main.css, but with some opacity applied:

#element {
  background: (somehow use var(--color) at some opacity);
}

How would I go about doing this? I am not using any preprocessor, only CSS. I would prefer an all-CSS answer, but I will accept JavaScript/jQuery.

I cannot use opacity because I am using a background image that should not be transparent.

So sounds like you should be using more than one element....
I would prefer not to, but it seems I might have to... :(
AHHHHH!!!!! This is so annoying! It's almost 2020 now. Color picker gets #hex colors. alpha / rgba doesn't work in Sass/Stylus - because it's not a rgb value. Should I put 4 sliders in my CMS for every single color?

C
Community

You can't take an existing color value and apply an alpha channel to it. Namely, you can't take an existing hex value such as #f0f0f0, give it an alpha component and use the resulting value with another property.

However, custom properties allow you to convert your hex value into an RGB triplet for use with rgba(), store that value in the custom property (including the commas!), substitute that value using var() into an rgba() function with your desired alpha value, and it'll just work:

:root { /* #f0f0f0 in decimal RGB */ --color: 240, 240, 240; } body { color: #000; background-color: #000; } #element { background-color: rgba(var(--color), 0.8); }

If you can see this, your browser supports custom properties.

This seems almost too good to be true.1 How does it work?

The magic lies in the fact that the values of custom properties are substituted as is when replacing var() references in a property value, before that property's value is computed. This means that as far as custom properties are concerned, the value of --color in your example isn't a color value at all until a var(--color) expression appears somewhere that expects a color value (and only in that context). From section 2.1 of the css-variables spec:

The allowed syntax for custom properties is extremely permissive. The production matches any sequence of one or more tokens, so long as the sequence does not contain , , unmatched <)-token>, <]-token>, or <}-token>, or top-level tokens or tokens with a value of "!".

For example, the following is a valid custom property: --foo: if(x > 5) this.width = 10; While this value is obviously useless as a variable, as it would be invalid in any normal property, it might be read and acted on by JavaScript.

And section 3:

If a property contains one or more var() functions, and those functions are syntactically valid, the entire property’s grammar must be assumed to be valid at parse time. It is only syntax-checked at computed-value time, after var() functions have been substituted.

This means that the 240, 240, 240 value you see above gets substituted directly into the rgba() function before the declaration is computed. So this:

#element {
  background-color: rgba(var(--color), 0.8);
}

which doesn't appear to be valid CSS at first because rgba() expects no less than four comma-separated numeric values, becomes this:

#element {
  background-color: rgba(240, 240, 240, 0.8);
}

which, of course, is perfectly valid CSS.

Taking it one step further, you can store the alpha component in its own custom property:

:root {
  --color: 240, 240, 240;
  --alpha: 0.8;
}

and substitute it, with the same result:

#element {
  background-color: rgba(var(--color), var(--alpha));
}

This allows you to have different alpha values that you can swap around on-the-fly.

1 Well, it is, if you're running the code snippet in a browser that doesn't support custom properties.


This is beautiful
Unfortunately, the value "240, 240, 240" is not editable with a color picker. That is a huge miss when you need to find the right colors for your GUI.
rgba() is nowadays a synonym for rgb() so you can use color: rgb(var(--color), .4). But the reason I found this SOQ is because the following doesn't work: :root { --hex-color: "#333333"; } element { background: var(--hex-color)99 /*this should be alpha 60%*/ }, but somehow the two strings are not joined into #33333399 which would work.
@s3c The syntax var(--hex-color)99 is converted to two tokens #333333 99 (notice the space to separate tokens) which obviously is not the thing you want. Custom properties were originally defined to copy tokens, not strings and this is the end result. It's way too late to fix this now.
@s3c: The good news is that CSS Color 5 is introducing some cool new functions to manipulate existing hex values, including changing their alpha channel: drafts.csswg.org/css-color-5/#colormodify
S
SimplyPhy

I know the OP isn't using a preprocessor, but I would have been helped if the following information was part of the answer here (I can't comment yet, otherwise I would have commented @BoltClock answer.

If you are using, e.g. scss, the answer above will fail, because scss attempts to compile the styles with a scss-specific rgba()/hsla() function, which requires 4 parameters. However, rgba()/hsla() are also native css functions, so you can use string interpolation to bypass the scss function.

Example (valid in sass 3.5.0+):

:root { --color_rgb: 250, 250, 250; --color_hsl: 250, 50%, 50%; } div { /* This is valid CSS, but will fail in a scss compilation */ background-color: rgba(var(--color_rgb), 0.5); /* This is valid scss, and will generate the CSS above */ background-color: #{'rgba(var(--color_rgb), 0.5)'}; }

Note that string interpolation will not work for non-CSS scss functions, such as lighten(), because the resulting code would not be functional CSS. It would still be valid scss though, so you would receive no error in compilation.


If you prefer to use native CSS color functions in your Sass .scss files, you can include the following function definitions at the top of your file to override Sass' handling and make them pass through: @function rgb($args...) { @return #{'rgb(#{$args})'}; } @function rgba($args...) { @return #{'rgba(#{$args})'}; } @function hsl($args...) { @return #{'hsl(#{$args})'}; } @function hsla($args...) { @return #{'hsla(#{$args})'}; } ````
rgba is a synonym for rgb for quite some time now.. You are hence allowed to drop the "a".
Another workaround for scss files is to use uppercase (RGB) which is then ignored by sass. Eg: color: RGB(var(--color_rgb), 0.5);. From GitHub
Nice answer! If you have already defined the colors in hex, you can simply add this code to convert it to the custom rgb properties: :root { @each $color, $value in $colors { --#{$color}_rgb: #{red($value), green($value), blue($value)}; } }
Using the latest sass package on npm, this workaround doesn't appear necessary anymore
v
vsync

I was in a similar situation, but unfortunately the given solutions did not work for me, as the variables could be anything from rgb to hsl to hex or even color names.
I solved this issue now, by applying the background-color and the opacity to a pseudo :after or :before element:

.container {
    position: relative;
}

.container::before {
    content: "";
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    background-color: var(--color);
    opacity: 0.3;
}

The styles might need to be changed a little, depending on the element the background should be applied to. Also it might not work for all situations, but hopefully it helps in some cases, where the other solutions can't be used.

Edit: I just noticed, that this solution obviously also impacts the text color, as it creates an element in front of the target element and applies a transparent background color to it. This might be a problem in some cases.


This not only has the advantage of allowing more flexible specification of the color (e.g., a name, or rgb or HSL) but also avoids any conflict between native CSS color functions and Sass's color functions. See SimplyPhy's answer below.
I think it's better to use :before so you get the right stacking order without playing with z-index.
v
vsync

Relative color syntax

With this new CSS ability (css-color-5) which allows color format transformations, it also will also allow adding opacity to any color in any format, for example, to RGB (relative transformations can be done to any other format):

html { --color: blue }
.with-opacity { background: rgb(from var(--color) r g b / 50%) }

(As of writing, not yet available in browsers. Will update once arrvies)


S
Stefan Rein

SCSS / SASS

Advantage: You can just use Hex color values, instead to use the 8 Bit for every channel (0-255).

This is how I did it with the initial idea of: https://codyhouse.co/blog/post/how-to-combine-sass-color-functions-and-css-variables

Edit: You could also modify the alpha function to just use #{$color-name}-rgb and omit the generated *-r, *-g, *-b CSS variables.

Result

body {
  --main-color: rgb(170, 68, 204);
  --main-color-rgb: 170,68,204;
  --main-color-r: 170;
  --main-color-g: 68;
  --main-color-b: 204;
}

.button-test {
  // Generated from the alpha function
  color: rgba(var(--main-color-r), var(--main-color-g), var(--main-color-b), 0.5);
  // OR (you wrote this yourself, see usage)
  color: rgba(var(--main-color-rgb), 0.5);
}

Usage:

body {
    @include defineColorRGB(--main-color, #aa44cc);
}

.button-test {
  // With alpha function:
  color: alpha(var(--main-color), 0.5);
  // OR just using the generated variable directly
  color: rgba(var(--main-color-rgb), 0.5);
}

Mixin and functions

@mixin defineColorRGB($color-name, $value) {
    $red: red($value);
    $green: green($value);
    $blue: blue($value);
    #{$color-name}: unquote("rgb(#{$red}, #{$green}, #{$blue})");
    #{$color-name}-rgb: $red,$green,$blue;
    #{$color-name}-r: $red;
    #{$color-name}-g: $green;
    #{$color-name}-b: $blue;
}

// 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;
}

@function alpha($color, $opacity) {
    $color: str-replace($color, 'var(');
    $color: str-replace($color, ')');
    $color-r: var(#{$color+'-r'});
    $color-g: var(#{$color+'-g'});
    $color-b: var(#{$color+'-b'});
    @return rgba($color-r, $color-g, $color-b, $opacity);
}

Hopefully this will save someone some time.


r
roberrrt-s

This is indeed possible with CSS. It's just a bit dirty, and you'll have to use gradients. I've coded a small snippet as example, take note that for dark backgrounds, you should use the black opacity, as for light- the white ones.:

:root { --red: rgba(255, 0, 0, 1); --white-low-opacity: rgba(255, 255, 255, .3); --white-high-opacity: rgba(255, 255, 255, .7); --black-low-opacity: rgba(0, 0, 0, .3); --black-high-opacity: rgba(0, 0, 0, .7); } div { width: 100px; height: 100px; margin: 10px; } .element1 { background: linear-gradient(var(--white-low-opacity), var(--white-low-opacity)) no-repeat, linear-gradient(var(--red), var(--red)) no-repeat; } .element2 { background: linear-gradient(var(--white-high-opacity), var(--white-high-opacity)) no-repeat, linear-gradient(var(--red), var(--red)) no-repeat; } .element3 { background: linear-gradient(var(--black-low-opacity), var(--black-low-opacity)) no-repeat, linear-gradient(var(--red), var(--red)) no-repeat; } .element4 { background: linear-gradient(var(--black-high-opacity), var(--black-high-opacity)) no-repeat, linear-gradient(var(--red), var(--red)) no-repeat; }

hello world
hello world
hello world
hello world


You do not need to specify background-size - gradients have no intrinsic size and will automatically stretch as a result.
@BoltClock Yeah, I literally thought of that when I posted it, it was just a bit of playing around in the codepen ;). Cleaned up now, thanks!
This is clever, I had not thought of layering solid-color gradients over one another when I answered a similar question last year. This question is probably more general anyway the way it was written, the one I answered was for a very specific use case.
It doesn't really work when the backgrounds are different though, I now assume a white background (255,255,255) when applying the 'opacity'. It could possibly be defaulted to OP's main background color. But then again, white background will probably fit the need of most lighter colors to the extend that people will not notice this.
I just discovered something else that's pretty incredible. I've now posted an answer.
A
Ahmet Tuna POTUR

If you use dark and light mode, i use this sample. I prefer separate between colors and rgb colors variable assignment. So i use two each loop. I realise this solution is not dry code. If you want to dry code could you use one loop.

$colors-light: ( white: #fff, black: #0c0d0e, orange: #f48024, green: #5eba7d, blue: #0077cc, red: #d1383d, red-100: #e2474c, red-200: red, ); $colors-dark: ( black: #fff, white: #2d2d2d, orange: #dd7118, green: #5eba7d, blue: #0077cc, red: #aa1c21, red-100: #c9292e, red-200: red, ); @function hexToRGB($hex) { @return red($hex), green($hex), blue($hex); } @mixin generate_colors($colors) { // Colors @each $color, $value in $colors { @if str-slice(#{$value}, 1, 1) == "#" { --#{$color}: #{$value}; } @else { --#{$color}: var(--#{$value}); } } // RGB Colors @each $color, $value in $colors { @if str-slice(#{$value}, 1, 1) == "#" { --RGB_#{$color}: #{hexToRGB($value)}; } @else { --RGB_#{$color}: var(--RGB_#{$value}); } } } :root { @include generate_colors($colors-light); } [data-theme="dark"] { @include generate_colors($colors-dark); }

dry code

@mixin generate_colors($colors) { // Colors, RGB Colors @each $color, $value in $colors { @if str-slice(#{$value}, 1, 1) == "#" { --#{$color}: #{$value}; --RGB_#{$color}: #{hexToRGB($value)}; } @else { --#{$color}: var(--#{$value}); --RGB_#{$color}: var(--RGB_#{$value}); } } }

css Output

:root { --white: #fff; --RGB_white: 255, 255, 255; --black: #0c0d0e; --RGB_black: 12, 13, 14; --orange: #f48024; --RGB_orange: 244, 128, 36; --green: #5eba7d; --RGB_green: 94, 186, 125; --blue: #0077cc; --RGB_blue: 0, 119, 204; --red: #d1383d; --RGB_red: 209, 56, 61; --red-100: #e2474c; --RGB_red-100: 226, 71, 76; --red-200: var(--red); --RGB_red-200: var(--RGB_red); } [data-theme="dark"] { --black: #fff; --RGB_black: 255, 255, 255; --white: #2d2d2d; --RGB_white: 45, 45, 45; --orange: #dd7118; --RGB_orange: 221, 113, 24; --green: #5eba7d; --RGB_green: 94, 186, 125; --blue: #0077cc; --RGB_blue: 0, 119, 204; --red: #aa1c21; --RGB_red: 170, 28, 33; --red-100: #c9292e; --RGB_red-100: 201, 41, 46; --red-200: var(--red); --RGB_red-200: var(--RGB_red); } body { background-color: var(--white); } .colors { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; -ms-flex-wrap: wrap; flex-wrap: wrap; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; -webkit-box-align: center; -ms-flex-align: center; align-items: center; margin: 50px 0 0 30px; } .box { width: 100px; height: 100px; margin-right: 5px; } .black { background-color: var(--black); } .white { background-color: var(--white); } .orange { background-color: var(--orange); } .green { background-color: var(--green); } .blue { background-color: var(--blue); } .red { background-color: var(--red); } .red-200 { background-color: var(--red-200); } .black-rgba { background-color: rgba(var(--RGB_black), 0.5); } .white-rgba { background-color: rgba(var(--RGB_white), 0.5); } .orange-rgba { background-color: rgba(var(--RGB_orange), 0.5); } .green-rgba { background-color: rgba(var(--RGB_green), 0.5); } .blue-rgba { background-color: rgba(var(--RGB_blue), 0.5); } .red-rgba { background-color: rgba(var(--RGB_red), 0.5); } .red-rgba-200 { background-color: rgba(var(--RGB_red-200), 0.5); } Document


RGBA


This was an incredible post! Thanks for all this code. I basically copied it all into my own project. For anyone coming here who does have issues, I did have to change one thing. When initially declaring the SASS color variables, you need to wrap quotes around the names like so: $colors-light: ( 'color-primary': #2F302F, 'color-primary-variant': #4E4E4E,
Unfortuntely, this isn't pure CSS as requested.
u
user2053153

you can use linear-gradient to hack the color:

background: linear-gradient(to bottom, var(--your-color) -1000%, var(--mixin-color), 1000%)

$(() => { const setOpacity = () => { $('#canvas').css('--opacity', $('#opacity-value').val()) } const setColor = () => { $('#canvas').css('--color', $('#color-value').val()); } $('#opacity-value').on('input', setOpacity); $('#color-value').on('input', setColor); setOpacity(); setColor(); }) #canvas { width: 100px; height: 100px; border: 2px solid #000; --hack: 10000%; background: linear-gradient( to bottom, var(--color) calc((var(--opacity) - 1) * var(--hack)), transparent calc(var(--opacity) * var(--hack))); } #container { background-image: linear-gradient(45deg, #b0b0b0 25%, transparent 25%), linear-gradient(-45deg, #b0b0b0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #b0b0b0 75%), linear-gradient(-45deg, transparent 75%, #b0b0b0 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px; padding: 10px; display: inline-block; }



P
Pizza lord
:root{
--color: 255, 0, 0;
}

#element{
    background-color: rgba(var(--color), opacity);
}

where you replace opacity with anything between 0 and 1


Is this an attempt at answering the question? Because if so, the code doesn't really make sense. Particularly the rgba(var(--color), opacity) bit. Especially since your custom property value is the entire rgb() notation. But also because of the "opacity" keyword.
woops my bad the rgb parts should not be in the var
D
Dekel

You can set specific variable/value for each color - the original and the one with opacity:

:root { --color: #F00; --color-opacity: rgba(255, 0, 0, 0.5); } #a1 { background: var(--color); } #a2 { background: var(--color-opacity); }

asdf
asdf

If you can't use this and you are ok with javascript solution, you can use this one:

$(function() { $('button').click(function() { bgcolor = $('#a2').css('backgroundColor'); rgb_value = bgcolor.match(/\d+,\s?\d+,\s?\d+/)[0] $('#a2').css('backgroundColor', 'rgba(' + rgb_value + ', 0.5)'); }); }); :root { --color: #F00; } #a1 { background: var(--color); } #a2 { background: var(--color); }

asdf
asdf


The opacity value will change, so it would be annoying to create a variable for every opacity.
S
Seth M

If you love hex colors like me there is another solution. The hex value is 6 digits after that is the alpha value. 00 is 100% transparency 99 is about 75% then it uses the alphabet 'a1-af' then 'b1-bf' ending with 'ff' which is 100% opaque.

:root {
--color: #F00;
}

#element {
background: var(--color)f6;
}

Unfortunately, I don't think this works. 8 digit hex code support is starting to spread, but it doesn't look like the trick used with the accepted answer works with them. Example: jsbin.com/nacuharige/edit?css,output
This does not work, although it would be a great solution if it did.
As of my posting, this is now available in nearly 94% of currently used browsers [caniuse.com/css-rrggbbaa]. I've gone down this route as all my colour variables were already saved as HEX values, so adding the extra 2 characters to indicate alpha was the perfect solution.
@Rillus, can you provide working example? Seems like this construction is not supported.
I now realise my implementation doesn't match up with what this poster used and that CSS variable concatenation i.e. var(--color)f6 doesn't work (the value produced in this case would be #f0f0f0 f6, which is invalid). My successfully working result was using Vue3 and referring to a colour property imported by the js e.g: ` background-color: v-bind(this.colourHex + 'f6');` Apologies for the confusion.
D
Dani Fadli

For using rgba() with general css variable, try this:

Declare your color inside :root, but don't use rgb() as other answers do. just write the value

:root{ --color : 255,0,0; }

Use --color variable using var() as other answers

#some-element { color : rgba(var(--color),0.5); }


P
Patrick H.

In CSS you should be able to either use rgba values:

#element {
  background: rgba(240, 240, 240, 0.5);
}

or just set the opacity:

#element {
  background: #f0f0f0;
  opacity: 0.5;    
}

I am unable to hardcode an rgba value, I am using color variables. I should have mentioned I cannot use opacity because I will have a background image that should not be transparent.
This isn't a solution b/c if you only want the BG to have transparency but the full element to have opacity then adding opacity to everything isn't helpful.