Si eres como yo, probablemente te fascinan algunas características de tu framework CSS favorito, que en mi caso es el sistema de utilidades de TailwindCSS, pero también consideras un poco excesivo incluirlo en un proyecto ya avanzado solo por unas cuantas clases.
Por otra parte no solo se trata de TailwindCSS, en realidad personalmente no me gusta usar frameworks en proyectos que ya tengo desarrollados y pensados, ya que en lugar de ahorrarme tiempo, me lo quitan, porque añaden una capa de abstracción que me impide entender lo que está pasando, es decir, mi código deja de ser mío.
Cabe aclarra que TailwindCss es un framework que me gusta bastante pero no lo uso en todos mis proyectos y mis razones van alineadas con las expuestas por un articulo en Smashing de Josh Comeau, un desarrollador que admiro bastante.
Situación
En TailwindCSS, las utilidades se componen de una o más palabras separadas por guiones, que por lo general son abreviaciones de las propiedades CSS que modifican, por ejemplo:
<p class="text-center text-2xl text-red-500">Hola mundo</p>
Otra de las características de las utilidades de TailwindCSS son los prefijos, que permiten aplicar clases solo en ciertos contextos, en este caso, los que afectan a distintos tamaños de pantalla:
<p class="text-center text-2xl text-red-500 md:text-left lg:text-right">Hola mundo</p>
Esto es muy útil, pero también es muy fácil de implementar sin necesidad de usar TailwindCSS, y es lo que vamos a ver a continuación.
Funciones necesarias
Para empezar vamos a necesitar un par de funciones que nos ayuden a constuir este generador. Primero necesitamos la capacidad de buscar y reemplazar una cadena:
@use "sass:string";
// Replace `$search` with `$replace` in `$string`
// @author Hugo Giraudel
// @param {String} $string - Initial string
// @param {String} $search - Substring to replace
// @param {String} $replace ('') - New value
// @return {String} - Updated string
@function str-replace($string, $search, $replace: "") {
$index: string.index($string, $search);
@if $index {
@return string.slice($string, 1, $index - 1) + $replace + str-replace(string.slice($string, $index + string.length($search)), $search, $replace);
}
@return $string;
}
Otra función muy importante es la que nos permite escapar caracteres especiales en una cadena, para que el código CSS siempre se interprete correctamente:
@use "sass:meta";
// Escape de caracteres en las clases
// @author zkreations
// @param {String} $string - String to escape
// @return {String} - Escaped string
@function escapeClass($string) {
$escaped-class: (
(".", "\\."),
(":", "\\:"),
("/", "\\/"),
);
@if meta.type-of($string) == "number" {
$string: #{$string};
}
@each $key, $value in $escaped-class {
$string: str-replace($string, $key, $value);
}
@return $string;
}
Con estas dos funciones ya podemos construir el generador de utilidades.
Generador de utilidades con Sass/SCSS
Este mixin espera recibir una propiedad CSS, una lista de valores y un booleano (opcional) que sirve para generar las variantes responsive. Con esto genera las clases y los media queries necesarios para que las utilidades funcionen correctamente.
@use "sass:list";
@use "sass:meta";
// Generador de utilidades
// @author zkreations
// @param {String} $property - CSS property
// @param {List} $values - List of values
// @param {Boolean} $responsive (false) - Responsive utilities
// @return {Mixin} - Utility
@mixin utility($property, $values, $responsive: false) {
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
);
$utility: if(sass(& != null): str-replace(#{&}, ".") + "-"; else: "");
@if meta.type-of($values) !="map" {
$values: list.zip($values, $values);
}
@each $key,
$value in $values {
$suffix: str-replace(escapeClass($key), "rem");
$class: $utility + $suffix;
@at-root .#{$class} {
#{$property}: $value;
}
}
@if $responsive {
@each $breakpoint,
$size in $breakpoints {
$prefix: escapeClass($breakpoint + ":");
@media (width >= $size) {
@each $key,
$value in $values {
$suffix: str-replace(escapeClass($key), "rem");
$class: $prefix + $utility + $suffix;
@at-root .#{$class} {
#{$property}: $value;
}
}
}
}
}
}
Puedes configurar la lista de breakpoints segun tus preferencias o tambien puedes reemplazarla completamente con tus propios breakpoints llamados desd las variables de tu proyecto. Recuerda que la clave de cada elemento de la lista, es el prefijo que se usará en las clases responsive.
Modo de uso
La forma mas fácil de usarlo es incluyendo directamente el mixin con la propiedad y los valores que queremos generar, recuerda que una utilidad se crea por cada valor que le pasemos en la lista:
@include utility(position, absolute relative fixed sticky);
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.fixed {
position: fixed;
}
.sticky {
position: sticky;
}
Si queremos que las utilidades sean responsive, solo tenemos que pasar un tercer argumento con el valor true, por ejemplo:
@include utility(text-transform, capitalize, true);
.capitalize {
text-transform: capitalize;
}
@media (width >= 576px) {
.sm\:capitalize {
text-transform: capitalize;
}
}
@media (width >= 768px) {
.md\:capitalize {
text-transform: capitalize;
}
}
@media (width >= 992px) {
.lg\:capitalize {
text-transform: capitalize;
}
}
Si el valor de la propiedad son numeros lo mejor es utilizar un selector e inclui el mixin dento. Tambien es util si deseas que la utilidad siga una convención de nombres, por ejemplo:
.p {
@include utility(padding, 1rem 2rem 3rem);
}
.p-1 {
padding: 1rem;
}
.p-2 {
padding: 2rem;
}
.p-3 {
padding: 3rem;
}
Como alternativa, también puedes pasar una lista para decidir las clases y sus valores correspondientes que resultarán al compilar:
.items {
@include utility(align-items, (
start: flex-start,
end: flex-end,
center: center,
));
}
.items-start {
align-items: flex-start;
}
.items-end {
align-items: flex-end;
}
.items-center {
align-items: center;
}
Conclusión
Con dos funciones y un mixin hemos construido muy fácil un generador de utilidades para Sass inspirado en TailwindCSS y no necesitas un framework para hacerlo, por supuesto aquí solo hemos construido algo para solventar 2 de las características de TailwindCSS, no es un reemplazo ni mucho menos. Si tienes alguna duda o sugerencia, déjame un comentario, estaré encantado de leerte.