Descripción general básica de cómo establecer un esquema de colores dinámico y configurable
En esta entrada, quiero compartir ideas sobre cómo administrar varios esquemas de color en CSS. Prueba la demostración.
Si prefieres un video, aquí tienes una versión de este artículo en YouTube:
Descripción general
Crearemos un sistema de color accesible con propiedades personalizadas y calc() para crear una página web que se adapte a las preferencias del usuario y, al mismo tiempo, mantenga la experiencia de creación al mínimo. Comenzamos con un color base de la marca y creamos un sistema de variantes a partir de él: 2 colores de texto, 4 colores de superficie y una sombra que coincida.
En esta guía, se comienza por definir todos los colores para cada esquema de color por adelantado. No se usan para cambiar la página hasta el final.
La marca
A menudo, ya se estableció un color de la marca y se entrega como hexadecimal o RGB. Este desafío de GUI tiene un color base de la marca de #0af. En primer lugar, para este sistema de color, el valor hexadecimal debe convertirse a hsl.
* {
--brand: #0af;
--brand: hsl(200 100% 50%);
}
Para habilitar un concepto de oscurecimiento o aclaramiento del color de la marca, por ejemplo, en un 20%, los 3 canales del valor de color hsl deben extraerse en sus propias propiedades personalizadas, de la siguiente manera:
* {
--brand-hue: 200;
--brand-saturation: 100%;
--brand-lightness: 50%;
}
CSS puede realizar cálculos matemáticos en esas propiedades de color, por ejemplo, calc(var(--brand-lightness) -
20%) para disminuir el valor de luminosidad en un 20%. Esto es fundamental para crear un esquema de color, ya que CSS puede mantener todos los colores en la misma familia de tonos ajustando los valores de saturación y luminosidad de HSL.
Tema claro
Cada variante de color se marcará con su esquema coincidente. En este caso, a cada una se le agrega -light.

Marca
Comenzando con el color de la marca, se reconstruye envolviendo las propiedades personalizadas --brand-hue, --brand-saturation y --brand-lightness dentro del paréntesis de la función () de hsl, sin ningún cálculo:
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
}
Colores del texto
A continuación, los elementos esenciales de un esquema de color necesitan colores de texto. En un tema claro, el texto debe ser muy oscuro. Observa cómo la luminosidad de los siguientes colores es baja, muy por debajo del 50%.
* {
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
}
--text1-light, ya que es muy oscuro con un 10% de luminosidad, mantiene la saturación alta del 100% para que el color de la marca aún pueda asomarse en el azul oscuro.
--text2-light, no es tan oscuro como el primer color, lo que es bueno, ya que es un color secundario, y también es mucho menos saturado.
Colores de superficie
Los colores de superficie son los fondos, los bordes y otras superficies decorativas sobre las que se encuentra el texto o dentro de las que se encuentra. En un tema claro, estos son los colores claros, a diferencia de los colores del texto, que eran oscuros. Para crear colores claros con HSL, usaremos valores de porcentaje más altos en el tercer valor de luminosidad. También reduciremos la saturación para que los grises claros no se vean demasiado teñidos.
* {
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
}
Se crearon 4 colores de superficie, ya que los colores decorativos suelen necesitar más variantes para momentos interactivos, como :focus o :hover, o para crear la apariencia de capas de papel. En estos casos, es conveniente hacer la transición de --surface2-light a --surface3-light cuando se coloca el cursor sobre el elemento, de modo que el contraste aumente (del 99% de claridad al 92%, lo que lo hace más oscuro).
Sombras
Las sombras dentro de un esquema de color son excelentes, pero agregan un aspecto realista al efecto y ayudan a que se destaque de las sombras poco realistas basadas en el negro. Para ello, el color de la sombra usará la propiedad personalizada de tono, estará ligeramente saturado con el tono, pero seguirá siendo muy oscuro. Básicamente, se crea una sombra muy oscura y ligeramente azulada.
* {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
--surface-shadow-light no está incluido en una función hsl. Esto se debe a que el valor --shadow-strength se combinará para crear algo de opacidad, y CSS necesita las partes para realizar cálculos. Ve a la sección de sombra de radio para obtener más información.
Colores claros juntos
No es necesario buscar cómo se crean los colores de la luz, ya que todos están en un solo lugar en el CSS.
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
--surface-shadow-light: var(--brand-hue) 10% calc(var(--brand-lightness) / 5);
--shadow-strength-light: .02;
}
Tema oscuro
La mayoría de las marcas no comienzan con un tema oscuro, sino que es una variante de su tema principal, que suele ser más claro. Por otro lado, los usuarios suelen elegir un tema oscuro para diferentes contextos, como la noche. Estos factores me llevaron a tener en cuenta dos cosas con los temas oscuros:
- Por lo general, los usuarios estarán en la oscuridad cuando usen este tema, así que haz pruebas en la oscuridad.
- Los colores deben desaturarse para no vibrar en la pantalla debido a su intensidad excesiva.

