Comment appliquer l'opacité à une variable de couleur CSS?

145

Je conçois une application en électron, j'ai donc accès aux variables CSS. J'ai défini une variable de couleur dans vars.css:

:root {
  --color: #f0f0f0;
}

Je souhaite utiliser cette couleur dans main.css, mais avec une certaine opacité appliquée:

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

Comment pourrais-je procéder? Je n'utilise aucun préprocesseur, seulement CSS. Je préférerais une réponse entièrement CSS, mais j'accepterai JavaScript / jQuery.

Je ne peux pas utiliser opacitycar j'utilise une image d'arrière-plan qui ne doit pas être transparente.

JoshyRobot
la source
On dirait donc que vous devriez utiliser plus d'un élément ...
epascarello
Je préférerais ne pas le faire, mais il semble que je
devrais
4
AHHHHH !!!!! C'est tellement ennuyeux! C'est presque 2020 maintenant. Le sélecteur de couleurs obtient les couleurs #hex. alpha / rgba ne fonctionne pas dans Sass / Stylus - car ce n'est pas une valeur rgb. Dois-je mettre 4 curseurs dans mon CMS pour chaque couleur?
sheriffderek

Réponses:

242

Vous ne pouvez pas prendre une valeur de couleur existante et lui appliquer un canal alpha. À savoir, vous ne pouvez pas prendre une valeur hexadécimale existante telle que #f0f0f0, lui donner un composant alpha et utiliser la valeur résultante avec une autre propriété.

Cependant, les propriétés personnalisées vous permettent de convertir votre valeur hexadécimale en un triplet RVB à utiliser avec rgba(), de stocker cette valeur dans la propriété personnalisée (y compris les virgules!), De remplacer cette valeur en utilisant var()dans une rgba()fonction avec la valeur alpha souhaitée, et cela il suffit de travailler:

: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>

Cela semble presque trop beau pour être vrai. 1 Comment ça marche?

La magie réside dans le fait que les valeurs des propriétés personnalisées sont substituées telles quelles lors du remplacement des var()références dans une valeur de propriété, avant que la valeur de cette propriété ne soit calculée. Cela signifie qu'en ce qui concerne les propriétés personnalisées, la valeur de --colordans votre exemple n'est pas du tout une valeur de couleur jusqu'à ce qu'une var(--color)expression apparaisse quelque part qui attend une valeur de couleur (et uniquement dans ce contexte). À partir de la section 2.1 de la spécification des variables css:

La syntaxe autorisée pour les propriétés personnalisées est extrêmement permissive. La production de la <declaration-value> correspond à n'importe quelle séquence d'un ou plusieurs jetons, tant que la séquence ne contient pas <bad-string-token>, <bad-url-token>, unmatched <) - token>, <] - token>, ou <} - token>, ou des jetons <semicolon-token> de niveau supérieur ou des jetons <delim-token> avec une valeur de "!".

Par exemple, ce qui suit est une propriété personnalisée valide:

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

Bien que cette valeur soit évidemment inutile en tant que variable, car elle ne serait pas valide dans n'importe quelle propriété normale, elle pourrait être lue et utilisée par JavaScript.

Et section 3 :

Si une propriété contient une ou plusieurs fonctions var () et que ces fonctions sont syntaxiquement valides, la grammaire de la propriété entière doit être considérée comme valide au moment de l'analyse. La syntaxe n'est vérifiée qu'au moment de la valeur calculée, après que les fonctions var () ont été remplacées.

Cela signifie que la 240, 240, 240valeur que vous voyez ci-dessus est directement substituée dans la rgba()fonction avant que la déclaration ne soit calculée. Donc ça:

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

qui ne semble pas être un CSS valide au début car rgba()n'attend pas moins de quatre valeurs numériques séparées par des virgules, devient ceci:

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

qui, bien sûr, est parfaitement valide CSS.

Pour aller plus loin, vous pouvez stocker le composant alpha dans sa propre propriété personnalisée:

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

et remplacez-le, avec le même résultat:

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

Cela vous permet d'avoir différentes valeurs alpha que vous pouvez échanger à la volée.


1 Eh bien, si vous exécutez l'extrait de code dans un navigateur qui ne prend pas en charge les propriétés personnalisées.

