Cómo crear un esquema de colores

Una descripción general fundamental de cómo establecer un esquema de colores dinámico y configurable

En esta publicación, quiero compartir formas de administrar varios esquemas de color en CSS. Prueba la demostración.

Demostración

Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:

Descripción general

Compilaremos un sistema de colores accesible con propiedades personalizadas y calc() para crear una página web que se adapte a las preferencias del usuario y que, al mismo tiempo, mantenga una experiencia de creación mínima. Comenzamos con un color de marca base y creamos un sistema de variantes a partir de él: 2 colores de texto, 4 colores de superficie y una sombra coincidente.

En esta guía, se definen por adelantado todos los colores para cada esquema de colores. Solo se utilizan para cambiar la página hasta el final.

La marca

A menudo, un color de marca ya se estableció y se entrega como hex o rgb. Este desafío de la GUI tiene un color de marca base de #0af. Primero, para este sistema de color, el valor hexadecimal debe convertirse a hsl.

* {
  --brand: #0af;
  --brand: hsl(200 100% 50%);
}

Para habilitar un concepto de oscurecer o aclarar el color de la marca, por ejemplo, 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 sobre esas propiedades de color, por ejemplo, calc(var(--brand-lightness) - 20%) para disminuir el valor de luminosidad en un 20%. Esto es fundamental para compilar un esquema de colores, ya que CSS puede mantener todos los colores en la misma familia de tonos ajustando las cantidades de saturación y luminosidad de la HSL.

Tema claro

Cada variante de color se marcará con su esquema coincidente. En este caso, a cada una se le agregará -light.

vista previa de los resultados finales del tema claro

Brand

Comenzando por el color de la marca, se vuelve a compilar uniendo 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

Luego, los elementos esenciales de un esquema de colores 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, como es muy oscuro con un 10% de luminosidad, mantiene la alta saturación del 100% para que el color de la marca pueda verse en el oscuro azul marino.

--text2-light, no es tan oscuro como el primer color, que es bueno como color secundario y, además, está mucho menos saturado.

Colores de superficie

Los colores de la superficie son los fondos, los bordes y otras superficies decorativas sobre las que se ubica el texto. En un tema claro, estos son los colores claros, a diferencia de los colores del texto que eran oscuros. Para crear colores de luz con hsl, usaremos valores porcentuales 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 porque los colores decorativos suelen necesitar más variantes para momentos interactivos, como :focus o :hover, o para crear la apariencia de las capas de papel. En estas situaciones, es bueno realizar la transición de --surface2-light cuando colocas el cursor sobre --surface3-light, por lo que colocar el cursor sobre ellos genera un aumento del contraste (un 99% de luminosidad a un 92% de luminosidad, por lo que se oscurece más).

Sombras

Las sombras dentro de un esquema de colores están muy por encima, pero le agregan una naturaleza realista al efecto y ayudan a que se destaque de 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, construyendo una sombra muy oscura de color azul claro.

* {
  --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 cierta opacidad y CSS necesita las partes necesarias para realizar cálculos. Ve a la sección de sombras radiales para obtener más información.

Colores de las luces juntos

No es necesario buscar por descubrir cómo se hacen los colores de las luces, 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;
}
captura de pantalla de los colores de las luces juntos
Zona de pruebas en CodePen

Tema oscuro

La mayoría de las marcas no comienzan con un tema oscuro, sino que es una variante del tema principal, que suele ser más claro. Por otro lado, los usuarios a menudo eligen un tema oscuro para diferentes contextos, como la noche. Estos factores me llevaron a tener en cuenta dos cosas con los temas oscuros:

  1. Por lo general, los usuarios estarán en la oscuridad mientras usen este tema, así que realiza pruebas en la oscuridad.
  2. Los colores deben reducir la saturación para no vibrar en la pantalla debido a que son demasiado intensos.

vista previa del resultado final del tema oscuro

Brand

El tema claro usó los 3 valores de canales de color de marca hsl sin alteraciones, mientras que el tema oscuro no. La saturación se reduce a la mitad y la luminosidad se reduce a 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 una luminosidad y saturación bajas, y la primera 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 cambiando una variable.

* {
  --surface-shadow-dark: var(--brand-hue) 50% 3%;
  --shadow-strength-dark: .8;
}

