Temas de color con propiedades personalizadas de CSS y Tailwind
Las propiedades personalizadas no solo nos permiten hacer que nuestro código sea más eficiente, sino que también nos permiten hacer algo de magia real con CSS. Un área en la que tienen un enorme potencial es la tematización. En Atomic Smash utilizamos Tailwind CSS, un marco de clase de utilidad, para escribir nuestros estilos. En este artículo, veremos cómo se pueden usar propiedades personalizadas para la creación de temas y cómo podemos integrarlas con Tailwind para maximizar la reutilización de nuestro código. No cubriremos la puesta en marcha de Tailwind (consulte la documentación oficial para ello), pero incluso si es nuevo en esto, es posible que algunos de estos consejos le resulten útiles.
Descripción general de la temática
Digamos que tenemos un componente de "Llamado a la acción" (CTA) con un encabezado, un cuerpo de texto y un botón.
Escribir CSS normal (que no sea Tailwind) para este esquema de color se vería así:
.cta { background-color: #742a2a; // dark red color: #ffffff; //white} .cta__heading { background-color: #e53e3e; // medium red color: #742a2a;}
.cta__button { background-color: #e53e3e;}
Usando Tailwind, aplicaríamos estos colores como clases de utilidad en nuestro HTML:
div h3Join our mailing list/h3 div pBe the first to hear about our new offerings/p button type="button"Sign up/button /div/div
He omitido deliberadamente clases relacionadas con cualquier otra cosa que no sea el esquema de color básico, pero puedes ver el ejemplo en su totalidad en esta demostración:
Ahora, si quisiéramos aplicar un esquema de color diferente a nuestro componente, necesitaríamos anular los valores de color de nuestro componente original. Sin Tailwind, una forma común de hacerlo sería agregar una clase de tema al componente mismo y redefinir los valores de color más abajo en la cascada. Entonces, para un componente con una clase modificadora de .cta--blue
(usando la convención BEM), aplicaremos los valores CSS para un esquema de color azul:
.cta--blue { background-color: #2a4365; // dark blue}
.cta--blue .cta__heading { background-color: #3182ce; // medium blue color: #2a4365;}
.cta--blue .cta__button { background-color: #3182ce;}
Si usamos Sass u otro preprocesador, es probable que nos hagamos la vida más fácil usando variables para esos nombres de colores, y podríamos anidar los selectores .cta__heading
y . .cta__body
No hace exactamente que nuestro código sea más conciso, pero sí lo hace más manejable al tener un único lugar para actualizar esos valores.
Ahora, supongamos que tenemos 10 combinaciones de colores diferentes, como fue mi experiencia en un proyecto reciente. Nuestro código comienza a alargarse, ya que básicamente estamos duplicando el ejemplo anterior 10 veces para cambiar esos valores de color. Ahora imagine que cada componente de nuestro sistema de diseño necesita 10 combinaciones de colores, y muchos de esos componentes son mucho más complejos que nuestro simple CTA. Quizás nuestros temas también necesiten fuentes diferentes. De repente tenemos mucho CSS para escribir.
Tematización con Tailwind
Por otro lado, si usamos Tailwind, necesitaríamos cambiar varias clases en el propio HTML. Incluso si usamos un marco de JavaScript, como React o Vue, esta no es exactamente una tarea trivial. Para garantizar que los estilos no utilizados se eliminen en una compilación de producción, Tailwind desaconseja el uso de concatenación de cadenas para nombres de clases (al momento de escribir este artículo). Entonces, construir nuestros temas significa potencialmente acumular mucha lógica en nuestros componentes.
Tematización con propiedades personalizadas
Al utilizar propiedades personalizadas para nuestros temas de color, podemos reducir drásticamente la cantidad de código que necesitamos escribir y aliviar la carga de mantenimiento. Primero echemos un vistazo a cómo podemos hacer esto en CSS normal.
Definimos nuestras propiedades personalizadas como variables en el selector :root, convirtiéndolas en variables globales. (El selector de cuerpo nos serviría igual de bien.) Luego podemos usar esas variables en un selector, en lugar de nuestros valores de propiedad de color:
:root { --primary: #742a2a; // dark red; --secondary: #e53e3e; // medium red}
.cta { background-color: var(--primary); color: white;}
.cta__heading { background-color: var(--secondary); color: var(--primary);}
.cta__button { background-color: var(--secondary);}
Aquí es donde ocurre la verdadera magia: ahora el código para crear cada uno de nuestros temas se convierte en un caso de solo actualizar esos valores de propiedad personalizados. Los nuevos valores se heredarán dondequiera que apliquemos nuestra clase de tema:
.th-blue { --primary: #2a4365; // dark blue --secondary: #3182ce; // medium blue}
Si queremos un esquema de color azul, podemos aplicar esa .th-blue
clase al componente, o incluso usarla en la body
etiqueta para aplicar un tema para toda la página, que se puede anular en componentes individuales según se desee. El uso de una clase de utilidad potencialmente nos ahorra escribir aún más código en comparación con una clase específica de componente (como .cta--blue
en el código original), ya que podría aplicarse en cualquier parte de nuestro código base.
Manejo de navegadores antiguos
Como muchas agencias, muchos de nuestros clientes en Atomic Smash todavía requieren que admitamos Internet Explorer 11. Si bien estoy de acuerdo con un enfoque de mejora progresiva en la mayoría de los casos (proporcionando diseños alternativos más simples para navegadores que no admiten CSS Grid, por ejemplo). Por ejemplo), creo que la temática es un área que a menudo no permite un compromiso fácil. Los clientes quieren que se vean los colores y fuentes de su marca, incluso en navegadores más antiguos. Proporcionar alternativas mediante consultas de funciones implicaría mucho trabajo adicional que anularía los beneficios de usar propiedades personalizadas en primer lugar. Para superar esto, necesitamos un polyfill.
Hay un par de opciones para rellenar propiedades personalizadas en IE 11.
propiedades-personalizadas-postcss
El primero es utilizar un complemento PostCSS llamado postcss-custom-properties. Si ya está utilizando PostCSS en su flujo de trabajo, agregarlo es bastante sencillo. Funciona procesando su CSS y generando el resultado de la variable como valor de propiedad. Entonces, si tienes el siguiente CSS:
:root { --color: red;}
h1 { color: var(--color);}
El resultado procesado será:
h1 { color: red; color: var(--color);}
Los navegadores que no admiten propiedades personalizadas ignorarán la segunda regla y recurrirán al valor de propiedad normal. También hay una opción para eliminar las reglas con las propiedades personalizadas en la salida, por lo que el tamaño del archivo será menor. Esto significa que ningún navegador obtendrá la propiedad personalizada, lo cual es un problema si actualiza las variables dinámicamente, pero podrá usarlas para valores estáticos en su código sin efectos nocivos.
Desafortunadamente este polyfill tiene algunas limitaciones:
- Debe especificar el archivo (o archivos) en su configuración donde está definiendo las propiedades personalizadas.
- Las propiedades personalizadas solo se pueden definir en el
:root
selector.
La primera limitación es relativamente trivial, pero desafortunadamente la segunda hace que este polyfill sea completamente inútil para nuestro caso de uso temático. Significa que no podemos redefinir variables en un selector para crear nuestros temas.
ie11Propiedades personalizadas
Esta opción de polyfill implica servir un script del lado del cliente, en lugar de preprocesar el CSS. Podemos agregar el siguiente script a nuestro encabezado para asegurarnos de que el polyfill solo se cargue en IE 11:
scriptwindow.MSInputMethodContext document.documentMode document.write('script src="https://cdn.jsdelivr.net/gh/nuxodin/ie11CustomProperties@4.1.0/ie11CustomProperties.min.js"/script');/script
Esto nos permite disfrutar de todos los beneficios de las propiedades personalizadas como en los ejemplos aquí, por lo que es la solución que decidí utilizar. Tiene una limitación en la que las propiedades personalizadas establecidas en style
los atributos no se rellenan en forma polivalente. Pero lo probé para el ejemplo temático anterior y funciona bien.
¿Pero qué tiene esto que ver con Tailwind?
Como ya hemos visto, las clases de utilidad (clases de propósito único que se pueden aplicar en cualquier parte de nuestro HTML) pueden hacer que nuestro código sea más reutilizable. Ese es el principal punto de venta de Tailwind y otros frameworks de clase de utilidad: como resultado, el tamaño del archivo CSS que envía debería ser más pequeño. Tailwind pone a disposición varias clases de color: .bg-red-medium
nos daría un background-color
valor de propiedad rojo, .text-red-medium
para color
y así sucesivamente para border
, box-shadow
o cualquier lugar que se le ocurra donde pueda necesitar un valor de color.
Los colores se pueden definir en un archivo de configuración:
module.exports = { theme: { colors: { red: { medium: '#e53e3e', dark: '#742a2a' }, blue: { medium: '#3182ce', dark: '#2a4365' } } }}
Si queremos utilizar valores de propiedad personalizados para nuestras clases de Tailwind, podemos especificarlos en la configuración:
module.exports = { theme: { colors: { 'th-primary': 'var(--primary)', 'th-secondary': 'var(--secondary)' } }}
Estoy anteponiendo mis colores y nombres de clases relacionados con el tema th-
para que sea obvio que están específicamente relacionados con la temática, pero siéntete libre de usar la convención que más te convenga.
Ahora esas clases estarán disponibles para nosotros a través de Tailwind. Usar .bg-th-primary
nos da el equivalente a escribir:
.some-element { background-color: var(--primary);}
En nuestro CSS podemos definir nuestras propiedades personalizadas para nuestros temas como antes:
:root { --primary: #742a2a; --secondary: #742a2a;}
.th-blue { --primary: #2a4365; --secondary: #3182ce;}
Apliquemos esas clases a nuestro HTML. El primer ejemplo nos proporciona un componente con nuestro tema predeterminado (las variables definidas en :root). El segundo tiene nuestro tema azul. La única diferencia es la adición de la .th-blue
clase al componente. (Una vez más, he omitido las clases que no están relacionadas con el tema, por motivos de brevedad y claridad).
!--Component with default (red) theme--div h3Join our mailing list/h3 div pBe the first to hear about our new offerings/p button type="button"Sign up/button /div/div
!--Component with blue theme--div h3Join our mailing list/h3 div pBe the first to hear about our new offerings/p button type="button"Sign up/button /div/div
Usando la configuración como guía de estilo
Tailwind lo alienta a definir todas las variables en la configuración y, personalmente, estoy de acuerdo en que es un mejor enfoque. Significa que el archivo de configuración puede ser una única fuente de verdad en lugar de terminar (potencialmente) con múltiples lugares para definir los colores y otros valores del tema. Afortunadamente, también podemos usar valores del archivo de configuración de Tailwind para nuestras propiedades personalizadas. Primero necesitaremos definir todos nuestros colores en la configuración (asumiendo que no estamos usando la paleta de colores predeterminada incluida con Tailwind):
module.exports = { theme: { colors: { red: { medium: '#e53e3e', dark: '#742a2a' }, blue: { medium: '#3182ce', dark: '#2a4365' }, 'th-primary': 'var(--primary)', 'th-secondary': 'var(--secondary)' } }}
Luego podemos acceder al objeto del tema en el CSS:
:root { --primary: theme('colors.red.dark'); --secondary: theme('colors.red.medium');}
.th-blue { --primary: theme('colors.blue.dark'); --secondary: theme('colors.blue.medium');}
Terminando
Estoy realmente entusiasmado con los beneficios de poder usar propiedades personalizadas sin tener que preocuparme por la compatibilidad del navegador, más aún de poder integrarlas sin problemas con nuestro flujo de trabajo existente. Es difícil exagerar la cantidad de tiempo que nos ahorrarán para la tematización. Espero que incluso si no eres usuario de Tailwind, este artículo te anime a probar propiedades personalizadas para este caso de uso.
Deja un comentario