¿Cómo aplico opacidad a una variable de color CSS?

145

Estoy diseñando una aplicación en electrón, así que tengo acceso a las variables CSS. He definido una variable de color en vars.css:

:root {
  --color: #f0f0f0;
}

Quiero usar este color main.css, pero con algo de opacidad aplicada:

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

¿Cómo haría para hacer esto? No estoy usando ningún preprocesador, solo CSS. Preferiría una respuesta totalmente CSS, pero aceptaré JavaScript / jQuery.

No puedo usar opacityporque estoy usando una imagen de fondo que no debería ser transparente.

JoshyRobot
fuente
Así suena como usted debe utilizar más de un elemento ....
epascarello
Preferiría no hacerlo, pero parece que podría tener que ... :(
JoshyRobot
44
AHHHHH !!!!! ¡Esto es muy molesto! Es casi 2020 ahora. El selector de color obtiene #hex colores. alpha / rgba no funciona en Sass / Stylus, porque no es un valor rgb. ¿Debo poner 4 controles deslizantes en mi CMS para cada color?
Sheriffderek

Respuestas:

242

No puede tomar un valor de color existente y aplicarle un canal alfa. Es decir, no puede tomar un valor hexadecimal existente como#f0f0f0 darle un componente alfa y usar el valor resultante con otra propiedad.

Sin embargo, las propiedades personalizadas le permiten convertir su valor hexadecimal en un triplete RGB para usarlo rgba(), almacenar ese valor en la propiedad personalizada (¡incluidas las comas!), Sustituir ese valor usando var()una rgba()función con su valor alfa deseado, y solo trabajo:

:root {
  /* #f0f0f0 in decimal RGB */
  --color: 240, 240, 240;
}

body {
  color: #000;
  background-color: #000;
}

#element {
  background-color: rgba(var(--color), 0.8);
}
<p id="element">If you can see this, your browser supports custom properties.</p>

Esto parece casi demasiado bueno para ser verdad. 1 ¿Cómo funciona?

La magia radica en el hecho de que los valores de las propiedades personalizadas se sustituyen tal como están al reemplazar las var()referencias en un valor de propiedad, antes de que se calcule el valor de esa propiedad. Esto significa que, en lo que respecta a las propiedades personalizadas, el valor de --coloren su ejemplo no es un valor de color en absoluto hastavar(--color) que aparezca una expresión en algún lugar que espera un valor de color (y solo en ese contexto). De la sección 2.1 de la especificación de variables css:

La sintaxis permitida para las propiedades personalizadas es extremadamente permisiva. La producción <declaration-value> coincide con cualquier secuencia de uno o más tokens, siempre que la secuencia no contenga <bad-string-token>, <bad-url-token>, unmatched <) - token>, <] - token>, o <} - token>, o tokens <semicolon-token> de nivel superior o tokens <delim-token> con un valor de "!".

Por ejemplo, la siguiente es una propiedad personalizada válida:

--foo: if(x > 5) this.width = 10;

Si bien este valor es obviamente inútil como variable, ya que sería inválido en cualquier propiedad normal, JavaScript podría leerlo y actuar sobre él.

Y sección 3 :

Si una propiedad contiene una o más funciones var (), y esas funciones son sintácticamente válidas, se debe suponer que toda la gramática de la propiedad es válida en el momento del análisis. Solo se verifica la sintaxis en el tiempo de valor calculado, después de que se hayan sustituido las funciones var ().

Esto significa que el 240, 240, 240valor que ve arriba se sustituye directamente en la rgba()función antes de que se calcule la declaración. Así que esto:

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

que no parece ser CSS válido al principio porque rgba()espera no menos de cuatro valores numéricos separados por comas, se convierte en esto:

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

que, por supuesto, es CSS perfectamente válido.

Dando un paso más, puede almacenar el componente alfa en su propia propiedad personalizada:

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

y sustitúyalo, con el mismo resultado:

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

Esto le permite tener diferentes valores alfa que puede intercambiar sobre la marcha.


1 Bueno, lo es, si está ejecutando el fragmento de código en un navegador que no admite propiedades personalizadas.

