Configuración, Plugins

¿Qué que son los plugins de Tailwind CSS?

Los plugins permiten registrar nuevos estilos en Tailwind inyectando nuevas hojas de estilos utilizando JavaScript en lugar de CSS.

Para crear tu primer plugin, importa la función plugin de tailwindcss/plugin. Después dentro del arreglo plugins, llama la función plugin con una función anónima como primer argumento.

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
        plugin(function ({ addUtilities, addComponents, e, config }) {
            // Add your custom styles here
        }),
    ],
}

Las funciones de plugin reciben un objeto como argumento que se puede descomponer en varios helpers (funciones de ayuda).

Helper Descripción
addUtilities Registra nuevos estilos de utilidades estáticas.
matchUtilities Registra nuevos estilos de utilidades dinámicas.
addComponents() Registra nuevos estilos de componentes estáticos.
matchComponents() Registra nuevos estilos de components dinámicos.
addBase() Registra nuevos estilos base.
addVariant() Registra variantes personalizadas estáticas.
matchVariant() Registra variantes personalizadas dinámicas.
theme() Busca valores en la configuración de temas del usuario.
config() Busca valores en la configuración de Tailwind del usuario.
corePlugins() Verifica si un complemento central está habilitado.
e() Escapa cadenas destinadas a ser usadas en nombres de clases.

Plugins Oficiales

Hay una lista de plugins para funcionalidades que no se encuentran aun dentro del corazón de Tailwind.

Los plugins pueden ser agregados al proyecto utilizando npm, y después agregándolos al archivo tailwind.config.js.

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
    // ...
    plugins: [
        require('@tailwindcss/typography'),
        require('@tailwindcss/forms'),
        require('@tailwindcss/aspect-ratio'),
        require('@tailwindcss/container-queries'),
    ],
}

Typography (Tipografía)

El plugin @tailwindcss/typography agrega las clases prose, que pueden ser utilizadas para agregar estilos tipográficos que vienen de fuentes como markdown o una base de datos de un CSM.

<article class="prose lg:prose-xl">....</article>

Forms (Formularios)

El plugin @tailwindcss/forms agrega clases que hacen mas sencillo de agregar estilos a los elementos de los formularios.

<!-- Puedes personalizar el padding de un select input -->
<select class="px-4 py-3 rounded-full">
    <!-- ... -->
</select>
<!-- O el color de un checkbox -->
<input type="checkbox" class="rounded text-pink-500" />

Aspect ratio (aspecto en proporción)

El plugin @tailwindcss/aspect-ratio es una alternativa de aspect-ratio, que ademas incluye soporte para navegadores viejos y agrega las clases aspect-w-{n} y aspect-h-{n} que pueden ser combinadas para dar a un elemento un aspecto fijo.

<div class="aspect-w-16 aspect-h-9">
    <iframe
        src="https://www.youtube.com/embed/dQw4w9WgXcQ"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen
    ></iframe>
</div>

En este ejemplo se incluye un video de youtube con aspecto 16:9, es decir aspect-w-16 y aspect-h-9.

Container queries

El plugin @tailwindcss/container-queries agrega nuevas variantes del tipo @{size} y @md que permiten agregar estilos a un elemento basado en las dimensiones de otro que es el elemento padre y marcado como @container, en lugar del viewport.

<div class="@container">
    <div class="@lg:text-sky-400">
        <!-- ... -->
    </div>
</div>

Agregando utilidades

Las funciones addUtilities y matchUtilities permiten registrar nuevos estilos en la capa utilities de Tailwind.

Así como con los estilos de las utilidades de Tailwind, los estilos de las utilidades añadidas por un plugin solo serán incluidos en el CSS final, si estos estilos son utilizados a lo largo del proyecto.

Utilidades estáticas (addUtilities)

Utiliza la función addUtilities para registrar una utilidad estática que no soporte valores provistos por el usuario.

const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
        plugin(function ({ addUtilities }) {
            addUtilities({
                '.content-auto': {
                    'content-visibility': 'auto',
                },
                '.content-hidden': {
                    'content-visibility': 'hidden',
                },
                '.content-visible': {
                    'content-visibility': 'visible',
                },
            })
        }),
    ],
}