Marca
El tema claro usaba los 3 valores de los canales de color HSL de la marca sin alteraciones, pero el tema oscuro no. La saturación se reduce a la mitad y la luminosidad se reduce en un 50% relativo.
* {
--brand-dark: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 2)
calc(var(--brand-lightness) / 1.5)
);
}
Colores del texto
En un tema oscuro, los colores del texto deben ser claros. Los siguientes colores tienen valores altos de luminosidad, lo que los acerca al blanco.
* {
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
}
Colores de superficie
En un tema oscuro, los colores de superficie deben ser oscuros. Los siguientes colores tienen poca luminosidad y saturación, y la 1ª superficie es la más oscura, con un 10%.
* {
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
}
Sombras
En un tema oscuro, las sombras pueden ser muy difíciles de ver. Tiene sentido, ya que es difícil oscurecer algo que ya es bastante oscuro. Aquí es donde --shadow-strength-dark resulta muy útil, ya que nos permite oscurecer las sombras con solo cambiar una variable.
* {
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
También observa cuánta saturación hay en esa sombra. ¿Puedes notar el color cuando miras la interfaz? Intenta quitar la saturación de las Herramientas para desarrolladores. ¿Qué prefieres?
Colores oscuros todos juntos
* {
--brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5));
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
Tema Atenuado
Este esquema de color se basa en la combinación de luminosidad y saturación. Debe haber suficiente saturación para que se vea un tono, pero también debe superar apenas las puntuaciones de contraste, ya que se pretende que sea tenue y de contraste bajo.

