Saltar al contenido

currentColor y el icono que se adapta

Cómo funciona currentColor por dentro, por qué deja de funcionar en un SVG servido como imagen, y el patrón completo de un icono que hereda el color del texto.

Qué es exactamente

currentColor no es un color: es una referencia al valor computado de la propiedad color del mismo elemento. Cuando escribes fill: currentColor en un path, el valor usado de fill es lo que valga color en ese path. Y como color se hereda, lo que valga en su ancestro más cercano que la declare.

El detalle que hace que todo encaje: en SVG, color no pinta nada por sí sola. No hay texto que colorear en un path. Su único papel es servir de canal de transporte, y por eso es la propiedad perfecta para el trabajo: nadie más la está usando.

HTML
<button class="boton">  <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">    <path fill="currentColor" d="M12 2 15 9l7 .6-5.3 4.6 1.6 6.8L12 17.3 5.7 21l1.6-6.8L2 9.6 9 9z" />  </svg>  Destacar</button>
CSS
.boton { color: #cdd6f4; }.boton:hover { color: #f9e2af; }

El icono cambia de color en el hover sin que ninguna regla lo mencione. Es exactamente lo mismo que hace el texto, y por la misma vía.

Un matiz sobre el orden: currentColor se resuelve en el valor computado, no en el usado. Eso significa que si color cambia por una transición, fill transiciona con él sin declararlo, porque el navegador interpola el color computado. Es una de las pocas cosas que salen gratis.

Dónde funciona y dónde no

Hay tres maneras de meter un SVG en una página y currentColor solo funciona en dos.

SVG en línea. El marcado forma parte del documento, los nodos son nodos del DOM, la herencia funciona igual que con cualquier elemento HTML. Funciona.

use referenciando un símbolo del mismo documento. El contenido se clona en un árbol de sombra que hereda del elemento use, y color cruza la frontera porque la herencia cruza los árboles de sombra. Funciona.

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true">  <symbol id="i-estrella" viewBox="0 0 24 24">    <path fill="currentColor" d="M12 2 15 9l7 .6-5.3 4.6 1.6 6.8L12 17.3 5.7 21l1.6-6.8L2 9.6 9 9z" />  </symbol></svg><span style="color:#f38ba8">  <svg width="24" height="24" aria-hidden="true"><use href="#i-estrella" /></svg></span>

img, background-image o un object. El SVG se carga como un documento independiente. Tiene su propio árbol, su propia cascada y su propia raíz. No hay ninguna relación de herencia con la página, así que color en el documento anfitrión no llega. currentColor se resuelve contra el valor inicial de color dentro del SVG, que es el negro por omisión, y el icono sale negro pase lo que pase. No funciona.

Esa frontera no es un bug ni una limitación caprichosa: es la misma que impide que la página lea el contenido del SVG, que el SVG ejecute scripts en ese modo, y que cargue recursos externos. Es una frontera de seguridad, y viene con todas las consecuencias.

Un icono de dos tonos

currentColor da un canal. Cuando hacen falta dos, la combinación con custom properties da el resto, y es el patrón que usan las bibliotecas de iconos duotono:

HTML
<svg class="icono-duo" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">  <path class="fondo" d="M4 4h16v16H4z" />  <path class="frente" d="M8 12h8M12 8v8" /></svg>
CSS
.icono-duo .fondo  { fill: currentColor; opacity: .25; }.icono-duo .frente { fill: none; stroke: currentColor; stroke-width: 2; }/* Un segundo tono independiente cuando hace falta */.icono-duo { --acento: #f9e2af; }.icono-duo.acentuado .frente { stroke: var(--acento); }

La capa de fondo usa el mismo color con opacidad reducida, así que se adapta automáticamente a cualquier contexto. La de delante puede desviarse cuando se le pide. Y el icono sigue siendo un fichero neutro que funciona en cualquier tema.

Una advertencia sobre la opacidad como segundo tono: sobre un fondo oscuro, un currentColor claro al 25 por ciento da un gris azulado; sobre un fondo claro, el mismo cálculo da otro gris. Si tu diseño exige un valor exacto, la opacidad no sirve y necesitas una segunda custom property. Lo que la opacidad garantiza es la coherencia relativa, no el valor absoluto.

La regla operativa

Resumida en tres líneas para el sistema de diseño:

  1. Los iconos monocromos declaran fill="currentColor" (o stroke="currentColor") en el propio marcado, como atributo. Así funcionan aunque no haya CSS.
  2. El color se pone en el contenedor con la propiedad color, nunca con fill desde una regla que apunte a svg.
  3. Si el icono tiene que ir en un fondo CSS, usa mask y no background-image.

Con eso, el mismo fichero sirve para el tema claro, el oscuro, el estado de hover, el de error y el deshabilitado, sin variantes.