Utilidades dinámicas (matchUtilities)

Utiliza la función matchUtilities para registrar utilidades que enlacen con valores definidos por el usuario en la configuración de theme.

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
    theme: {
        tabSize: {
            1: '1',
            2: '2',
            4: '4',
            8: '8',
        },
    },
    plugins: [
        plugin(function ({ matchUtilities, theme }) {
            matchUtilities(
                {
                    tab: (value) => ({
                        tabSize: value,
                    }),
                },
                { values: theme('tabSize') }
            )
        }),
    ],
}

Las utilidades definidas de esta forma soportan el uso de valores arbitrarios, lo que significa que puedes utilizar valores que no se encuentran presentes en el tema mediante la notación de paréntesis.

<div class="tab-[13]">
    <!-- ... -->
</div>

Prefix e important

Por default, los plugins de utilidades respetan las preferencias de uso de prefix e important.

Esto quiere decir que dada una configuración como la siguiente:

/** @type {import('tailwindcss').Config} */
module.exports = {
    prefix: 'tw-',
    important: true,
    // ...
}

El ejemplo anterior generará los siguientes estilos.

.tw-content-auto {
    content-visibility: auto !important;
}
.tw-content-hidden {
    content-visibility: hidden !important;
}
.tw-content-visible {
    content-visibility: visible !important;
}

Utilizar con modificadores

Cualquier utility propia agregada mediante addUtilities puede ser automáticamente utilizada con modificadores.

<div class="content-auto lg:content-visible">
    <!-- ... -->
</div>

Uso de valores por default

Los plugins pueden proporcionar valores predeterminados incluyendo un objeto de configuración como segundo argumento en la función del plugin.

// plugins/tab-size.js
const plugin = require('tailwindcss/plugin')

module.exports = plugin(
    function ({ matchUtilities, theme }) {
        matchUtilities(
            {
                tab: (value) => ({
                    tabSize: value,
                }),
            },
            { values: theme('tabSize') }
        )
    },
    {
        theme: {
            tabSize: {
                1: '1',
                2: '2',
                4: '4',
                8: '8',
            },
        },
    }
)

Estos valores funcionan como valores predeterminados en la configuración, y pueden ser sobreescritos o extendidos por el usuario.

Agregar componentes

La función addComponents permite registrar nuevos estilos en la capa components de Tailwind.

Utiliza esta para agregar mas clases para componentes como botones, elementos de formulario, alertas, etc.

Para agregar un nuevos estilos de componentes desde un plugin, utiliza addComponents.

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
        plugin(function ({ addComponents }) {
            addComponents({
                '.btn': {
                    padding: '.5rem 1rem',
                    borderRadius: '.25rem',
                    fontWeight: '600',
                },
                '.btn-blue': {
                    backgroundColor: '#3490dc',
                    color: '#fff',
                    '&:hover': {
                        backgroundColor: '#2779bd',
                    },
                },
                '.btn-red': {
                    backgroundColor: '#e3342f',
                    color: '#fff',
                    '&:hover': {
                        backgroundColor: '#cc1f1a',
                    },
                },
            })
        }),
    ],
}

Así como con otros clases de componentes en Tailwind, solo las clases de los componentes añadidos por plugins que sean utilizadas a través del proyecto, serán generadas en el css final.

Prefix e important

De manera predeterminada, las clases de componentes respetan automáticamente la preferencia de prefijo del usuario, pero no se ven afectadas por la preferencia de importancia del usuario.

Esto quiere decir que dada la siguiente configuración.

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
    prefix: 'tw-',
    important: true,
    // ...
}

El ejemplo anterior generará las siguientes clases.

.tw-btn {
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
    font-weight: 600;
}
.tw-btn-blue {
    background-color: #3490dc;
    color: #fff;
}
.tw-btn-blue:hover {
    background-color: #2779bd;
}
.tw-btn-red {
    background-color: #e3342f;
    color: #fff;
}
.tw-btn-red:hover {
    background-color: #cc1f1a;
}

