Cómo crear un tema oscuro sin romper cosas: aprender con el equipo de Yandex Mail


Mi nombre es Vladimir y desarrollo front-end móvil para Yandex Mail. Nuestras aplicaciones han tenido un tema oscuro por un tiempo, pero estaba incompleto: solo la interfaz y los correos electrónicos simples estaban oscuros. Los mensajes con formato personalizado permanecieron claros y se destacaron contra la interfaz oscura, dañando los ojos de nuestros usuarios por la noche.


Hoy te diré cómo solucionamos este problema. Aprenderá sobre dos técnicas simples que no funcionaron para nosotros y el método que finalmente hizo el truco: la recoloración adaptativa de páginas. También compartiré algunas ideas sobre la adaptación de imágenes a un tema oscuro. Para ser justos, oscurecer las páginas con CSS personalizado es una tarea bastante peculiar, pero creo que algunos de ustedes pueden encontrar nuestra experiencia útil.


Métodos simples


Antes de decidirnos por nuestra técnica mágica de doblar colores, probamos dos opciones realmente básicas: aplicar un estilo oscuro adicional o un filtro CSS a los elementos. Ninguna de las opciones funcionó para nosotros, pero podrían ser más adecuadas en otros casos (lo simple es genial, ¿verdad?).


Estilos primordiales


Esta es una opción muy simple, que lógicamente extiende el tema CSS oscuro de la aplicación. Simplemente coloca estilos oscuros adicionales en un contenedor de correo electrónico (o, en general, en un contenedor para que el contenido generado por el usuario se oscurezca):


.message--dark { background-color: black; color: white; } 

