
El framework tailwindcss nos ofrece la posibilidad de definir todos los estilos css inline
Esto que parece restrictivo (y lo es), nos permite entre otras cosas tener un diseño consistente y sobre todo con muy poco mantenimiento
Prólogo
El framework TailwindCSS se define como utility-first, y este es un concepto
diferente al de styled-components
que tiene sus ventajas y sus inconvenientes
-
El principal inconveniente es que perdemos la encapsulación que conseguimos con
styled-components
(con la queja recurrente de importar todos los estilos detailwind
en todas las páginas) -
La principal ventaja es la restricción a la hora de elegir, en lugar de escoger entre todo el repertorio de
css
lo que hacemos es escoger entre un grupo declases
preestablecidas y que no son semánticas, esto nos permite ser más consistentes
En otras palabras, tailwind
nos presiona para que seamos muy minimalistas, mientras que con styled-components
al final es fácil generar mucha redundancia con cada componente absolutamente ajustado a las necesidades
-
Lo bueno es que siendo minimalistas tendremos consistencia en la estética y por definición muy poco mantenimiento
-
Lo malo es que si nos despistamos terminaremos con monstruos inline con 20 o más clases (a ver quien entiende y mantiene eso)
En este curso vamos a aprender tailwindcss
, vamos allá
Funcionamiento básico de tailwind
Cómo funciona un framework utility-first?
Fácil, se trata de prescindir de la semántica
En lugar de utilizar nombres semánticos como .estilo_de_mi_formulario
vamos a utilizar nombres que describan su función, tipo .ancho_500 .fondo_gris .letra_Lato
Así nos saltamos el paso intermedio de encapsular los estilos en una clase con un nombre irrelevante, para definirlos directamente en el mismo elemento
Y esto lo podemos hacer (sin volvernos locos) porque tailwind
nos da una sintaxis breve y un grupo de clases predefinidas que nos limitan y por lo tanto nos obligan a escribir de un modo consistente
Cuando se dice "consistente" es porque en lugar de tener paddings
de 10px
, de 15px
y de 18px
pero también de 20px
, producto de diseñar cada vez de nuevo cada elemento, tendremos clases que nos darán distancias ya fijadas (que podremos cambiar si queremos, pero que serán comunes para toda nuestra página web), con lo que visualmente tendremos una estética por fuerza más homogénea
El espíritu de tailwind
es que no necesitas estar constantemente reinventando los diseños web, ya están todos inventados, tu trabajo sólo es combinarlos a partir de unidades concretas predefinidas
En la práctica
<!-- clase semántica -->
<h1 className="mi-componente-de-header">Hey</h1>
<style>
.mi-componente-de-header {
font-size: 1.875rem;
font-weight: 700;
background: #cbd5e0;
}
</style>
<!-- clase utility-first -->
<h1 className="text-3xl font-bold bg-gray-400">Hey</h1>
Si te fijas en la cantidad de código que nos hemos ahorrado verás el porqué tendrás menos mantenimiento con tailwind
Y esto es porque estas clases utility-first ya nos vienen por defecto con tailwind
, aunque si los valores no nos gustan podemos definir nuestras propias normas (compartidas para todo el proyecto)
Otro concepto interesante es que ahorrarse el encapsular los estilos en una clase tiene mucho sentido puesto que la gran mayoría de veces esa clase no la reutilizaremos
Y si no necesitamos reutilizarlo, para qué definirlo aparte?
Y en aquellos casos donde sí queremos reutilizar clases, si utilizamos React
la solución es extremadamente elegante (lo vemos más abajo)
Aprendiendo tailwind
Aprender tailwind
se reduce a
- Familiarizarse con las clases existentes
- Familiarizarse con los archivos que podemos tocar para cambiar los valores por defecto
- Aprenderse como escribir los valores responsive, los hover, etc
Familiarizarse con las clases existentes
Para el primer punto hay que mirarse la documentación, y aunque al principio pueda parecer confusa lo cierto es que sigue una lógica sencilla y consistente
Un ejemplo lo tenemos con este botón de la documentación oficial
<button class="bg-blue-500 text-white font-bold py-2 px-4 rounded">Button</button>
Todas las clases son auto-explicativas excepto quizá las py-2
, px-4
y rounded
, que si miramos la documentación vemos que se traducen en
/* py-2 */
padding-top: 0.5rem;
padding-bottom: 0.5rem;
/* px-4 */
padding-left: 1rem;
padding-right: 1rem;
/* rounded */
border-radius: 0.25rem;
Lo bueno es que es muy fácil acostumbrarse a esta nomenclatura
Lo malo es que es otra nomenclatura que hay que aprender
Cambiando los valores por defecto
Tenemos un archivo central que es tailwind.config.js
, y allí podemos cambiar lo que nos plazca (veremos el archivo en sí cuando instalemos la librería más adelante)
La sintaxis es la misma que con theme-ui
por lo que si ya lo conoces te sonará todo, y si no lo conoces, luego si lo conoces también te sonará todo
// tailwind.config.js
module.exports = {
theme: {},
}
Todo lo que pongamos dentro de theme
sustituirá los valores por defecto que tengamos
Quizá lo más habitual será el font-size
(que al final siempre depende de la tipografía) y los colores (la paleta por defecto, por ejemplo, no incluye grises reales)
// tailwind.config.js
module.exports = {
theme: {
colors: {
realgrey: '#ccc',
},
},
}
Configurándolo de esta forma tailwind
nos dará acceso automáticamente a las clases bg-realgrey
, text-realgrey
y border-realgrey
No tenemos forma de utilizar ese color directamente, si queremos hacerle tendremos que definir una nueva clase al estilo clásico
.btn-blue {
background-color: theme('colors.realgrey');
}
En realidad lo que hemos hecho arriba con tailwind.config.js
es sustituir los colores que nos vienen por defecto, y quizá no queremos sustituirlos sino extenderlos, algo muy sencillo
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
realgrey: '#ccc',
},
},
},
}
De este modo estamos añadiendo el color realgrey
(y si ese nombre ya estuviera definido entonces sólo sustituiríamos ese)
Para ver qué valores nos vienen por defecto los tienes aquí, y nada te impide copiar y utilizar ese mismo archivo tu mismo modificándolo directamente
Aparte del theme
también podemos modificar otras cosas, como las variants
que incluye la variante responsive
Estas variantes lo que hacen es generar las clases correspondientes a esas variantes
module.exports = {
variants: {
backgroundColor: ['responsive', 'hover'],
},
}
Aquí lo que hacemos es
- Generar clases para la propiedad
backgroundColor
que nos servirán para distintasmedia-queries
- Generar clases para el estado
hover
Generar sólo las que nos interesen nos servirá para reducir el tamaño final del bundle
Del mismo modo también podemos modificar los breakpoints
, es decir los valores que utilizaremos para los media-queries
// tailwind.config.js
module.exports = {
theme: {
screens: {
sm: '640px',
// => @media (min-width: 640px) { ... }
md: '768px',
// => @media (min-width: 768px) { ... }
lg: '1024px',
// => @media (min-width: 1024px) { ... }
xl: '1280px',
// => @media (min-width: 1280px) { ... }
},
},
}
Es decir, que si los valores que nos vienen por defecto no nos gustan, aquí es donde podemos cambiarlos y/o extenderlos, e incluso definir media-queries
que no sean mobile-first y tengan el max-width
Lo puedes mirar en la documentación, mientras tanto aquí nos centraremos en utilizar lo que ya nos viene por defecto
Utilizando tailwind
Para utilizar tailwind
nada más sencillo que utilizar las clases que ya nos vienen predefinidas
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Button</button>
Si te fijas, la clase hover:bg-blue-700
ya nos da pistas de que estamos definiendo una propiedad :hover
, que en este caso quiere decir que ese botón cuando esté en hover
se le aplique la clase bg-blue-700
No hay ninguna norma que te prohiba utilizar caracteres como el :
en un nombre de clase
Y para el responsive?
Imaginemos que queremos modificar el padding
de forma responsive
<button class="p-2 sm:p-10">Button</button>
Tan sencillo como esto, aquí tenemos definido para todo el mundo el p-2
, y para tamaños mayores de 640px
aplicaremos la clase p-10
Sólo nos falta saber qué hacer cuando queremos reutilizar parte de las clases, lo hacemos con @apply
<!-- original -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Button</button>
<!-- con una clase definida -->
<button class="my-button">Button</button>
<style>
.my-button {
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
}
.btn-blue:hover {
@apply bg-blue-700;
}
</style>
Claro está que así pierdes toda la gracia de tailwind
, por lo que lo que se aconseja es hacer esta extracción a nivel de componente
const Button = () => (
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Button</button>
)
Luego verás un ejemplo algo más completo en el código final, pero ya ves cómo podemos reutilizar clases sin perder la elegancia de tailwind
Instalando tailwind
Para instalar tailwind
en tu aplicación React
(basada en create-react-app
) bastaría con instalar el paquete
yarn add tailwindcss
Y luego en tu hoja de estilos central
/* purgecss start ignore */
@tailwind base;
@tailwind components;
/* purgecss end ignore */
@tailwind utilities;
Y con esto ya tenemos a tailwind
funcionando para nosotros, sin más, aunque lo suyo es crear el archivos de configuración de tailwind
para ajustar el funcionamiento de PurgeCSS, integrado en tailwind y que nos sirve para descartar estilos que no estemos utilizando
// tailwind.config.js
module.exports = {
purge: ['./src/**/*.html', './src/**/*.jsx', './src/**/*.js'],
}
También forma parte de la configuración de purgeCSS
los comentarios anteriores en la hoja css
(/* purgecss start ignore */
), que no es más que decirle a purgeCSS
que sólo nos procese las @tailwind utilities
(que es lo que nos recomiendan en la documentación)
Ahora podríamos bien importar los estilos de tailwind
directamente (pero nos perderemos bastantes cosas), o utilizar la librería para transpilar nuestro tailwind-css a css
de verdad
Para hacerlo bastaría con configurar el package.json
para que cuando hagamos el start
nos compilase el código, y esto lo podemos hacer con el comando tailwindcss
que ya nos viene incluido en la librería
"scripts": {
"build:style": "tailwindcss build src/styles.css -o transpiled_styles.css",
"start": "npm run build:style && react-scripts start"
}
Si esto lo queremos mirar en un codesandbox no es tan sencillo, ya que necesitamos un sandbox que nos venga con un backend incluido, algo que nos explican aquí
Por ahora y para no complicarlo, me limitaré a importar los estilo de tailwind
en el codesandbox
Instalarlo en Gatsby
Si quisiésemos instalarlo con Gatsby
necesitaríamos configurar también PostCSS
, algo que en verdad haremos en la mayoría de los casos en los que instalemos tailwind
Para Gatsby necesitaríamos instalar el starter y las librerías
gatsby new hello-world https://github.com/gatsbyjs/gatsby-starter-hello-world
yarn add --dev tailwindcss
yarn add gatsby-plugin-postcss
Configurar el plugin postCSS
// gatsby-config.js
module.exports = {
plugins: ['gatsby-plugin-postcss'],
}
Configurar el postCSS
// postcss.config.js
module.exports = () => ({
plugins: [require('tailwindcss')],
})
E importar un archivo global css
en gatsby-browser.js
// gatsby-browser.js
import './src/global.css'
Un archivo que contendrá las bases de tailwind
/* ./src/global.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Y ya sólo faltaría la configuración de purgeCSS
// tailwind.config.js
module.exports = {
purge: ['./src/**/*.js', './src/**/*.jsx', './src/**/*.ts', './src/**/*.tsx'],
}
Lo tienes todo en la documentación, donde también podríamos combinar tailwind
con styled-components
, algo que si bien tiene ventajas evidentes, me gustaría saber en cuánto augmenta el tamaño de JavaScript a interpretar para el navegador
Diseñando un bloque de ejemplo
Vista la instalación, algo que podemos hacer a nivel local con un simple CRA
o aprovechar y utilizar NextJS
o GatsbyJS
, vamos a ver cómo luce un componente sencillo con un simple ejemplo que tienes en este codesandbox
El package.json
(en el codesandbox no podemos ejecutar el tailwindcss
por lo que no transpilamos nada)
{
"name": "tailwind",
"version": "1.0.0",
"description": "",
"keywords": [],
"main": "src/index.js",
"dependencies": {
"react": "16.12.0",
"react-dom": "16.12.0",
"react-scripts": "3.0.1",
"tailwindcss": "1.0.4"
},
"devDependencies": {},
"scripts": {
"build:style": "tailwindcss build src/styles.css -o transpiled_styles.css",
"start": "npm run build:style && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"browserslist": [">0.2%", "not dead", "not ie <= 11", "not op_mini all"]
}
La configuración de purge
// tailwind.config.js
module.exports = {
purge: ['./src/**/*.html', './src/**/*.jsx', './src/**/*.js'],
}
La hoja de estilos donde aprovecho para definir las clases de un background de gradientmagic.com
/* styles.css */
/* purgecss start ignore */
@tailwind base;
@tailwind components;
/* purgecss end ignore */
@tailwind utilities;
/* https://www.gradientmagic.com/collection/fracture */
.background {
background-image: linear-gradient(
35deg,
rgba(253, 253, 253, 0.03) 0%,
rgba(253, 253, 253, 0.03) 53%,
rgba(109, 109, 109, 0.03) 53%,
rgba(109, 109, 109, 0.03) 59%,
rgba(228, 228, 228, 0.03) 59%,
rgba(228, 228, 228, 0.03) 66%,
rgba(42, 42, 42, 0.03) 66%,
rgba(42, 42, 42, 0.03) 95%,
rgba(165, 165, 165, 0.03) 95%,
rgba(165, 165, 165, 0.03) 100%
), linear-gradient(
205deg,
rgba(62, 62, 62, 0.03) 0%,
rgba(62, 62, 62, 0.03) 31%,
rgba(200, 200, 200, 0.03) 31%,
rgba(200, 200, 200, 0.03) 41%,
rgba(30, 30, 30, 0.03) 41%,
rgba(30, 30, 30, 0.03) 47%,
rgba(151, 151, 151, 0.03) 47%,
rgba(151, 151, 151, 0.03) 60%,
rgba(95, 95, 95, 0.03) 60%,
rgba(95, 95, 95, 0.03) 100%
), linear-gradient(
30deg,
rgba(7, 7, 7, 0.03) 0%,
rgba(7, 7, 7, 0.03) 19%,
rgba(63, 63, 63, 0.03) 19%,
rgba(63, 63, 63, 0.03) 33%,
rgba(175, 175, 175, 0.03) 33%,
rgba(175, 175, 175, 0.03) 37%,
rgba(244, 244, 244, 0.03) 37%,
rgba(244, 244, 244, 0.03) 60%,
rgba(177, 177, 177, 0.03) 60%,
rgba(177, 177, 177, 0.03) 100%
), linear-gradient(90deg, rgb(162, 162, 162), rgb(229, 229, 229));
}
.background-box1 {
background-image: linear-gradient(
45deg,
rgb(20, 59, 44) 0%,
rgb(20, 59, 44) 62%,
rgb(72, 77, 92) 62%,
rgb(72, 77, 92) 69%,
rgb(124, 94, 140) 69%,
rgb(124, 94, 140) 76%,
rgb(176, 112, 188) 76%,
rgb(176, 112, 188) 88%,
rgb(228, 129, 236) 88%,
rgb(228, 129, 236) 100%
);
}
.background-box2 {
background-image: linear-gradient(
135deg,
transparent 0%,
transparent 21%,
rgba(253, 225, 42, 0.5) 21%,
rgba(253, 225, 42, 0.5) 48%,
transparent 48%,
transparent 95%,
rgba(247, 141, 81, 0.5) 95%,
rgba(247, 141, 81, 0.5) 100%
), linear-gradient(
45deg,
transparent 0%,
transparent 6%,
rgb(253, 225, 42) 6%,
rgb(253, 225, 42) 13%,
rgb(249, 169, 68) 13%,
rgb(249, 169, 68) 32%,
transparent 32%,
transparent 100%
), linear-gradient(90deg, rgb(255, 255, 255), rgb(255, 255, 255));
}
La puerta de entrada de React
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
Y el componente en sí
// App.js
import React from 'react'
import './styles.css'
import 'tailwindcss/dist/tailwind.min.css'
export default function App() {
return (
<div className="container fixed mx-auto h-full p-4 background">
<div className="background-box1 text-gray-200 p-4 rounded mb-6 max-w-screen-md">
<H1 attr="font-black">Hello CodeSandbox</H1>
<h2 className="mb-4">Start editing to see some magic happen!</h2>
<Button attr="text-gray-200 bg-blue-800 hover:bg-blue-400">Button</Button>
</div>
<div className="background-box2 text-gray-900 p-4 rounded max-w-screen-md">
<H1 attr="font-normal">Hello CodeSandbox</H1>
<h2 className="mb-4">Start editing to see some magic happen!</h2>
<Button attr="text-gray-200 bg-red-500 hover:bg-gray-500">Button</Button>
</div>
</div>
)
}
const H1 = ({ children, attr }) => (
<h1 className={`${attr} text-4xl sm:text-6xl leading-none mb-2 ${attr}`}>{children}</h1>
)
const Button = ({ children, attr }) => (
<button className={`p-2 rounded transition duration-200 ease-in-out ${attr}`}>{children}</button>
)
Y aquí arriba puedes ver como es muy sencillo integrar clases que utilices en un componente y combinarlas con clases que quieras reutilizar en un componente genérico, todo esto sin perder lo bonito que es escribir en tailwind
El resultado lo tienes en este codesandbox, y aquí abajo tienes un pantallazo
Como ves, para definir los fondos he utilizado exclusivamente css
, mientras que para el resto he podido conseguir una estética más que suficiente con lo que me ofrece tailwind
Sin duda es una manera muy agradable de diseñar, mucho menos pesada para la cabeza
Listos!
🙋♂️
Lista de correo: escribo algo de tanto en cuanto