Programación de Sass para crear combinaciones de colores accesibles
- Qué queremos decir con “combinaciones de colores accesibles”
- Las paletas de colores accesibles comienzan con los diseños.
- Desarrollar para la accesibilidad mientras se mantiene fiel a la intención de un sistema de color
- El caso de uso que quiero resolver
- Con las fórmulas puede venir la automatización
- Tiene que haber otra manera...
- Ejemplo de uso
- Una inmersión más profunda en el corazón de la función Sass
- El archivo final de Sass
- ¡Código genial! ¿Pero puedo usar esto en producción?
Todos buscamos algo fácil de lograr para hacer que nuestros sitios y aplicaciones sean más accesibles. Una de las cosas más fáciles que podemos hacer es asegurarnos de que los colores que utilizamos sean agradables a la vista. El alto contraste de color es algo que beneficia a todos. No sólo reduce la fatiga visual en general, sino que también es crucial para las personas que padecen visión reducida.
Así que no solo usemos mejores combinaciones de colores en nuestros diseños, sino que busquemos una manera de facilitarnos la implementación de altos contrastes. Hay una estrategia específica que utilizamos en Oomph que permite que una función Sass haga todo el trabajo pesado por nosotros. Te explicaré cómo lo armamos.
¿Quiere ir directamente al código porque ya comprende todo lo que hay que saber sobre la accesibilidad del color? Aquí tienes.
Qué queremos decir con “combinaciones de colores accesibles”
El contraste de color también es una de esas cosas que podemos pensar que hemos manejado. Pero los altos contrastes de color implican más que simplemente mirar un diseño. Existen diferentes niveles de criterios aceptables que las WCAG han definido como accesibles. En realidad, es una lección de humildad abrir el Comprobador de contraste WebAIM y ejecutar las combinaciones de colores de un sitio a través de él.
Mi equipo cumple con las pautas de nivel AA de las WCAG de forma predeterminada. Esto significa que:
- El texto de 24 píxeles o más, o de 19 píxeles o más si está en negro, debe tener una relación de contraste de color (CCR) de 3,0:1.
- El texto de menos de 24 píxeles debe tener un CCR de 4,5:1.
Si un sitio necesita cumplir con las pautas mejoradas para el Nivel AAA, los requisitos son un poco más altos:
- El texto de 24 píxeles o más, o de 19 píxeles o más si está en negro, debe tener un CCR de 4,5:1.
- El texto de menos de 24 píxeles debe tener un CCR de 7:1.
¿Proporciones? ¿Eh? Sí, hay algunas matemáticas involucradas aquí. Pero la buena noticia es que no necesitamos hacerlo nosotros mismos ni siquiera tener el mismo conocimiento profundo sobre cómo se calculan como lo compartido recientemente Stacie Arellano (que es una lectura obligada si te gusta la ciencia de la accesibilidad del color). ).
Ahí es donde entra Sass. Podemos aprovecharlo para ejecutar cálculos matemáticos difíciles que de otro modo pasarían por alto a muchas de nuestras cabezas. Pero primero, creo que vale la pena abordar los colores accesibles a nivel de diseño.
Las paletas de colores accesibles comienzan con los diseños.
Eso es correcto. El núcleo del trabajo de crear una paleta de colores accesible comienza con los diseños. Idealmente, cualquier diseño web debería consultar una herramienta para verificar que cualquier combinación de colores en uso cumpla con las pautas establecidas y luego modifique los colores que no lo hagan. Cuando nuestro equipo de diseño hace esto, utiliza una herramienta que desarrollamos internamente. Funciona con una lista de colores, probándolos sobre un color oscuro y uno claro, además de proporcionar una forma de probar otras combinaciones.
Esto es lo primero que hace nuestro equipo. Me atrevería a suponer que muchos colores de marca no se eligen teniendo en cuenta la accesibilidad. A menudo encuentro que esos colores deben cambiar cuando se traducen a un diseño web. A través de educación, conversación y muestras visuales, logramos que el cliente apruebe la nueva paleta de colores. Lo admito: esa parte puede ser más difícil que el trabajo real de implementar combinaciones de colores accesibles.
El problema que quería resolver con la automatización son los casos extremos . No se puede culpar a un diseñador por pasar por alto algún caso en el que dos colores se combinan de forma no deseada; simplemente sucede. Y esos casos extremos surgirán, ya sea durante la construcción o incluso un año después, cuando se agreguen nuevos colores al sistema.
Desarrollar para la accesibilidad mientras se mantiene fiel a la intención de un sistema de color
El truco a la hora de cambiar los colores para cumplir con los requisitos de accesibilidad es no cambiarlos tanto que ya no parezcan del mismo color. Una marca que ama su color verde esmeralda querrá mantener la intención de ese color: su “esmeralda”. Para que pase por accesibilidad cuando se utiliza como texto sobre fondo blanco, es posible que tengamos que oscurecer el verde y aumentar su saturación. Pero todavía queremos que el color “se lea” igual que el color original.
Para lograr esto, utilizamos el modelo de color Hue Saturation Lightness (HSL). HSL nos da la posibilidad de mantener el tono tal como está pero ajustar la saturación (es decir, aumentar o disminuir el color) y la luminosidad (es decir, agregar más negro o más blanco). El tono es lo que hace que un verde sea verde o un azul tan azul. Es el “alma” del color, para decirlo un poco más místico.
El tono se representa como una rueda de colores con un valor entre 0° y 360°: amarillo a 60°, verde a 120°, cian a 180°, etc. La saturación es un porcentaje que va del 0% (sin saturación) al 100% (saturación total). La luminosidad también es un valor que va del 0% al 100%, donde ninguna luminosidad está en 0%, nada de negro ni blanco está en 50%, y 100% es toda luminosidad, o muy clara.
Una visión rápida de cómo se ve ajustar un color en nuestra herramienta:
Para obtener más información, juegue con el divertido visualizador HSL mothereffinghsl.com. Pero para obtener una descripción más detallada del daltonismo, los niveles de contraste de color WCAG y el espacio de color HSL, escribimos una publicación de blog detallada al respecto.
El caso de uso que quiero resolver
Los diseñadores pueden ajustar los colores con las herramientas que acabamos de revisar, pero hasta ahora, ningún Sass que haya encontrado puede hacerlo con magia matemática. Tenía que haber una manera.
Estos son algunos enfoques similares que he visto en la naturaleza:
- Una idea de Josh Bader utiliza variables CSS y colores divididos en sus valores RGB para calcular si el blanco o el negro es el mejor color accesible para usar en una situación determinada.
- Otra idea de Facundo Corradini hace algo similar con los valores HSL y una “función de cambio” muy interesante en CSS.
No me gustaron estos enfoques. No quería recurrir al blanco o al negro. Quería que los colores se mantuvieran pero se ajustaran para que fueran accesibles. Además, cambiar los colores de sus componentes RGB o HSL y almacenarlos con variables CSS parecía complicado e insostenible para una base de código grande.
Quería usar un preprocesador como Sass para hacer esto: dados dos colores, ajustar automáticamente uno de ellos para que el par reciba una calificación WCAG aprobatoria. Las reglas también establecen algunas otras cosas a considerar: el tamaño del texto y si la fuente está en negrita o no. La solución debía tener esto en cuenta.
En términos de código, quería hacer esto:
// Transform this non-passing color pair:.example { background-color: #444; color: #0094c2; // a 2.79 contrast ratio when AA requires 4.5 font-size: 1.25rem; font-weight: normal;}
// To this passing color pair:.example { background-color: #444; color: #00c0fc; // a 4.61 contrast ratio font-size: 1.25rem; font-weight: normal;}
Una solución que haga esto sería capaz de detectar y manejar esos casos extremos que mencionamos anteriormente. Quizás el diseñador prefirió usar un azul de marca sobre un azul claro, pero no un gris claro. Tal vez sea necesario modificar el rojo utilizado en los mensajes de error para este formulario que tiene un color de fondo único. Tal vez queramos implementar una función de modo oscuro en la interfaz de usuario sin tener que volver a probar todos los colores. Estos son los casos de uso que tenía en mente al abordar esto.
Con las fórmulas puede venir la automatización
El W3C ha proporcionado a la comunidad fórmulas que ayudan a analizar dos colores utilizados juntos. La fórmula multiplica los canales RGB de ambos colores por números mágicos (un peso visual basado en cómo los humanos perciben estos canales de color) y luego los divide para obtener una proporción de 0,0 (sin contraste) a 21,0 (todo el contraste, sólo posible con blanco y negro). Aunque imperfecta, esta es la fórmula que utilizamos ahora:
If L1 is the relative luminance of a first colorAnd L2 is the relative luminance of a second color, then- Color Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)Where- L = 0.2126 * R + 0.7152 * G + 0.0722 * BAnd- if R sRGB = 0.03928 then R = R sRGB /12.92 else R = ((R sRGB +0.055)/1.055) ^ 2.4- if G sRGB = 0.03928 then G = G sRGB /12.92 else G = ((G sRGB +0.055)/1.055) ^ 2.4- if B sRGB = 0.03928 then B = B sRGB /12.92 else B = ((B sRGB +0.055)/1.055) ^ 2.4And- R sRGB = R 8bit /255- G sRGB = G 8bit /255- B sRGB = B 8bit /255
Si bien la fórmula parece compleja, es solo matemática, ¿verdad? Bueno, no tan rápido. Hay una parte al final de algunas líneas donde el valor se multiplica por una potencia decimal, elevada a la potencia de 2,4. ¿Darse cuenta de? Resulta que son matemáticas complejas las que la mayoría de los lenguajes de programación pueden realizar (piense en math.pow()
la función de Javascript), pero Sass no es lo suficientemente potente para hacerlo.
Tiene que haber otra manera...
Por supuesto que sí. Sólo tomó algo de tiempo encontrarlo.
Mi primera versión utilizó una serie compleja de cálculos matemáticos que hacían el trabajo de potencias decimales dentro de los límites limitados de lo que Sass puede lograr. Muchas búsquedas en Google encontraron que personas mucho más inteligentes que yo proporcionaban las funciones. Desafortunadamente, calcular solo un puñado de combinaciones de contraste de color aumentó exponencialmente los tiempos de construcción de Sass. Entonces, eso significa que Sass puede hacerlo, pero eso no significa que deba hacerlo. En producción, los tiempos de compilación para una base de código grande podrían aumentar a varios minutos. Eso no es aceptable.
Después de buscar más en Google, encontré una publicación de alguien que intentaba hacer algo similar. También se toparon con la falta de apoyo de los exponentes en Sass. Querían explorar "la posibilidad de utilizar la aproximación newtoniana para las partes fraccionarias del exponente". Entiendo totalmente el impulso (no). En lugar de ello, decidieron utilizar una “tabla de consulta”. Es una solución genial. En lugar de hacer los cálculos desde cero cada vez, una tabla de búsqueda proporciona todas las respuestas posibles calculadas previamente. La función Sass recupera la respuesta de la lista y listo.
En sus palabras:
La única parte [de Sass que] implica exponenciación son las conversiones de espacio de color por canal realizadas como parte del cálculo de luminancia. [E]aquí sólo hay 256 valores posibles para cada canal. Esto significa que podemos crear fácilmente una tabla de búsqueda.
Ahora estamos cocinando. Había encontrado una dirección más eficaz.
Ejemplo de uso
Usar la función debe ser fácil y flexible. Dado un conjunto de dos colores, ajuste el primer color para que pase el valor de contraste correcto para el nivel WCAG dado cuando se use con el segundo color. Los parámetros opcionales también tendrán en cuenta el tamaño de fuente o la negrita.
// @function a11y-color(// $color-to-adjust,// $color-that-will-stay-the-same,// $wcag-level: 'AA',// $font-size: 16,// $bold: false// );
// Sass sample usage declaring only what is required.example { background-color: #444; color: a11y-color(#0094c2, #444); // a 2.79 contrast ratio when AA requires 4.5 for small text that is not bold}
// Compiled CSS results:.example { background-color: #444; color: #00c0fc; // which is a 4.61 contrast ratio}
Utilicé una función en lugar de un mixin porque prefería la salida de un valor único independiente de una regla CSS. Con una función, el autor puede determinar qué color debe cambiar.
Un ejemplo con más parámetros implementados se ve así:
// Sass.example-2 { background-color: a11y-color(#0094c2, #f0f0f0, 'AAA', 1.25rem, true); // a 3.06 contrast ratio when AAA requires 4.5 for text 19px or larger that is also bold color: #f0f0f0; font-size: 1.25rem; font-weight: bold;}
// Compiled CSS results:.example-2 { background-color: #087597; // a 4.6 contrast ratio color: #f0f0f0; font-size: 1.25rem; font-weight: bold;}
Una inmersión más profunda en el corazón de la función Sass
Para explicar el enfoque, veamos qué hace la función final, línea por línea. Hay muchas funciones auxiliares a lo largo del camino, pero los comentarios y la lógica de la función principal explican el enfoque:
// Expected:// $fg as a color that will change// $bg as a color that will be static and not change// Optional:// $level, default 'AA'. 'AAA' also accepted// $size, default 16. PX expected, EM and REM allowed// $bold, boolean, default false. Whether or not the font is currently bold//@function a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false) { // Helper: make sure the font size value is acceptable $font-size: validate-font-size($size); // Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 results expected $ratio: get-ratio($level, $font-size, $bold); // Calculate the first contrast ratio of the given pair $original-contrast: color-contrast($fg, $bg); @if $original-contrast = $ratio { // If we pass the ratio already, return the original color @return $fg; } @else { // Doesn't pass. Time to get to work // Should the color be lightened or darkened? // Helper: Single color input, 'light' or 'dark' as output $fg-lod: light-or-dark($fg); $bg-lod: light-or-dark($bg);
// Set a "step" value to lighten or darken a color // Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 5% $step: 2%; // Run through some cases where we want to darken, or use a negative step value @if $fg-lod == 'light' and $bg-lod == 'light' { // Both are light colors, darken the fg (make the step value negative) $step: - $step; } @else if $fg-lod == 'dark' and $bg-lod == 'light' { // bg is light, fg is dark but does not pass, darken more $step: - $step; } // Keeping the rest of the logic here, but our default values do not change, so this logic is not needed //@else if $fg-lod == 'light' and $bg-lod == 'dark' { // // bg is dark, fg is light but does not pass, lighten further // $step: $step; //} @else if $fg-lod == 'dark' and $bg-lod == 'dark' { // // Both are dark, so lighten the fg // $step: $step; //} // The magic happens here // Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false // This might loop 100 times or more depending on the colors @while color-contrast($fg, $bg) $ratio { // Moving the lightness is most effective, but also moving the saturation by a little bit is nice and helps maintain the "power" of the color $fg: scale-color($fg, $lightness: $step, $saturation: $step/2); } @return $fg; }}
El archivo final de Sass
¡Aquí está todo el conjunto de funciones! Abra esto en CodePen para editar las variables de color en la parte superior del archivo y ver los ajustes que realiza Sass:
Todas las funciones auxiliares están ahí, así como la tabla de búsqueda de 256 líneas. Muchos comentarios deberían ayudar a la gente a comprender lo que está pasando.
Cuando se encontró un caso límite, una versión en SassMeister con salida de depuración fue útil mientras la desarrollaba para ver qué podría estar sucediendo. (Cambié la función principal a un mixin para poder depurar la salida). Siéntete libre de explorar esto también.
Juega con esta esencia en SassMeister.
Y finalmente, las funciones se eliminaron de CodePen y se colocaron en un repositorio de GitHub. Coloque los problemas en la cola si tiene problemas.
¡Código genial! ¿Pero puedo usar esto en producción?
Tal vez.
Me gustaría decir que sí, pero he estado insistiendo en este espinoso problema desde hace un tiempo. Tengo confianza en este código, pero me encantaría recibir más comentarios. Úselo en un proyecto pequeño y patee los neumáticos. Déjame saber cómo funciona el tiempo de construcción. Avíseme si se encuentra con casos extremos en los que no se proporcionan valores de color de paso. Envíe problemas al repositorio de GutHub. Sugiera mejoras basadas en otro código que haya visto en el mercado.
Me encantaría decir que he automatizado todas las cosas, pero también sé que es necesario probarlo antes de que pueda llamarse Production Ready™. Estoy emocionado de presentarlo al mundo. Gracias por leer y espero saber cómo lo estás usando muy pronto.
Deja un comentario