BoltClock
la source
12
This is beautiful
roberrrt-s
1
@Roberrrt: C'est quelque chose que j'aurais dû réaliser très tôt, en fait, vu que j'ai posté ces réponses précédemment.
BoltClock
2
Si nous allons cette var, pourquoi ne pas utiliser quelque chose comme: .element2 { background-color: rgba(var(--red), var(--low-opacity); }. De cette façon, vous pouvez utiliser pleinement l'utilisation des variables :).
roberrrt-s
7
Malheureusement, la valeur "240, 240, 240"n'est pas modifiable avec un sélecteur de couleur. C'est un énorme manque lorsque vous devez trouver les bonnes couleurs pour votre interface graphique.
GetFree
1
@ s3c La syntaxe var(--hex-color)99est convertie en deux jetons #333333 99(notez l'espace pour séparer les jetons) ce qui n'est évidemment pas ce que vous voulez. Les propriétés personnalisées ont été initialement définies pour copier des jetons, pas des chaînes et c'est le résultat final. Il est bien trop tard pour résoudre ce problème maintenant.
Mikko Rantalainen le
20

Je sais que l'OP n'utilise pas de préprocesseur, mais j'aurais été aidé si les informations suivantes faisaient partie de la réponse ici (je ne peux pas encore commenter, sinon j'aurais commenté la réponse @BoltClock.

Si vous utilisez, par exemple, scss, la réponse ci-dessus échouera, car scss tente de compiler les styles avec une fonction rgba () / hsla () spécifique à scss, qui nécessite 4 paramètres. Cependant, rgba () / hsla () sont également des fonctions css natives, vous pouvez donc utiliser l'interpolation de chaîne pour contourner la fonction scss.

Exemple (valide dans 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>

Notez que l'interpolation de chaîne ne fonctionnera pas pour les fonctions scss non-CSS, par exemple lighten(), parce que le code résultant ne serait pas un CSS fonctionnel. Ce serait toujours un scss valide, vous ne recevrez donc aucune erreur de compilation.

SimplyPhy
la source
4
Si vous préférez utiliser des fonctions de couleur CSS natives dans vos fichiers Sass .scss, vous pouvez inclure les définitions de fonction suivantes en haut de votre fichier pour remplacer la gestion de Sass et les faire passer: @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
rgbaest un synonyme de rgbdepuis un certain temps maintenant .. Vous êtes donc autorisé à supprimer le "a".
s3c
1
Une autre solution de contournement pour les fichiers scss consiste à utiliser des majuscules ( RGB) qui sont ensuite ignorées par sass. Par exemple: color: RGB(var(--color_rgb), 0.5);. De GitHub
Jono Job
Bonne réponse! Si vous avez déjà défini les couleurs en hexadécimal, vous pouvez simplement ajouter ce code pour le convertir en propriétés rgb personnalisées::root { @each $color, $value in $colors { --#{$color}_rgb: #{red($value), green($value), blue($value)}; } }
Plumpssack
9

J'étais dans une situation similaire, mais malheureusement , les solutions données ne fonctionne pas pour moi, car les variables pourraient être quelque chose de rgbà hslà hexou même des noms de couleur.
J'ai résolu ce problème maintenant, en appliquant le background-coloret le opacityà un pseudo :afterou un :beforeélément:

.container {
    position: relative;
}

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

Les styles devront peut-être être légèrement modifiés, en fonction de l'élément auquel l'arrière-plan doit être appliqué.
De plus, cela peut ne pas fonctionner pour toutes les situations, mais j'espère que cela aide dans certains cas, où les autres solutions ne peuvent pas être utilisées.

Edit: Je viens de remarquer que cette solution a évidemment également un impact sur la couleur du texte, car elle crée un élément devant l'élément cible et lui applique une couleur d'arrière-plan transparente.
Cela peut être un problème dans certains cas.

Springrbua
la source
Cela a non seulement l'avantage de permettre une spécification plus flexible de la couleur (par exemple, un nom, ou rgbou HSL) mais évite également tout conflit entre les fonctions de couleur CSS natives et les fonctions de couleur de Sass. Voir la réponse de SimplyPhy ci-dessous.
Jim Ratliff
1
Je pense qu'il est préférable d'utiliser :beforepour obtenir le bon ordre d'empilement sans jouer avec z-index.
Mikko Rantalainen le
5

C'est en effet possible avec CSS . C'est juste un peu sale et vous devrez utiliser des dégradés. J'ai codé un petit extrait de code à titre d'exemple, notez que pour les arrière-plans sombres, vous devez utiliser l'opacité noire, comme pour la lumière - les blancs .:

: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
la source
Vous n'avez pas besoin de spécifier la taille de l'arrière-plan - les dégradés n'ont pas de taille intrinsèque et s'étireront automatiquement en conséquence.
BoltClock
@BoltClock Ouais, j'ai littéralement pensé à ça quand je l'ai posté, c'était juste un peu de jouer dans le codepen;). Nettoyé maintenant, merci!
roberrrt-s
C'est malin, je n'avais pas pensé à superposer des dégradés de couleur unie lorsque j'ai répondu à une question similaire l'année dernière. Cette question est probablement plus générale de toute façon la façon dont elle a été écrite, celle à laquelle j'ai répondu était pour un cas d'utilisation très spécifique.
BoltClock
Cela ne fonctionne pas vraiment lorsque les arrière-plans sont différents, je suppose maintenant un fond blanc (255,255,255) lors de l'application de l'opacité. Il pourrait éventuellement être défini par défaut sur la couleur d'arrière-plan principale d'OP. Mais là encore, le fond blanc répondra probablement au besoin de la plupart des couleurs plus claires dans la mesure où les gens ne le remarqueront pas.
roberrrt-s
1
Je viens de découvrir quelque chose d'autre qui est assez incroyable. J'ai maintenant posté une réponse.
BoltClock
1
:root{
--color: 255, 0, 0;
}

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

où vous remplacez l'opacité par quelque chose entre 0 et 1

Seigneur de la pizza
la source
Est-ce une tentative de répondre à la question? Parce que si c'est le cas, le code n'a pas vraiment de sens. Particulièrement le rgba(var(--color), opacity)bit. Surtout que la valeur de votre propriété personnalisée est la notation rgb () entière. Mais aussi à cause du mot-clé "opacity".
BoltClock
woops my bad the rgb parts should not be in the var
Pizza Lord
1

SCSS / SASS

Avantage: vous pouvez simplement utiliser des valeurs de couleur hexadécimales, au lieu d'utiliser le 8 bits pour chaque canal (0-255).

C'est ainsi que je l'ai fait avec l'idée initiale de: https://codyhouse.co/blog/post/how-to-combine-sass-color-functions-and-css-variables

Edit: Vous pouvez également modifier la fonction alpha pour simplement utiliser #{$color-name}-rgbet omettre les variables CSS générées * -r, * -g, * -b.


Résultat

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 et fonctions

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

Espérons que cela fera gagner du temps à quelqu'un.

Stefan Rein
la source
1
J'aime cette approche. Merci
tonygatta
0

Vous pouvez définir une variable / valeur spécifique pour chaque couleur - l'original et celle avec l'opacité:

: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 vous ne pouvez pas utiliser ceci et que vous êtes d'accord avec la solution javascript, vous pouvez utiliser celle-ci:

$(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
la source
1
La valeur d'opacité changera, il serait donc ennuyeux de créer une variable pour chaque opacité.
JoshyRobot
-1

Pour utiliser rgba () avec la variable css générale, essayez ceci:

  1. Déclarez votre couleur à l'intérieur: root, mais n'utilisez pas rgb () comme le font les autres réponses. écris juste la valeur

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

  1. Utilisez la variable --color en utilisant var () comme autres réponses

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

Dani Fadli
la source
-3

En CSS, vous devriez pouvoir utiliser les valeurs rgba:

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

ou définissez simplement l'opacité:

#element {
  background: #f0f0f0;
  opacity: 0.5;    
}
Patrick H.
la source
1
Je ne parviens pas à coder en dur une valeur rgba, j'utilise des variables de couleur. J'aurais dû mentionner que je ne peux pas utiliser l'opacité parce que j'aurai une image d'arrière-plan qui ne devrait pas être transparente.
JoshyRobot
Ce n'est pas une solution b / c si vous voulez seulement que le BG ait de la transparence mais que l'élément complet ait une opacité, alors ajouter de l'opacité à tout n'est pas utile.
Levidps le
-4

Si vous aimez les couleurs hexadécimales comme moi, il existe une autre solution. La valeur hexadécimale est de 6 chiffres après la valeur alpha. 00 est une transparence de 100% 99 est d'environ 75% puis il utilise l'alphabet 'a1-af' puis 'b1-bf' se terminant par 'ff' qui est 100% opaque.

:root {
--color: #F00;
}

#element {
background: var(--color)f6;
}
Seth M
la source
1
Malheureusement, je ne pense pas que cela fonctionne. La prise en charge du code hexadécimal à 8 chiffres commence à se répandre, mais il ne semble pas que l'astuce utilisée avec la réponse acceptée fonctionne avec eux. Exemple: jsbin.com/nacuharige/edit?css,output
JoshyRobot
Cela ne fonctionne pas, même si ce serait une excellente solution si c'était le cas.
Braden Rockwell Napier