No necesitas TailwindCSS si haces esto

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 tu proyecto solo por unas cuantas clases.

Por otra parte no solo se trata de TailwindCSS, de hecho a mi no me gusta usar frameworks, 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.

Un articulo en Smashing de Josh Comeau (un desarrollador que admiro bastante) explica muy bien esto, y me hizo reflexionar sobre el tema en su momento.

Situación

En TailwindCSS, las clases 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, que será el reemplazo de las utilidades de TailwindCSS. Primero necesitamos la capacidad de buscar y reemplazar una cadena:

// 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: str-index($string, $search);

  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-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:

// Escape de caracteres en las clases
// @author Daniel Abel
// @param {String} $string - String to escape
// @return {String} - Escaped string
@function escapeClass($string) {
  $escaped-class: (
    (".", "\\."),
    (":", "\\:"),
    ("/", "\\/"),
  );

  @if 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.

// Generador de utilidades
// @author Daniel Abel
// @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) {
  $utility: if(& !=null, str-replace(#{&}, ".") + "-", "");

  @if type-of($values) !="map" {
    $values: 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 (min-width: $size) {

        @each $key,
        $value in $values {
          $suffix: str-replace(escapeClass($key), "rem");
          $class: $prefix + $utility + $suffix;

          @at-root .#{$class} {
            #{$property}: $value;
          }
        }
      }
    }
  }
}

Por último es muy importante agregar la lista de breakpoints a tu archivo de configuración, en donde se encuentran las variables de tu proyecto, asi podrás decidir cuales son los tamaños de pantalla que quieres usar en tus utilidades.

// Breakpoints
$breakpoints: (
  sm: 576px,
  md: 768px,
  lg: 992px,
);

Recuerda que la clave de cada elemento de la lista, es el prefijo que se usará en las clases responsive, por ejemplo, si usamos sm como clave, las clases responsive se verán así: sm:clase.

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 (min-width: 576px) {
  .sm\:capitalize {
    text-transform: capitalize;
  }
}

@media (min-width: 768px) {
  .md\:capitalize {
    text-transform: capitalize;
  }
}

@media (min-width: 992px) {
  .lg\:capitalize {
    text-transform: capitalize;
  }
}

Si deseas que la utilidad siga una convención de nombres, para ello incluye el mixin dentro de un selector, por ejemplo:

.fw {
  @include utility(font-weight, 500 600 700);
}
.fw-500 {
  font-weight: 500;
}

.fw-600 {
  font-weight: 600;
}

.fw-700 {
  font-weight: 700;
}

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

Es muy fácil crear tus propias utilidades y no necesitas un framework para hacerlo, por supuesto aquí solo hemos construido algo para solventar 2 de las características de TailwindCSS, pero este mixin puede ser extendido y te animo a hacerlo para abarcar aún más.

Si te ha gustado este artículo, te invito a que lo compartas con tus seguidores, y si tienes alguna duda o sugerencia, déjame un comentario, estaré encantado de leerte.