Cómo crear un tema oscuro y no dañar. Yandex.Mail Team Experience


Mi nombre es Vladimir, estoy involucrado en una interfaz móvil en Yandex. Correo. En nuestra aplicación, ya había un tema oscuro, pero no lo suficiente: pudimos volver a pintar la interfaz y las letras simples. Pero las letras formateadas seguían siendo claras y contrastaban con una interfaz oscura, lo que podía cansar mis ojos por la noche.


Hoy les diré a los lectores de Habr cómo resolvimos este problema. Aprenderá acerca de dos métodos simples que no nos convenían, entonces: sobre nuestra forma principal de repintado adaptativo de páginas y, finalmente, sobre la dirección para la próxima iteración: repintar imágenes. Aunque la tarea en sí misma - repintar páginas con formato arbitrario - es específica, creo que nuestra experiencia también será útil para usted.


Formas simples


Antes de llegar a nuestro "repainter" mágico, probamos dos opciones simples como un corcho: colgar un estilo oscuro adicional o un filtro CSS en el elemento. No nos quedaron bien, pero tal vez en algunos casos serán aún mejores (porque es simplemente genial).


Anular estilos


La forma más simple, expandiendo lógicamente el tema oscuro de la aplicación en CSS: colgaremos estilos oscuros en un contenedor para letras (en el caso general, para el contenido de otra persona que necesita ser repintado):


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

Pero si los elementos dentro de la letra tienen sus propios estilos, redefinirán nuestro estilo raíz. !important No !important no ayudará. Puede exprimir una idea cortando la herencia:


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

En este caso, no puede prescindir de !important , porque el selector en sí no es muy específico. Además, será necesario redefinir los estilos en línea (¡y los estilos en línea con !important se arrastrará de todos modos, no hay nada que hacer).


Nuestro estilo es bastante torpe y los colores son iguales, por lo que surge otro problema: probablemente el diseñador quería decir algo organizando los colores (prioridades de los elementos y otras cosas del diseñador), pero tomamos y desechamos toda esta idea.



Si respetas a los diseñadores menos que yo y sigues decidiendo usar este método, no olvides terminar los pequeños detalles:


  • box-shadow : solo el color no se puede redefinir; debes eliminar todas las sombras o vivir con las claras.
  • Colores de elementos semánticos: enlaces, elementos de entrada.
  • SVG en línea: en lugar de background , deben establecer el fill y, en lugar del stroke de color , pero esto no es preciso, dependiendo de qué SVG puede ser, y viceversa.

