tailwind2-3 Exercise 3: A Small Plugin With Options ==================================================== Setup: a folder with Tailwind installed (npm install tailwindcss @tailwindcss/cli). Create the files below, then build each CSS file with: npx @tailwindcss/cli -i .css -o .out.css --- shadow.js --- const plugin = require('tailwindcss/plugin'); module.exports = plugin.withOptions(function (options = {}) { const color = options.color || 'rgb(0 0 0 / 0.3)'; return function ({ addBase, addComponents, addUtilities, matchUtilities, addVariant, matchVariant, theme }) { addBase({ 'h1, h2': { 'text-wrap': 'balance' } }); addComponents({ '.pill': { 'border-radius': '9999px', 'padding': '0.25rem 0.75rem' } }); addUtilities({ '.no-scrollbar': { 'scrollbar-width': 'none' } }); matchUtilities( { 'glow-text': (value) => ({ 'text-shadow': `0 0 ${value} ${color}` }) }, { values: { sm: '2px', md: '6px', lg: '12px' } } ); matchUtilities( { 'accent-ring': (value) => ({ 'box-shadow': `0 0 0 3px ${value}` }) }, { values: theme('colors') , type: 'color' } ); addVariant('hocus', '&:is(:hover, :focus-visible)'); matchVariant('nth', (value) => `&:nth-child(${value})`, { values: { 1: '1', 2: '2', odd: 'odd' } }); }; }); --- pl.css --- @import "tailwindcss" source(none); @source "./tpl.html"; @theme { --color-brand: oklch(0.55 0.2 260); } @plugin "./shadow.js" { color: rgb(255 0 0 / 0.5); } --- tpl.html ---

--- pl2.css and tpl2.html (to read the computed values in Chrome; tpl2.html is the page with four spans and a script that prints padding and text-shadow) --- @import "tailwindcss" source(none); @source "./tpl2.html"; @plugin "./shadow.js" { color: rgb(255 0 0 / 0.5); } Output: exit 0 .no-scrollbar .pill .bg-red-500 .accent-ring-\[\#0f0\] .accent-ring-brand .accent-ring-red-500 .glow-text-\[9px\] .glow-text-md .nth-2\:font-bold:nth-child(2) .nth-odd\:italic:nth-child(odd) .hocus\:underline:is(:hover, :focus-visible) --- base layer @layer base { h1, h2 { text-wrap: balance; } } --- computed styles pill: paddingLeft 12px paddingTop 4px pill px-8: paddingLeft 32px paddingTop 4px pill p-8: paddingLeft 32px paddingTop 32px glow-text-md: rgba(255, 0, 0, 0.5) 0px 0px 6px WHY THIS WORKS AS AN ANSWER --------------------------- @plugin "./shadow.js" { color: ... } loads the file and passes the block to plugin.withOptions as an object, so the red tint comes from the CSS file, not from the plugin: glow-text-md used rgb(255 0 0 / 0.5). Each helper in the plugin shows up in the output: addBase put the text-wrap rule in the base layer; addUtilities and matchUtilities made .no-scrollbar, glow-text-md and glow-text-[9px] (matchUtilities accepts arbitrary values on its own); addVariant gave hocus; matchVariant gave nth-2 and nth-odd. Two things to notice. First, accent-ring-brand has the resolved colour oklch(0.55 0.2 260), not var(--color-brand): theme() in a plugin returns the value itself, so a plugin utility does not follow a re-themed variable the way an @utility with --value(--color-*) does. Second, addComponents put .pill in the utilities layer in this version (there was nothing in the components layer). It still lost to p-8 and px-8, as the computed values show (padding 12px, then 32px left only, then 32px all round), because it was written before them, but you should not rely on it for a layer guarantee.