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 mis ideas sobre cómo administrar varios esquemas de colores en CSS. Prueba la demostración.

Demo

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

Descripción general

Crearemos un sistema de colores accesible con propiedades personalizadas y calc() para hacer una página web que se adapte a las preferencias del usuario y, al mismo tiempo, mantenga la experiencia de autoría en un mínimo. Comenzamos con un color de marca base y a partir de él, compilamos un sistema de variantes: 2 colores de texto, 4 colores de superficie y una sombra a juego.

En esta guía, se comienza por definir todos los colores de cada esquema de color de forma anticipada. Solo se usan para cambiar la página al final.

La marca

A menudo, ya se estableció un color de marca y se entrega como hex o rgb. Este Desafío de GUI tiene un color de marca base de #0af. En primer lugar, para este sistema de colores, el valor hexadecimal se debe convertir a hsl.

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

Para habilitar un concepto de oscurecimiento o aclaramiento del 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 operaciones matemáticas 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 colores, 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, cada una se adjunta con -light.

vista previa de los resultados finales del tema claro

Marca

Comenzando con el color de la marca, se vuelve a compilar uniendo las propiedades personalizadas --brand-hue, --brand-saturation y --brand-lightness dentro de los paréntesis de la función 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 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, mucho 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 intensa del 100% para que el color de la marca pueda asomarse al azul marino oscuro.

--text2-light, no es tan oscuro como el primer color, lo cual es bueno, ya que es un color secundario y también es mucho menos saturado.

Colores de superficie

Los colores de la 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, en comparación con 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 bajaremos 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 estas situaciones, es conveniente hacer la transición de --surface2-light al colocar el cursor sobre --surface3-light, de modo que el desplazamiento del cursor genere un aumento del contraste (del 99% de luminosidad al 92%, lo que lo hace más oscuro).

Sombras

Las sombras dentro de un esquema de colores son más que eso, pero agregan una naturaleza realista al efecto y lo ayudan a destacarse 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 azul.

* {
  --surface-shadow-light: var(--brand-hue) 10% 20%;
  --shadow-strength-light: .02;
}

--surface-shadow-light no está unido a una función hsl. Esto se debe a que el valor --shadow-strength se combinará para crear algo de opacidad, y CSS necesita las piezas para realizar cálculos. Para obtener más información, ve a la sección de sombras de radio.

Todos los colores claros

No es necesario buscar cómo se hacen los colores claros, ya que están todos en un solo lugar del 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 claros en conjunto
Zona de pruebas en CodePen

Tema oscuro

La mayoría de las marcas no comienzan con un tema oscuro, sino con 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 aspectos con los temas oscuros:

  1. Por lo general, los usuarios estarán en la oscuridad mientras usan este tema, así que haz la prueba en la oscuridad.
  2. Los colores deben desaturarse para no vibrar en la pantalla debido a que son demasiado intensos.

vista previa del resultado final del tema oscuro

Marca

El tema claro usa los 3 valores de canales de color hsl de la marca sin alteraciones, mientras que el tema oscuro no lo hace. La saturación se reduce a la mitad y la luminosidad se reduce 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 la superficie deben ser oscuros. Los siguientes colores tienen una baja 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 cambiando una variable.

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

Además, observa la cantidad de saturación que tiene esa sombra. ¿Puedes ver el color cuando miras la interfaz? Intenta quitar la saturación de las herramientas para desarrolladores. ¿Qué prefieres?

Todos los colores oscuros

* {
  --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 en conjunto
Zona de pruebas en CodePen

Tema atenuado

Este esquema de colores se basa en la orquestación de la luminosidad y la saturación. Debe haber suficiente saturación para que se vea un tono, pero también debe pasar apenas las puntuaciones de contraste, ya que, de todos modos, se pretende que sea tenue y de bajo contraste.

vista previa de los resultados finales del tema atenuado

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

Atenúa 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;
}
captura de pantalla de los colores atenuados en conjunto
Zona de pruebas en CodePen

Colores accesibles

Observa cómo la claridad 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 ligereza y espacio entre ellos. En el tema claro, hay un 55% de espacio para respirar. Mantener las diferencias de luminosidad entre el texto y los colores de la superficie en un rango de alrededor del 40% al 50% puede ayudar a mantener altas las relaciones de contraste de color y, al mismo tiempo, es una palanca sutil para ajustar en caso de que las puntuaciones sean bajas.

Lo llamo “bump bump til ya pass”, que es la interacción de aumentar el valor de la luminosidad hasta que una herramienta muestra que estoy pasando.

Se presiona Mayúsculas + flecha hacia abajo para disminuir la luminosidad y aumentar el contraste hasta que se pasa el valor de

Cada uno de los temas creados en este desafío pasa las puntuaciones de contraste. El esquema de colores atenuados tiene el contraste más bajo, pero aún cumple con los requisitos mínimos. Para ayudar a los demás miembros del equipo a usar colores con buen contraste, es recomendable crear una clase de nombre 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 la superficie atenuada y las vinculaciones de texto
Captura de pantalla de la superficie atenuada y las combinaciones de texto con VisBug

Sombra intensa

Los temas usan una clase de utilidad llamada .rad-shadow. Esta sombra se generó con la herramienta Sombra suave, 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 esquema de colores.

cada sombra una al lado de la otra

Para lograrlo, creé 2 variables para cada esquema de colores que se ajustaran, un color de sombra y una intensidad de 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 las sombras cuando se trata de un esquema de colores oscuros. El resultado final fue algo como 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 tuviera que ir más allá con las sombras en mi esquema de colores, también haría que los ángulos de sombra sean una constante de token de diseño, 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

Ahora que se completó la definición previa de los colores, es hora de convertirlos en propiedades independientes del esquema. Me refiero a que, como autor de CSS dentro de este proyecto de esquema de colores, rara vez se necesita acceder al valor de un esquema de colores específico. Quiero que sea fácil mantener el tema.

Para lograrlo, 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, quienes usan las variables de diseño nunca tienen que preocuparse por qué esquema de colores está configurado actualmente, solo deben usar los colores de la superficie y el texto. En lugar de color: var(--text1-light), usa color: var(--text1). Toda la adaptación y el giro de los colores se realizan en un nivel mucho más alto en el CSS.

En el siguiente bloque de código, los estilos de conexión 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 de la marca claro.

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

El sitio ahora usa el tema claro. Este es un momento de éxito muy divertido. Veamos algunos más de esos momentos 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 esquemas de colores genéricos proporcionados según sea necesario y nunca más deberían tener que 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 compilar en la Web. Crea una cuenta de Codepen o aloja tu propia demo, envíame un tweet 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.