BoltClock
fuente
12
Esto es hermoso
roberrrt-s
1
@Roberrrt: Es algo que debería haberme dado cuenta al principio, de hecho, ya que publiqué estas respuestas anteriormente.
BoltClock
2
Si vamos esta var, por qué no usar algo como: .element2 { background-color: rgba(var(--red), var(--low-opacity); }. De esta manera, puede utilizar completamente el uso de variables :).
roberrrt-s
77
Desafortunadamente, el valor "240, 240, 240"no es editable con un selector de color. Esa es una gran falta cuando necesitas encontrar los colores correctos para tu GUI.
GetFree
1
@ s3c La sintaxis var(--hex-color)99se convierte en dos tokens #333333 99(observe el espacio para separar los tokens) que obviamente no es lo que desea. Las propiedades personalizadas se definieron originalmente para copiar tokens, no cadenas, y este es el resultado final. Es demasiado tarde para arreglar esto ahora.
Mikko Rantalainen
20

Sé que el OP no está utilizando un preprocesador, pero me habría ayudado si la siguiente información fuera parte de la respuesta aquí (no puedo comentar todavía, de lo contrario habría comentado la respuesta de @BoltClock.

Si está utilizando, por ejemplo, scss, la respuesta anterior fallará, porque scss intenta compilar los estilos con una función rgba () / hsla () específica de scss, que requiere 4 parámetros. Sin embargo, rgba () / hsla () también son funciones css nativas, por lo que puede usar la interpolación de cadenas para omitir la función scss.

Ejemplo (válido en 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)'};
}
<div></div>

Tenga en cuenta que la interpolación de cadenas no funcionará para funciones scss que no sean CSS, como lighten()porque el código resultante no sería CSS funcional. Sin embargo, seguiría siendo válido scss, por lo que no recibiría ningún error en la compilación.

SimplyPhy
fuente
44
Si prefiere usar funciones de color CSS nativas en sus archivos .scss de Sass, puede incluir las siguientes definiciones de funciones en la parte superior de su archivo para anular el manejo de Sass y hacer que pasen: @function rgb($args...) { @return #{'rgb(#{$args})'}; } @function rgba($args...) { @return #{'rgba(#{$args})'}; } @function hsl($args...) { @return #{'hsl(#{$args})'}; } @function hsla($args...) { @return #{'hsla(#{$args})'}; }`` ``
lunelson
rgbaes sinónimo desde rgbhace bastante tiempo. Por lo tanto, se le permite soltar la "a".
s3c
1
Otra solución para los archivos scss es usar mayúsculas ( RGB) que luego sass ignora. Por ejemplo: color: RGB(var(--color_rgb), 0.5);. De GitHub
Jono Job
¡Buena respuesta! Si ya ha definido los colores en hexadecimal, simplemente puede agregar este código para convertirlo a las propiedades rgb personalizadas::root { @each $color, $value in $colors { --#{$color}_rgb: #{red($value), green($value), blue($value)}; } }
Plumpssack
9

Yo estaba en una situación similar, pero por desgracia las soluciones dadas no funcionó para mí, ya que las variables podrían ser cualquier cosa de rgba hsla hexo incluso nombres de colores.
Resolví este problema ahora, aplicando el background-colory el opacitya un pseudo :aftero :beforeelemento:

.container {
    position: relative;
}

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

Es posible que sea necesario cambiar un poco los estilos, según el elemento al que se debe aplicar el fondo.
También podría no funcionar para todas las situaciones, pero es de esperar que ayude en algunos casos, donde las otras soluciones no se pueden usar.

Editar: Acabo de notar que esta solución obviamente también afecta el color del texto, ya que crea un elemento delante del elemento de destino y le aplica un color de fondo transparente.
Esto podría ser un problema en algunos casos.

Springrbua
fuente
Esto no solo tiene la ventaja de permitir una especificación más flexible del color (por ejemplo, un nombre, rgbo HSL), sino que también evita cualquier conflicto entre las funciones de color CSS nativas y las funciones de color de Sass. Vea la respuesta de SimplyPhy a continuación.
Jim Ratliff
1
Creo que es mejor usarlo :beforepara obtener el orden correcto de apilamiento sin jugar z-index.
Mikko Rantalainen
5

De hecho, esto es posible con CSS . Está un poco sucio y tendrás que usar gradientes. He codificado un pequeño fragmento como ejemplo, tenga en cuenta que para fondos oscuros, debe usar la opacidad negra, como para los claros:

: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;
}
<div class="element1">hello world</div>
<div class="element2">hello world</div>
<div class="element3">hello world</div>
<div class="element4">hello world</div>

roberrrt-s
fuente
No necesita especificar el tamaño del fondo: los gradientes no tienen un tamaño intrínseco y, como resultado, se estirarán automáticamente.
BoltClock
@BoltClock Sí, literalmente pensé en eso cuando lo publiqué, fue solo un poco jugando en el codepen;). Limpiado ahora, gracias!
roberrrt-s
Esto es inteligente, no había pensado en superponer gradientes de color sólido entre sí cuando respondí una pregunta similar el año pasado. Esta pregunta es probablemente más general de todos modos, la forma en que fue escrita, la que respondí fue para un caso de uso muy específico.
BoltClock
Sin embargo, realmente no funciona cuando los fondos son diferentes, ahora asumo un fondo blanco (255,255,255) al aplicar la 'opacidad'. Posiblemente podría estar predeterminado en el color de fondo principal de OP. Pero, de nuevo, el fondo blanco probablemente se ajuste a la necesidad de la mayoría de los colores más claros hasta el punto de que las personas no lo noten.
roberrrt-s
1
Acabo de descubrir algo más que es bastante increíble. Ahora he publicado una respuesta.
BoltClock
1
:root{
--color: 255, 0, 0;
}

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

donde reemplazas la opacidad con cualquier cosa entre 0 y 1

Señor de la pizza
fuente
¿Es este un intento de responder la pregunta? Porque si es así, el código realmente no tiene sentido. Particularmente el rgba(var(--color), opacity)bit. Especialmente porque el valor de su propiedad personalizada es la notación rgb () completa. Pero también por la palabra clave "opacidad".
BoltClock
woops my bad las partes rgb no deberían estar en la var
Pizza lord
1

SCSS / SASS

Ventaja: puede usar valores de color Hex, en su lugar, usar los 8 bits para cada canal (0-255).

Así es como lo hice con la idea inicial de: https://codyhouse.co/blog/post/how-to-combine-sass-color-functions-and-css-variables

Editar: También puede modificar la función alfa para usar #{$color-name}-rgby omitir las variables CSS * -r, * -g, * -b generadas.


Resultado

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

Uso:

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 y funciones

@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);
}