Sin embargo, cualquier estilo en los elementos dentro del correo electrónico anulará nuestro estilo raíz. Y no !important no ayuda aquí. La idea se puede dar un paso más al prevenir la herencia:


 .message--dark * { background-color: black !important; color: white !important; border-color: #333 !important; } 

¡En este caso, no puede prescindir !important , ya que el selector en sí no es muy específico. Esto también sucede para anular la mayoría de los estilos en línea (¡los que tienen en línea !important aún encontrará su camino, y no hay mucho que pueda hacer al respecto).


Nuestro estilo pinta con entusiasmo todo del mismo color, presentando así otro problema. Existe la posibilidad de que el diseñador quisiera decir algo con la forma en que organizaron los colores (ya sabes, prioridades, combinaciones, todas esas cosas de diseñador), y simplemente tomamos su idea y la arrojamos por la ventana. No es una buena cosa que hacer.



Si no respetas a los diseñadores tanto como a mí y decides seguir este método, no olvides manejar las cosas no tan obvias:


  • box-shadow Sin embargo, no podrá anular solo el color. O deshazte de las sombras por completo o haz las paces con las claras.
  • Los colores de los elementos semánticos, como enlaces o entradas.
  • SVG en línea. Use fill lugar de background y stroke lugar de color , pero nunca puede estar seguro, puede ser al revés.

Desde un punto de vista técnico, este es un método sólido: se necesitan tres líneas de código (OK, treinta para una versión lista para producción con todos los casos límite atendidos), es compatible con todos los navegadores del mundo, funciona en páginas dinámicas listas para usar, y la forma en que se adjunta CSS al documento es completamente irrelevante. También hay una buena ventaja: puede ajustar fácilmente los colores en el estilo para que coincidan con los colores principales de la aplicación (por ejemplo, haga el fondo #bbbbb8 lugar de negro).


Por cierto, así es como solíamos oscurecer los correos electrónicos antes. Si un correo electrónico tuviera algún estilo propio, simplemente nos rendiríamos y lo dejaríamos ligero por si acaso.


Usando un filtro CSS


Este es un método inteligente y elegante. Puede oscurecer una página usando un filtro CSS:


 .message--dark { filter: invert(100) hue-rotate(180deg); /* hue-rotate    */ } 

Las imágenes se vuelven espeluznantes, pero podemos solucionarlo fácilmente:


 .message-dark img { filter: invert(100) hue-rotate(180deg); } 


Sin embargo, esto no resuelve el problema de las imágenes de contenido adjuntas a través de la propiedad de fondo (claro, es útil para ajustar la relación de aspecto, pero ¿qué pasa con la semántica?). Bien, supongamos que podemos encontrar todos estos elementos, marcarlos explícitamente y cambiar sus colores también.


Lo bueno de este método es que conserva el brillo original y el equilibrio de contraste. Por otro lado, está lleno de problemas que superan las ventajas:



  1. Las páginas oscuras se vuelven brillantes.
  2. No puedes controlar los colores finales. ¿Qué filtro aplica al fondo para que coincida con su marca #bbbbb8 ? Eso es un verdadero rascador de cabeza.
  3. La doble inversión hace que las imágenes se desvanezcan.
  4. El rendimiento es lento, especialmente en dispositivos móviles: tiene sentido, en lugar de simplemente mostrar la página, el navegador ejecuta transformadas de Fourier en cada sorteo.

Este método funcionaría para correos electrónicos con texto de color neutro, pero nunca he conocido a nadie con una bandeja de entrada llena de dicho contenido. Por otro lado, los filtros son la única forma de oscurecer elementos sin acceder a sus contenidos: marcos, componentes web o imágenes.


Tema oscuro adaptativo


¡Y ahora es tiempo de magia! Con base en los inconvenientes de los dos primeros enfoques, hicimos una lista de verificación de lo que debemos cuidar:


  1. Oscurezca el fondo, el texto sea claro y los bordes en algún punto intermedio.
  2. Detecta páginas oscuras y conserva sus colores.
  3. Mantener el equilibrio original de brillo y contraste.
  4. Dar control sobre los colores resultantes.
  5. Deja los matices sin cambios.

Por lo tanto, debe cambiar los colores de los estilos para oscurecer el fondo. ¿Por qué no hacer eso, literalmente? Tome todos los estilos, extraiga las reglas relacionadas con el color ( color , background , border , box-shadow y sus subpropiedades) y reemplácelas con sus versiones "oscurecidas": oscurezca el fondo y los bordes (pero no tanto como el fondo), aligerar el texto, etc.


Este método tiene una ventaja increíble que calentará el corazón de cualquier desarrollador. Puede configurar reglas de conversión de color para cada propiedad, ¡sí, usando código! Con un poco de imaginación, puede ajustar los colores a cualquier tema externo, realizar cualquier corrección de color que desee (por ejemplo, hacer un tema claro en lugar de uno oscuro o un tema del color que desee) e incluso agregar un un poco de contextualidad, por ejemplo, trate los bordes anchos y estrechos de manera diferente.


Las desventajas son lo que esperaría de un enfoque de "todo en js". Ejecutamos scripts, encapsulamos el estilo de encapsulación y analizamos CSS con expresiones regulares. Sin embargo, esto último no es tan humillante como con HTML, porque la gramática CSS es regular (al menos en el nivel con el que estamos trabajando).


Aquí está nuestro plan oscuro:


  1. Normalice las propiedades de estilo heredadas ( bgcolor y otras) y muévalas a style="..." .
  2. Encuentra todos los estilos en línea.
  3. Encuentre todas las reglas relacionadas con el color en cada estilo ( background-color , color , box-shadow y otros).
  4. Toma los colores de las propiedades relacionadas con el color y combínalos con el convertidor correcto (oscurece el fondo, aligera el texto).
  5. Llama al convertidor.
  6. Vuelva a colocar las propiedades convertidas en el CSS.

El código contenedor (normalización, estilos de localización, análisis) es bastante trivial. Veamos cómo funciona exactamente nuestro convertidor mágico.


Conversiones HSL


El oscurecimiento de un color no es tan simple como parece, especialmente si desea preservar el tono (por ejemplo, convierta el azul en azul oscuro, no en naranja). Puede hacer esto en RGB ordinario, pero es problemático. Si te gusta el diseño algorítmico, sabes que en RGB, incluso los gradientes están rotos. Mientras tanto, trabajar con colores en HSL es un verdadero placer: en lugar de rojo, verde y azul, que no tiene idea de qué hacer, tiene los siguientes tres canales:


  • Hue: lo que buscamos preservar.
  • Saturación, que no es muy importante para nosotros en este momento.
  • La ligereza, que estaremos cambiando.

Puedes pensarlo como un cilindro. Necesitamos poner este cilindro al revés. La corrección de color hace algo como esto: (h, s, l) => [h, s, 1-l] .


Colores que ya están bien


A veces tenemos suerte y el diseño personalizado del correo electrónico (o una parte de él) ya está oscuro. Esto significa que no necesitamos cambiar nada: el diseñador probablemente hizo un mejor trabajo al elegir los colores de lo que nuestro algoritmo podría soñar. En HSL, solo necesita considerar la L - Ligereza. Si su valor es mayor o menor (para el texto y el fondo, respectivamente) que el umbral (que puede establecer), simplemente déjelo solo.


Circo dinámico


Aunque no necesitábamos nada de esto (muchas gracias, desinfectante, me salvaste el día aquí), enumeraré las características adicionales que un tema adaptable necesita para oscurecer páginas enteras y no solo correos electrónicos estáticos tontos de los años 90. Advertencia justa: esta tarea no es para todos.


Estilos dinámicos en línea


Cambiar los estilos en línea es el caso más simple que arruina nuestras páginas oscuras. Es común, pero hay una solución simple: agregue MutationObserver y corrija los estilos en línea tan pronto como ocurran los cambios.


Estilos externos


Trabajar con estilos referenciados por un elemento <link> desde el interior de la página puede ser agonizante debido a la asincronía y las declaraciones @import . Y CORS no mejora las cosas. Parece que este problema se puede resolver con bastante elegancia con un trabajador web (proxy de todo *.css ).


Estilos dinámicos


Para empeorar las cosas, los scripts pueden agregar, eliminar o reorganizar los elementos <style> y <link> como lo deseen, e incluso cambiar las reglas en <style> . Aquí también deberá usar MutationObserver , pero cada cambio generará más procesamiento.


Variables CSS


Las cosas se ponen realmente salvajes cuando las variables CSS entran en juego. No puede oscurecer las variables: incluso si adivina que una variable contiene un color en función de su formato (aunque no lo recomendaría), aún no sabe cuál es su función: fondo, texto, borde o todo a la vez? Además, los valores de las variables CSS se heredan, por lo que debe realizar un seguimiento no solo de los estilos, sino también de los elementos a los que se aplican, y esto rápidamente se descontrola.


Una vez que las variables CSS llegan a la corriente principal, estamos en problemas. Por otro lado, el color() será compatible para entonces, lo que nos permitirá reemplazar todas nuestras conversiones JS con una color(var(--bg) lightness(-50%)) .


Resumen



En nuestro caso, cuando el desinfectante deja solo estilos en línea, el oscurecimiento adaptativo a nivel de CSS funciona de maravilla: proporciona un oscurecimiento de mayor calidad, no interrumpe la estructura original y es relativamente simple y rápido. Sin embargo, extenderlo para abordar páginas dinámicas probablemente no valga la pena. Afortunadamente, si está trabajando con contenido generado por el usuario y no está desarrollando un navegador, es probable que su desinfectante funcione de la misma manera.


En la práctica, el modo adaptativo debe combinarse con anulaciones de estilo, ya que los elementos estándar como <input> o <a> menudo usan los estilos de luz predeterminados.


Cómo oscurecer imágenes


El oscurecimiento de la imagen es un tema separado que me molesta personalmente. Es desafiante y me da una excusa para finalmente usar la frase "análisis espectral". Hay varios problemas típicos con imágenes en un tema oscuro.


Algunas imágenes son demasiado brillantes. Estos nos dan el mismo problema que los brillantes correos electrónicos con los que comenzamos nuestra búsqueda. Este problema a menudo surge en las fotos comunes, pero no es exclusivo de estas. Dado que escribir CSS en el boletín no es muy divertido, a algunos les gusta saltear esquinas insertando un diseño complejo como una imagen, lo que evita el oscurecimiento. Cuando veo eso, mi perfeccionista interno gime de frustración. Estas imágenes deben atenuarse, pero no invertirse, a menos que desee asustar a sus usuarios.



Luego hay imágenes oscuras con transparencia real. Este es un problema típico con los logotipos: están diseñados con un fondo claro en mente, y cuando lo reemplazamos con un fondo oscuro, el logotipo desaparece en él. Estas imágenes deben invertirse.



En algún punto intermedio hay imágenes que usan un fondo blanco que se supone que representa la transparencia. En el tema oscuro, simplemente terminan en un extraño triángulo blanco. En un mundo perfecto, podríamos cambiar el fondo blanco a uno transparente, pero si alguna vez has usado una herramienta de varita mágica, sabes lo difícil que es hacerlo automáticamente.



Curiosamente, algunas imágenes no tienen ningún significado. Por lo general, se trata de píxeles de seguimiento o "titulares de formato" en diseños particularmente extravagantes. Puede hacerlos invisibles de forma segura (por ejemplo, con opacity: 0 ).



Analizando lo que hay dentro


Para descubrir cómo ajustar una imagen para un tema oscuro, debemos mirar dentro y analizar su contenido, preferiblemente con un método rápido y simple. Aquí está la primera versión de un algoritmo que resuelve esto.


Primero cuente los píxeles oscuros, claros y transparentes de la imagen. Como una optimización obvia, solo se puede considerar un pequeño subconjunto de píxeles. Luego, determine el brillo general de la imagen (claro, oscuro o medio) y si hay alguna transparencia. Invierta las imágenes oscuras con transparencia, opaque las imágenes brillantes opacas y deje el resto sin cambios.


Estaba realmente feliz con este descubrimiento hasta que encontré un boletín de caridad con una foto de una lección en una escuela africana. El diseñador lo centró agregando píxeles transparentes a los lados. No queríamos involucrarnos en una historia sobre el reconocimiento de imágenes ofensivas, por lo que decidimos dejar la manipulación de imágenes fuera de nuestra primera iteración.


En el futuro, podemos prevenir tales problemas empleando esa heurística que llamo "análisis espectral". Es decir, contar el número de tonos diferentes en la imagen y solo invertir si no hay demasiados. Puede utilizar el mismo método para reconocer imágenes brillantes e incompletas e invertirlas también.



Conclusión


Para diseñar un tema oscuro completo, tuvimos que encontrar una manera de oscurecer los correos electrónicos que contienen formato personalizado, y lo hicimos. Primero, probamos dos soluciones CSS simples y puras: anular estilos y usar un filtro CSS. Ninguno de los dos dio en el blanco: el primero fue demasiado duro con el diseño original, y el otro simplemente no funcionó bien. Al final, decidimos usar el oscurecimiento adaptativo. Desmontamos los estilos, reemplazamos los colores y los volvemos a armar. Actualmente estamos trabajando en aplicar el tema oscuro a las imágenes. Para hacer eso, necesitamos analizar sus contenidos y luego solo ajustar algunos de ellos.


Si alguna vez necesita cambiar el color de los fragmentos HTML personalizados para adaptarlo a un tema oscuro, tenga en cuenta tres métodos:


  • Estilos primordiales. Es un método sin complicaciones, sin complicaciones, y de todos modos lo necesitará para su aplicación. La mala noticia es que destruye todos los colores originales.
  • Filtro CSS Es divertido pero deja mucho que desear. Resérvelo para elementos que no son fáciles de acceder, como marcos o componentes web.
  • Reescritura de estilo. Este método hace un gran trabajo al oscurecer, pero es más complejo.

Incluso si nunca intentas nada de esto, ¡espero que te lo hayas pasado genial leyendo este artículo!

Source: https://habr.com/ru/post/450032/


All Articles