Las propiedades personalizadas no solo nos permiten hacer nuestro código más eficiente, sino que también nos permiten hacer algo de magia real con CSS. Un área donde tienen enorme el potencial es el tema. En Atomic Smash usamos Tailwind CSS, un marco de clase de utilidad, para escribir nuestros estilos. En este artículo, veremos cómo se pueden usar las 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 con Tailwind; consulte la documentación oficial para ello, pero incluso si es nuevo en ello, es posible que algunos de estos consejos le resulten útiles.
Resumen de la temática
Digamos que tenemos un componente de “Llamada a la acción” (CTA) con un encabezado, texto de cuerpo y botón.
Escribir CSS regular (no 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 class="bg-red-900 text-white">
<h3 class="bg-red-600 text-red-900">Join our mailing list</h3>
<div>
<p>Be the first to hear about our new offerings</p>
<button class="bg-red-600" 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 puede 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 en sí y redefinir los valores de color más abajo en la cascada. Entonces, para un componente con una clase de modificador 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 color, y podríamos anidar el .cta__heading
y .cta__body
selectores. No hace exactamente que nuestro código sea más conciso, pero lo hace más manejable al tener un solo 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
Si usamos Tailwind, por otro lado, necesitaríamos cambiar varias clases en el propio HTML. Incluso si estamos usando 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 la concatenación de cadenas para los nombres de clases (en el 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 usar 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 también nos serviría). 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 siempre 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 ese .th-blue
clase al componente, o incluso utilizarlo en el <body>
para aplicar para aplicar un tema en toda la página, que se puede anular en componentes individuales según se desee. El uso de una clase de utilidad nos ahorra potencialmente escribir aún más código en comparación con una clase de componente específico (como .cta--blue
en el código original), ya que podría aplicarse en cualquier lugar de nuestra base de código.
Manejo de navegadores más antiguos
Como muchas agencias, muchos de nuestros clientes en Atomic Smash aún requieren que admitamos Internet Explorer 11. Aunque estoy de acuerdo con un enfoque de mejora progresiva en la mayoría de los casos (al proporcionar diseños de reserva más simples para navegadores que no admiten CSS Grid, por ejemplo), encuentro que la tematización 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 características 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 el polyfilling de propiedades personalizadas en IE 11.
postcss-propiedades-personalizadas
El primero es usar un complemento PostCSS llamado postcss-custom-properties. Si ya está utilizando PostCSS en su flujo de trabajo, esto es bastante simple de agregar. Funciona procesando su CSS y generando el resultado de la variable como valor de propiedad. Entonces, si tiene 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 volverá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 está actualizando 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 pueden solo ser definido en el
:root
selector.
La primera limitación es relativamente trivial, pero la segunda desafortunadamente hace que este polyfill sea completamente inútil para nuestro caso de uso de temas. Significa que no podemos redefinir variables en un selector para crear nuestros temas.
ie11CustomProperties
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 nuestra cabeza para asegurarnos de que el polyfill solo se cargue en IE 11:
<script>window.MSInputMethodContext && document.documentMode && document.write('<script src="https://cdn.jsdelivr.net/gh/nuxodin/[email protected]/ie11CustomProperties.min.js"></script>');</script>
Esto nos permite disfrutar de todos los beneficios de las propiedades personalizadas como en los ejemplos aquí, así que es la solución que decidí utilizar. Tiene una limitación donde las propiedades personalizadas se establecen en style
los atributos no se rellenan con polietileno. Pero lo he probado para el ejemplo de temática anterior y funciona bien.
Pero, ¿qué tiene esto que ver con Tailwind?
Como ya hemos visto, las clases de utilidad (clases de un solo propósito 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 marcos de clase de servicios públicos: el tamaño del archivo CSS que envía debería terminar siendo más pequeño como resultado. Tailwind pone a disposición varias clases de colores: .bg-red-medium
nos daría un rojo background-color
El valor de la propiedad, .text-red-medium
por color
y así sucesivamente para border
, box-shadow
, o cualquier lugar en el que se te ocurra 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 usar valores de propiedad personalizados para nuestras clases Tailwind, podemos especificarlos en la configuración:
module.exports = {
theme: {
colors: {
'th-primary': 'var(--primary)',
'th-secondary': 'var(--secondary)'
}
}
}
Estoy prefijando mis colores y nombres de clases relacionados con el tema con th-
para que sea obvio que están específicamente relacionados con la tematización, pero puedes usar cualquier convención que te convenga.
Ahora esas clases estarán disponibles para nosotros a través de Tailwind. Utilizando .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 da un componente con nuestro tema predeterminado (las variables definidas en: root). El segundo tiene nuestro tema azul. La única diferencia es la adición del .th-blue
clase en el componente. (Una vez más, he omitido las clases no relacionadas con el tema, por brevedad y claridad).
<!--Component with default (red) theme-->
<div class="bg-th-primary">
<h3 class="bg-th-secondary text-th-primary">Join our mailing list</h3>
<div>
<p>Be the first to hear about our new offerings</p>
<button class="bg-th-secondary" type="button">Sign up</button>
</div>
</div>
<!--Component with blue theme-->
<div class="th-blue bg-th-primary">
<h3 class="bg-th-secondary text-th-primary">Join our mailing list</h3>
<div>
<p>Be the first to hear about our new offerings</p>
<button class="bg-th-secondary" type="button">Sign up</button>
</div>
</div>
Usando la configuración como guía de estilo
Tailwind te anima 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 (potencialmente) terminar con varios lugares para definir sus colores y otros valores del tema. Afortunadamente, también podemos usar valores del archivo de configuración Tailwind para nuestras propiedades personalizadas. Primero tendremos que 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 de 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 con el navegador, más aún para poder integrarlas sin problemas con nuestro flujo de trabajo existente. Es difícil exagerar la cantidad de tiempo que nos ahorrarán para tematizar. Espero que, incluso si no eres un usuario de Tailwind, este artículo te anime a probar propiedades personalizadas en este caso de uso.