Esperemos que esto ahorre algo de tiempo a alguien.

Stefan Rein
fuente
1
Me gusta este enfoque. Gracias
tonygatta
0

Puede establecer una variable / valor específico para cada color: el original y el que tiene opacidad:

:root {
  --color: #F00;
  --color-opacity: rgba(255, 0, 0, 0.5);
}
#a1 {
  background: var(--color);
} 
#a2 {
  background: var(--color-opacity);
}
<div id="a1">asdf</div>
<div id="a2">asdf</div>

Si no puede usar esto y está de acuerdo con la solución de JavaScript, puede usar esta:

$(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);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="a1">asdf</div>
<div id="a2">asdf</div>
<button>Click to change opacity</button>

Dekel
fuente
1
El valor de opacidad cambiará, por lo que sería molesto crear una variable para cada opacidad.
JoshyRobot
-1

Para usar rgba () con la variable css general, intente esto:

  1. Declare su color en el interior: root, pero no use rgb () como lo hacen otras respuestas. solo escribe el valor

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

  1. Use la variable --color usando var () como otras respuestas

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

Dani Fadli
fuente
-3

En CSS deberías poder usar los valores rgba:

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

o simplemente establece la opacidad:

#element {
  background: #f0f0f0;
  opacity: 0.5;    
}
Patrick H.
fuente
1
No puedo codificar un valor rgba, estoy usando variables de color. Debería haber mencionado que no puedo usar la opacidad porque tendré una imagen de fondo que no debería ser transparente.
JoshyRobot
Esta no es una solución b / c si solo desea que el BG tenga transparencia pero el elemento completo tenga opacidad y luego agregar opacidad a todo no es útil.
Levidps
-4

Si amas los colores hexadecimales como yo, hay otra solución. El valor hexadecimal es de 6 dígitos después de eso es el valor alfa. 00 es 100% de transparencia 99 es aproximadamente 75%, luego usa el alfabeto 'a1-af' y luego 'b1-bf' que termina con 'ff' que es 100% opaco.

:root {
--color: #F00;
}

#element {
background: var(--color)f6;
}
Seth M
fuente
1
Desafortunadamente, no creo que esto funcione. El soporte de código hexadecimal de 8 dígitos está comenzando a extenderse, pero no parece que el truco utilizado con la respuesta aceptada funcione con ellos. Ejemplo: jsbin.com/nacuharige/edit?css,output
JoshyRobot
Esto no funciona, aunque sería una gran solución si lo hiciera.
Braden Rockwell Napier