Técnicamente, el método no es malo: estas son tres líneas de código (bueno, treinta para la versión redi de producción con casos de esquina), compatibilidad con todos los navegadores del mundo, procesamiento de páginas dinámicas fuera de la caja y sin vinculación a la forma de conectar estilos en el documento original. Una ventaja especial es que puede ajustar fácilmente los colores en el estilo para que se ajusten a la aplicación principal (por ejemplo, haga el fondo #bbbbb8 lugar del negro).


Por cierto, solíamos volver a pintar las letras de esta manera, pero si encontramos algún estilo dentro de la letra, nos asustamos y dejamos la letra clara.


Filtro CSS


Opción muy ingeniosa y elegante. Puede volver a pintar la página con un filtro CSS:


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

Después de esto, las fotos se volverán espeluznantes, pero no importa, las volveremos a pintar:


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


Todavía hay problemas con las imágenes de contenido vinculadas a través del background (sabemos que es más conveniente ajustar la relación de aspecto, pero ¿qué pasa con la semántica?). Supongamos que podemos encontrar todos esos elementos, marcarlos explícitamente y volver a pintarlos.


El método es bueno porque conserva la relación original de brillo y contrastes. Por otro lado, hay muchos problemas y superan con creces las ventajas:



  1. Las páginas oscuras se aclaran.
  2. Los colores resultantes no se pueden controlar: ¿qué filtro aplicar para ajustar el fondo a su #bbbbb8 corporativo? El acertijo.
  3. Después de dos repintado, las imágenes se desvanecen.
  4. Todo se ralentiza (especialmente en los teléfonos): es lógico, ahora en lugar de una simple representación, el navegador necesita impulsar el procesamiento de imágenes en cada pantalla.

Este método sería adecuado para letras que consisten en texto en tonos neutros, pero ¿quiénes son los estetas que obtienen una bandeja de entrada completa de un contenido tan peculiar? Pero los filtros pueden volver a pintar elementos cuyos contenidos no son accesibles: marcos, componentes web, imágenes.


Tema receptivo


¡Es hora de la magia! De las deficiencias de los dos primeros enfoques, recopilamos una lista de verificación:


  1. Oscurezca el fondo, el texto sea claro, los bordes sean medios.
  2. Identifique páginas oscuras y no las vuelva a pintar.
  3. Mantenga la proporción original de brillo y contraste.
  4. Da la posibilidad de personalizar los colores.
  5. Deja los tonos como estaban al principio.

Necesitamos cambiar los colores de los estilos para que el fondo sea oscuro. ¿Y por qué no hacerlo literalmente? Simplemente tomamos todos los estilos, buscamos las reglas asociadas con los colores ( color , background , border , box-shadow , sus sub-propiedades), y lo reemplazamos con "oscurecido": oscurece el fondo, aclara el texto, oscurece los bordes menos que el fondo, etc.


Este método tiene una ventaja increíble que calentará el alma de cualquier desarrollador. Cada propiedad se puede configurar (sí, ¡describa directamente con un código!) Sus propias reglas para la conversión de color. Con suficiente imaginación, puede integrarse con cualquier tema externo, hacer cualquier corrección de color (por ejemplo, hacer un color claro o gris-marrón-frambuesa en lugar de un tema oscuro) e incluso agregar un pequeño contexto, por ejemplo, manejar los bordes anchos y estrechos de manera diferente.


Las desventajas son estándar para todo en js. Sí, ejecutamos scripts, rompemos la encapsulación de estilos y analizamos expresiones regulares CSS. Bueno, a diferencia de HTML, este último no es tan vergonzoso porque la gramática CSS (del nivel que necesitamos) sigue siendo regular.


El plan de repintado es el siguiente:


  1. Normalizamos las propiedades heredadas del estilo ( bgcolor y amigos), las cambiamos a style="..." .
  2. Encuentra todos los estilos en línea.
  3. En cada estilo encontramos todas las reglas de color (color de background-color , color , box-shadow , etc.).
  4. De todas las reglas de color, obtenemos los colores, encontramos el convertidor deseado (más oscuro para el fondo, clarificador para el texto).
  5. Llamamos al convertidor.
  6. Poner las reglas convertidas nuevamente en CSS.

El enlace (normalización, búsqueda de estilo, análisis) es bastante simple. Descubriremos cómo funciona exactamente nuestro convertidor mágico.


Conversiones HSL


"Atenuar el color" no es una acción tan simple como podría parecer, especialmente si queremos mantener el tono (el cian se vuelve azul oscuro, no naranja). Esto se puede hacer en RGB normal, pero problemático. Los fanáticos del diseño algorítmico saben que incluso los gradientes allí están torcidos. Pero trabajar con colores en HSL es un puro placer: en lugar de rojo, verde y azul, con el que no está claro qué hacer, tenemos otros tres canales:


  • Hue es el tono que queremos mantener.
  • Saturación - saturación, que no es muy importante para nosotros ahora.
  • Luminosidad: el brillo que cambiaremos.

Es conveniente imaginar tal espacio en forma de cilindro. Y nuestra tarea es poner este cilindro al revés. Las funciones de gradación de color hacen algo como (h, s, l) => [h, s, 1 - l] .


Los colores con los que todo es bueno


A veces la situación es exitosa: el diseño exclusivo de la letra (o parte de ella) ya está oscuro. En este caso, no necesita cambiar nada, es mejor simplemente ser silenciosamente feliz, probablemente el diseñador eligió los colores no peor que nuestro algoritmo. En HSL, solo mire L - brillo. Si es más alto (para texto) o más bajo (para fondo) el umbral (que, por supuesto, es personalizable), no hacemos nada.


Circo dinámico


Aunque no lo necesitábamos (gracias de nuevo, desinfectante, ¡me salvaste de la locura!), Aún te diré qué tipo de complementos necesita un tema adaptativo para oscurecer las páginas completas, y no solo estúpidas letras estáticas de los noventa. Más exactamente, esta es una tarea para aquellos a quienes les gusta el olor de los selectores en la mañana.


Estilos dinámicos en línea


El caso más simple que rompe nuestra página oscura es cambiar los estilos en línea. La operación es frecuente, pero la solución es simple: agregue MutationObserver y repare rápidamente los estilos en línea al cambiar.


Estilos externos


Trabajar con estilos desde <link> desde el interior de la página es bastante doloroso debido a la asincronía y a @import , y CORS no es más divertido. Parece que este problema podría resolverse de manera elegante a través de un trabajador web (proxy para *.css ).


Estilos dinámicos


Finalmente, juntando todos nuestros problemas, recordamos que el script generalmente puede agregar, eliminar y reorganizar (¡especificidad! ¡Cascada!) <style> y <link> , e incluso cambiar las reglas en <style> . El mismo MutationObserver resuelve todo para los elementos de estilo, pero para cada cambio hay más procesamiento.


Variables CSS


Una nueva ronda de locura llega cuando las variables CSS ingresan al juego. No podemos ocultar las variables en sí mismas: incluso si suponemos que adivinaremos por el formato que la variable contiene color (aunque no le aconsejaría que hiciera esto), no se sabe en qué papel nos encontrará: ¿fondo, texto, borde, todo de una vez? Además, los valores de las variables se heredan, por lo que debemos considerar no solo los estilos, sino también los elementos a los que se aplican, y todo esto aumenta y explota rápidamente.


Si las variables CSS llegan a la corriente principal, tenemos un problema. Por otro lado, para ese momento, color() ya se habrá iniciado, con lo cual será posible no cambiar los colores en JS, sino simplemente reemplazar los colores con color(var(--bg) lightness(-50%)) .


Resumen



Para nuestro caso, cuando el desinfectante deja solo estilos en línea, la atenuación adaptativa a nivel de CSS funciona bien: brinda la mejor calidad de atenuación, no rompe letras y funciona de manera relativamente rápida y fácil. No estoy seguro si la opción con todo el relleno para la dinámica vale la pena. Afortunadamente, si trabaja con contenido generado por el usuario y no escribe un navegador, su desinfectante debería hacer lo mismo.


En la práctica, el modo adaptativo debe usarse junto con la redefinición de estilos: los estilos generalmente no se aplican explícitamente a elementos estándar como <input> o <a> , pero por defecto son ligeros.


Cómo oscurecer imágenes


Volver a pintar imágenes es un tema aparte que me molesta personalmente. Esto es interesante, y finalmente tengo la oportunidad de usar la frase "análisis espectral". Existen varios problemas comunes con las imágenes en un sujeto oscuro.


En primer lugar, las imágenes son demasiado claras. Funciona de la misma manera que las letras sin pintar con las que todo comenzó. A menudo (pero no necesariamente) estas son fotografías ordinarias. Dado que el diseño de los boletines no es muy divertido, muchos chicos simplemente exportan la parte difícil de la carta como una imagen, no se repinta y por la noche ilumina mi perfeccionismo. Tales imágenes deben oscurecerse, pero no invertirse; de ​​lo contrario, saldrá un terrible negativo.



En segundo lugar, imágenes oscuras con transparencia real. Este problema a menudo se encuentra en los logotipos: están diseñados para un fondo claro y, cuando lo reemplazamos por uno oscuro, se fusionan con él. Tales imágenes deben invertirse.



En algún lugar en el medio hay imágenes para las cuales el blanco representaba un "fondo transparente", pero ahora se encuentran en un extraño rectángulo blanco. En un mundo ideal, reemplazaríamos un fondo blanco por uno transparente, pero si alguna vez has trabajado con una varita mágica en un editor de fotos, entonces sabes que hacer esto automáticamente no es tan fácil.



Es interesante que a veces las imágenes no tengan ningún significado: se trata de píxeles de seguimiento y "soportes de formato" en un diseño particularmente perverso. Estos pueden hacerse invisibles de forma segura (por ejemplo, opacity: 0 ).



Heurística de introspección


Para decidir qué hacer con la imagen, necesitamos entrar y analizar su contenido, de manera simple y rápida. Según nuestro conjunto de problemas, la primera versión del algoritmo se avecina. Ahí está ella.


Consideramos píxeles oscuros, claros y transparentes en la imagen, y no todos, pero de forma selectiva: optimización obvia. Determinamos el brillo general de la imagen (claro, oscuro, medio) y la presencia de transparencia. Invierta imágenes oscuras con transparencia, luz sin transparencia: silencio, no toque el resto.


La alegría de esta maravillosa heurística terminó cuando me encontré con un boletín de caridad con una foto de una lección en una escuela africana. Todo estaría bien, pero el diseñador lo centró, agregando píxeles transparentes a lo largo de los bordes. No queríamos encontrarnos en el centro de una nueva historia sobre el reconocimiento ofensivo de imágenes, y decidimos no hacer ningún procesamiento de imágenes en la primera versión.


En el futuro, las heurísticas adicionales, que simplemente llamo "análisis espectral", deberían proteger contra tales problemas: contamos el número de colores diferentes en la imagen e invertimos solo si hay pocos. El mismo criterio se puede utilizar para buscar imágenes gráficas claras y volver a pintarlas, suena tentador.



Resumen


Para un tema oscuro completo en el correo, nos faltó volver a pintar las letras con estilos, y descubrimos cómo organizarlo. Dos opciones simples en CSS puro - redefinir estilos y un filtro CSS - no funcionaron: la primera es demasiado dura en el diseño original, la segunda simplemente no funciona bien. Como resultado, utilizamos atenuación adaptativa: analizamos estilos, reemplazamos colores por otros más adecuados y los recuperamos. Ahora estamos trabajando para expandir el tema en imágenes, para esto necesitamos analizar sus contenidos y volver a pintar solo unos pocos.


Si alguna vez necesita volver a pintar HTML personalizado a un tema oscuro, tenga en cuenta tres métodos:


  • Estilos sobresalientes: de todos modos, lo necesita para su aplicación principal, de forma económica y enojada, pero elimina todos los colores originales.
  • El filtro CSS es genial, pero funciona más o menos. Úselo solo para elementos opacos (en términos de acceso) como marcos o componentes web.
  • Conversión de estilos: oscurece la calidad muy alta, pero es más complicada que otros métodos.

Incluso si nunca haces esto, ¡espero que te hayas divertido!


Enlaces utiles :


  1. Si está interesado en discutir este tema en vivo y en el contexto de desarrollo para Android, lo invitamos a visitar la oficina de Yandex en Petersburgo el 18 de abril.


  2. Recientemente, hablamos sobre resolver otro problema de los usuarios de correo: los problemas de correo.


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


All Articles