Marca
* {
--brand-dim: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 1.25)
calc(var(--brand-lightness) / 1.25)
);
}
Colores del texto
* {
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
}
Colores de superficie
* {
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
}
Sombras
* {
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
Atenuar todos los colores
* {
--brand-dim: hsl(var(--brand-hue) calc(var(--brand-saturation) / 1.25) calc(var(--brand-lightness) / 1.25));
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
Colores accesibles
Observa cómo la luminosidad más baja en el conjunto de colores de texto oscuro es del 65% y la luminosidad más alta en las superficies oscuras es del 25%. Eso representa un 40% de espacio de claridad entre ellos. En el tema claro, hay un 55% de espacio libre. Mantener las diferencias de luminosidad entre el texto y los colores de la superficie en torno al 40 y el 50% puede ayudar a mantener altas las relaciones de contraste de color, además de ser un factor sutil para ajustar en caso de que las puntuaciones sean bajas.
La llamo "bump bump til ya pass", que es la interacción de aumentar el valor de luminosidad hasta que una herramienta muestra que estoy pasando.
Cada uno de los temas creados en este desafío supera las puntuaciones de contraste. El esquema de color tenue tiene el contraste más bajo de todos, pero aun así cumple con los requisitos mínimos. Para ayudar a otros miembros del equipo a usar colores con buen contraste, es una buena idea crear un nombre de clase que combine un color de superficie con un color de texto accesible.
.surface1 {
background-color: var(--surface1);
color: var(--text2);
}
.surface2 {
background-color: var(--surface2);
color: var(--text2);
}
.surface3 {
background-color: var(--surface3);
color: var(--text1);
}
.surface4 {
background-color: var(--surface4);
color: var(--text1);
}
Rad Shadow
Los temas usan una clase de utilidad llamada .rad-shadow. Esta sombra se generó con la herramienta Sombra suave, lo que agradezco mucho. Tomé el fragmento generado y lo personalicé con mis propios colores y cálculos de opacidad. El motivo de esto fue crear una sombra que pudiera ajustar dentro de cada combinación de colores.

Para lograrlo, creé 2 variables para cada combinación de colores que se ajustará: un color de sombra y una intensidad de sombra. El color se usa para ajustar la saturación y la oscuridad, mientras que la intensidad es una forma sencilla de aumentar la intensidad de la sombra cuando se trata de un esquema de colores oscuros. El resultado final fue similar al siguiente.
:root {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
.rad-shadow {
box-shadow:
0 2.8px 2.2px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 6.7px 5.3px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .01)),
0 12.5px 10px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 22.3px 17.9px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 41.8px 33.4px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 100px 80px hsl(var(--surface-shadow) / var(--shadow-strength))
;
}
Si quisiera profundizar en las sombras de mi combinación de colores, también convertiría los ángulos de las sombras en una constante de token de diseño, ya que la dirección de la luz debería ser la misma entre todas las sombras del diseño.
Uso de los esquemas de colores
Una vez que se completó la predefinición de los colores, es momento de convertirlos en propiedades independientes del esquema. Lo que quiero decir es que, como autor de CSS dentro de este proyecto de esquema de colores, rara vez se debería necesitar acceder al valor de un esquema de colores específico. Quiero que sea fácil mantenerte dentro del tema.
Para lograr esto, el uso del esquema de color debe realizarse exclusivamente a través de las propiedades personalizadas genéricas, que definiremos en un momento. De esta manera, las personas que usan las variables de diseño nunca tienen que preocuparse por qué esquema de color está configurado actualmente, solo deben usar los colores de superficie y texto. En lugar de color: var(--text1-light), usa color: var(--text1). Toda la adaptación y el ajuste de colores se realizan en un nivel mucho más alto en el CSS.
En el siguiente bloque de código, los estilos conectivos del tema claro conectan una propiedad personalizada genérica con el color específico del tema claro. Ahora, todos los usos de var(--brand) usarán el color claro de la marca.
Tema claro (automático)
:root {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
Ahora el sitio usa el tema claro. Este es un momento muy divertido y exitoso. Tengamos algunos momentos más como esos mientras usamos nuestros colores predefinidos en otros contextos de esquemas de colores.
Tema oscuro (automático)
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
}
Tema claro
[color-scheme="light"] {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
Tema oscuro
[color-scheme="dark"] {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
Tema Atenuado
[color-scheme="dim"] {
color-scheme: dark;
--brand: var(--brand-dim);
--text1: var(--text1-dim);
--text2: var(--text2-dim);
--surface1: var(--surface1-dim);
--surface2: var(--surface2-dim);
--surface3: var(--surface3-dim);
--surface4: var(--surface4-dim);
--surface-shadow: var(--surface-shadow-dim);
--shadow-strength: var(--shadow-strength-dim);
}
En este punto, los autores pueden usar los elementos genéricos del esquema de color proporcionado según sea necesario y no deberían tener que preocuparse por los temas nunca más.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías tú? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de crear contenido en la Web. Crea un Codepen o aloja tu propia demostración, envíame un tuit con ella y la agregaré a la sección de remixes de la comunidad que se encuentra a continuación.
Fuente
Remixes de la comunidad
- @chris-kruining agregó un control deslizante de tono, colores de estado y modos de contraste para no-preference, more y less: demostración.