Aunque rara vez hay una buena razón para hacer que las declaraciones de componentes sean importantes, si realmente necesitas hacerlo, siempre puedes añadir !important manualmente:

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
        plugin(function ({ addComponents }) {
            addComponents({
                '.btn': {
                    padding: '.5rem 1rem !important',
                    borderRadius: '.25rem !important',
                    fontWeight: '600 !important',
                },
                // ...
            })
        }),
    ],
}

Todas las clases en un selector serán precedidas por default, así que si agregas un estilo mas complejo como:

// tailwind.config.js
module.exports = {
    prefix: 'tw-',
    plugins: [
        plugin(function ({ addComponents }) {
            const components = {
                // ...
                '.navbar-inverse a.nav-link': {
                    color: '#fff',
                },
            }

            addComponents(components)
        }),
    ],
}

El siguiente CSS será generado.

.tw-navbar-inverse a.tw-nav-link {
    color: #fff;
}

Utilizandolos con modificadores

Cualquier componente agregado utilizando addComponents puede ser automáticamente utilizado con modificadores.

<div class="btn md:btn-lg">
    <!-- ... -->
</div>

Agregar estilos base

La función addBase permite registrar nuevos estilos en la capa base de Tailwind. Utilizado para agregar cosas como estilos de tipografía, resets globales, o reglas @font-face.

Para agregar una nueva base de estilos desde un plugin, utiliza addBase.

const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
        plugin(function ({ addBase, theme }) {
            addBase({
                h1: { fontSize: theme('fontSize.2xl') },
                h2: { fontSize: theme('fontSize.xl') },
                h3: { fontSize: theme('fontSize.lg') },
            })
        }),
    ],
}

Agregar variantes

Utiliza la función addVariant para crear variantes personalizadas simples, pasando el nombre de tu variante personalizada y una cadena de formato que represente cómo debe modificarse el selector.

const plugin = require('tailwindcss/plugin')

module.exports = {
    // ...
    plugins: [
        plugin(function ({ addVariant }) {
            addVariant('optional', '&:optional')
            addVariant('hocus', ['&:hover', '&:focus'])
            addVariant('inverted-colors', '@media (inverted-colors: inverted)')
        }),
    ],
}

El primer argumento es el nombre el modificador que los usuarios utilizarán en el HTML, de forma que el anterior ejemplo hará posible escribir clases como esta:

<form class="flex inverted-colors:outline ...">
    <input class="optional:border-gray-300 ..." />
    <button class="bg-blue-500 hocus:bg-blue-600">...</button>
</form>

Dynamic variants (variantes dinámicas)

Utiliza la función matchVariant para registrar nuevas variantes parametrizadas como las variantes supports-*, data-* y aria-*:

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
    plugins: [
        plugin(function ({ matchVariant }) {
            matchVariant(
                'nth',
                (value) => {
                    return `&:nth-child(${value})`
                },
                {
                    values: {
                        1: '1',
                        2: '2',
                        3: '3',
                    },
                }
            )
        }),
    ],
}

Las variantes definidas mediante matchVariant también soportan valores arbitrarios mediante el uso de la notación de paréntesis:

<div class="nth-[3n+1]:bg-blue-500 ...">
    <!-- ... -->
</div>

Utiliza la opción sort para controlar el orden de origen del CSS generado si es necesario, para evitar problemas de precedencia con otros valores que provienen de la misma variante:

matchVariant('min', (value) => `@media (min-width: ${value})`, {
    sort(a, z) {
        return parseInt(a.value) - parseInt(z.value)
    },
})

Estados de padres y hermanos

Tus modificadores personalizados no funcionarán automáticamente con los modificadores de estado de padres y hermanos de Tailwind.

Para admitir las versiones group-* y peer-* de tus propios modificadores personalizados, regístralos como variantes separadas utilizando la directiva especial :merge para asegurarte de que las clases .group y .peer solo aparezcan una vez en el selector final.

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
    // ...
    plugins: [
        plugin(function ({ addVariant }) {
            addVariant('optional', '&:optional')
            addVariant('group-optional', ':merge(.group):optional &')
            addVariant('peer-optional', ':merge(.peer):optional ~ &')
        }),
    ],
}

Extendiendo la configuración