Además, observa cuánta saturación hay en esa sombra. ¿Notas el color cuando estás mirando la interfaz? Intenta quitar la saturación de las Herramientas para desarrolladores, ¿cuál prefieres?

Colores oscuros en conjunto

* {
  --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;
}
captura de pantalla de los colores oscuros juntos
Zona de pruebas en CodePen

Atenuar tema

Este esquema de colores tiene como objetivo orquestar la luminosidad y la saturación. Debe haber suficiente saturación presente para seguir teniendo un tono visible, pero también debería pasar muy poco las puntuaciones de contraste, ya que se espera que el contraste sea bajo y atenuado de todos modos.

Vista previa de los resultados finales del tema atenuado

Brand

* {
  --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 los colores todos juntos

* {
  --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;
}
captura de pantalla de los colores atenuados juntos
Zona de pruebas en CodePen

Colores accesibles

Observa que la luminosidad más baja en el conjunto de colores de texto oscuro es del 65% y la más alta en las superficies oscuras es del 25%. Eso es un 40% de luminosidad entre ellos. Con el tema claro, hay un 55% de espacio libre en el tema claro. Mantener las diferencias de luminosidad entre los colores del texto y de la superficie en alrededor de un 40% a un 50% puede ayudar a mantener altas las relaciones de contraste de color y, a su vez, ser una palanca sutil para ajustar en caso de que las puntuaciones sean bajas.

Lo llamo "baches hasta pasar", que es la interacción de cambiar el valor de luminosidad hasta que una herramienta muestra que estoy pasando.

Se presiona Mayúsculas + flecha hacia abajo para reducir la luminosidad y aumentar el contraste hasta pasar

Cada uno de los temas creados en este desafío aprueba las puntuaciones de contraste. El esquema de colores atenuados tiene el contraste más bajo de ellos, pero cumple con los requisitos mínimos. Para ayudar a otros miembros del equipo a usar colores contrastantes, se recomienda 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);
}
Captura de pantalla de las combinaciones de texto y superficie atenuadas
Captura de pantalla de las combinaciones de superficie atenuada y texto con VisBug

Sombra radical

Los temas usan una clase de utilidad llamada .rad-shadow. Esta sombra se generó en la herramienta Smooth Shadow, que apreciamos mucho. Tomé su fragmento generado y lo personalicé con mis propios colores y cálculos de opacidad. La razón de esto fue para crear una sombra que pudiera ajustar dentro de cada esquema de color.

cada sombra junto a la otra

A fin de lograr esto, creé 2 variables para que cada esquema de color se ajuste: un color de sombra y una intensidad de la sombra. El color es para los ajustes de saturación y oscuridad, mientras que la intensidad es una forma fácil de aumentar la intensidad de la sombra cuando es un esquema de colores oscuros. El resultado final fue similar a esto.

: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 fuera más amplio con las sombras en mi esquema de colores, convertiría los ángulos de las sombras en un token de diseño constante, ya que la dirección de la luz debe ser la misma entre todas las sombras del diseño.

Uso de los esquemas de colores

Una vez completada la predefinición de los colores, es hora 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 necesitaría acceder al valor de un esquema de color específico. Quiero que sea fácil mantenerte dentro del tema.

Para ello, el uso del esquema de colores 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 necesitan preocuparse por el esquema de colores que está configurado actualmente, solo deben usar los colores de superficie y texto. En lugar de color: var(--text1-light), usa color: var(--text1). Toda adaptación y dinamización de colores se realiza en un nivel mucho más alto en el CSS.

En profundidad, los estilos conectivos del tema claro en el siguiente bloque de código 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 está usando el tema claro. Este es un momento muy divertido y exitoso. Veamos un poco más de esos momentos cuando usamos nuestros colores predefinidos en otros contextos de esquema 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);
}

Atenuar tema

[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 tienen la libertad de usar los genéricos de esquema de colores proporcionados según sea necesario y nunca deberían tener que volver a preocuparse por los temas.

Conclusión

Ahora que sabes cómo lo hice, ¿cómo lo harías? 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea una Codepen o aloja tu propia demostración, twittea con ella y la agregaré a la sección de remixes de la comunidad 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: demo.