Los plugins pueden fusionar su configuración con la configuración del usuario en el archivo tailwind.config.js mediante un objeto como segundo argumento de la función plugin.

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = plugin(
    function ({ matchUtilities, theme }) {
        matchUtilities(
            {
                tab: (value) => ({
                    tabSize: value,
                }),
            },
            { values: theme('tabSize') }
        )
    },
    {
        theme: {
            tabSize: {
                1: '1',
                2: '2',
                4: '4',
                8: '8',
            },
        },
    }
)

Esto puede ser util para cosas como proveer valores por default al theme para aquellas clases que el plugin genera.

Exponer los options

A veces tiene sentido que un plugin sea configurable de una manera que no encaja realmente en el theme, como por ejemplo, si deseas que los usuarios puedan personalizar el nombre de clase que utiliza tu plugin.

Para casos como este, puedes usar plugin.withOptions para definir un plugin que pueda ser invocado con un objeto de configuración. Esta API es similar a la API regular de plugins, excepto que cada argumento debe ser una función que recibe las opciones del usuario y devuelve el valor que normalmente habrías pasado utilizando la API regular:

// plugins.markdown.js
const plugin = require('tailwindcss/plugin')

module.exports = plugin.withOptions(
    function (options = {}) {
        return function ({ addComponents }) {
            const className = options.className ?? 'markdown'

            addComponents({
                [`.${className}`]: {
                    // ...
                },
            })
        }
    },
    function (options) {
        return {
            theme: {
                markdown: {
                    // ...
                },
            },
        }
    }
)

El usuario invocaría tu plugin pasando sus opciones al registrarlo en su configuración de plugins:

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
    theme: {
        // ...
    },
    plugins: [
        require('./plugins/markdown.js')({
            className: 'wysiwyg',
        }),
    ],
}

El usuario también puede registrar plugins creados de esta manera sin invocarlos si estos no necesitan pasar ninguna opción personalizada:

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
    theme: {
        // ...
    },
    plugins: [require('./plugins/markdown.js')],
}

La sintaxis CCS-in-JS

El sistema de plugins de Tailwind espera reglas de CSS escritas como objetos de JavaScript, utilizando el mismo tipo de sintaxis que podrías reconocer de bibliotecas CSS-in-JS como Emotion, impulsado por postcss-js.

Para este ejemplo:

.card {
    background-color: #fff;
    border-radius: 0.25rem;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

Traduciendo esto a CSS-in-JS:

addComponents({
    '.card': {
        'background-color': '#fff',
        'border-radius': '.25rem',
        'box-shadow': '0 2px 4px rgba(0,0,0,0.2)',
    },
})

Para conveniencia, los nombres de las propiedad también pueden ser escritas utilizando la convención camelCase y será automáticamente convertido en dash-case.

addComponents({
    '.card': {
        backgroundColor: '#fff',
        borderRadius: '.25rem',
        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
    },
})

La anidación también está soportada (promovida por postcss-nested), utilizando la misma sintaxis que quizás te resulte familiar de Sass o Less:

addComponents({
    '.card': {
        backgroundColor: '#fff',
        borderRadius: '.25rem',
        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
        '&:hover': {
            boxShadow: '0 10px 15px rgba(0,0,0,0.2)',
        },
        '@media (min-width: 500px)': {
            borderRadius: '.5rem',
        },
    },
})

Se pueden definir múltiples reglas en el mismo objeto:

addComponents({
    '.btn': {
        padding: '.5rem 1rem',
        borderRadius: '.25rem',
        fontWeight: '600',
    },
    '.btn-blue': {
        backgroundColor: '#3490dc',
        color: '#fff',
        '&:hover': {
            backgroundColor: '#2779bd',
        },
    },
    '.btn-red': {
        backgroundColor: '#e3342f',
        color: '#fff',
        '&:hover': {
            backgroundColor: '#cc1f1a',
        },
    },
})

o como un arreglo de objetos en caso de que necesites repetir la misma llave:

addComponents([
    {
        '@media (min-width: 500px)': {
            // ...
        },
    },
    {
        '@media (min-width: 500px)': {
            // ...
        },
    },
    {
        '@media (min-width: 500px)': {
            // ...
        },
    },
])