Creating a robust TailwindCSS Pipeline for Custom Drupal Theme
In this insight post, we take you through the steps of setting up a TailwindCSS pipeline for a custom Drupal theme. Widely covering the benefits of setting up this sort of system and how to use it to create your own Drupal theme from scratch.
Introduction: The Basics of TailwindCSS and Why it is Awesome
TailwindCSS is an open-source project that provides a set of utility CSS classes for responsive, mobile-first web design and development. Ready to use, without the need to write actual CSS – instead, it writes the CSS for you. This may dissuade someone who’s coming from a purely Bootstrap, SCSS, or LESS background, like me!
From a strict development standpoint, Tailwind requires practice and acquaintance at first. As opposed to preprocessors like SASS/SCSS/LESS, it’s more of a baked-in toolset, though you can use preprocessors it’s generally not required or recommended with Tailwind.
Tailwind takes the approach of writing no actual CSS and at the same time leveraging advanced responsive design and development principles. When it comes to customizations and presets, it doesn’t fall short of setting breakpoints, containers, colors, typography, spacings, backgrounds, and flex/grid layouts – perhaps more than you can chew. Literally, for every CSS property, there’s a utility class, put them to use and witness your extraordinary accuracy and agility of quick prototyping to impress your cohorts :)
First Step: Creating a Custom Drupal Theme
The Drush CLI is your go-to gizmo when it comes to scaffolding new themes, modules, plugins, forms, and other boilerplate code. Assuming you’re running a complete Drupal 9 setup with Drush CLI and Composer dependencies, shoot up your terminal and execute the command from the project root: drush generate theme
Finish the prompted steps one by one, and note that we said no to SASS. You may set your sub-theme on anything else other than Classy. It’s highly recommended by the core Drupal dev team as it's based on abstracts of SMACSS following industry standards and best practices. However, starting with Drupal 9.4, Olivero is the new default core theme, which is far more refined for accessibility.
Second Step: Setting up Node Package Manager
If you’re familiar with NPM packages and often use them in your pipelines or dev environments you can start with npm init in your theme directory. Now go ahead and install all the dev dependencies for Tailwind with:
From here you’re all set to use Tailwind in your project. We’re also using some Tailwind plugins for forms, line-clamping, and typography which can be omitted. If you prefer a barebones setup check out the default Tailwind installation guide.
Third Step: Setting up default Tailwind and PostCSS configuration
In these final two steps configure defaults for both Tailwind and PostCSS. To do this CWD from Terminal into your themes directory and execute: npx tailwindcss init
A new Tailwind configuration file will be created with the name tailwind.config.js where Tailwind defaults can be configured. A good starting point would be referring to the default configuration file which is shipped with the Tailwind package, this way you can fine-tune and explore all the options or use the -full flag in Tailwind initialization.
The crux of this configuration is how you configure the content option i.e. where your templates are located. It’s the same as watching files or folders for changes using fuzzy search or regex search. For Drupal theme the following regex works flawlessly:
This tells Tailwind to observe changes in these files/folders to compile the output CSS dynamically as you author utility classes inside templates. No doubt it arms you with a double-edged sword that cuts both ways, you’re pretty much tied to templating because that’s where Tailwind will look for changes and regenerate CSS. Tailwind won’t know of your CMS-authored classes; for eg. adding Tailwind classes to a Drupal View will have no effect!
On the contrary, a positive benefit that Tailwind has over preprocessors or Bootstrap is it won’t create two-fold classes or add any extra over bloated rulesets unless they’re explicitly used in your templates, resulting in pre-optimized assets for production.
Finally, create a new postcss.config.js file alongside the Tailwind config file. In this configuration, we are using some built-in plugins and conditional plugins like cssnano for generating production-ready assets.
PostCSS nested or nesting works just like SCSS nested rulesets, though it kept failing for me ¯\_(ツ)_/¯ and you may need to install it as a separate dependency.
This gives you a barebones setup, to begin with. To see Tailwind in action put these additional scripts in your package.json and off you go.
Now you should be able to run Tailwind CLI with watch from Terminal using npm run dev or npm run build to work with production optimized assets.
Authoring twig templates with Tailwind in action
Even with Tailwind you’re not tied to a single CSS that piles up over time with custom Tailwind class extractions heavily used in conjunction with the @apply directive. To make things more manageable, my PostCSS files are organized by default Drupal asset structure and since we’re already using the postcss-import plugin it’s relatively easy to bundle them in one file.
Worth mentioning that we ended up using both core utility classes in templates and class extractions. With the Tailwind dev watch fired up, let's check how Tailwind reacts to authoring new utility classes. For this example, I’m using block.twig.html a core Drupal block template in the custom theme and adding a new class for line height as leading-loose which reflects immediately.
It happens instantaneously in less than 400ms, that’s how fast Tailwind can transpile. It’s notable that under the hood, Tailwind uses Chokidar and PostCSS to watch and compiles the CSS with PostCSS. I found this easier than setting up complicated Grunt/Gulp tasks.
Why Tailwind and not Bootstrap?
There is no simpler answer to this question other than the context of project requirements. Tasked with the migration of a Drupal 7 website to Drupal 9, a legacy stylesheet that was outdated and not up to speed with current web standards. It had massive amounts of nodes with complex Views that were consistently overstyled. At the outset, we considered these decisive factors before beginning the actual development:
- Is it worth using the entire legacy stylesheet?
- No, we can’t use it as it may break the twig theme, plus it's outdated
- Is it worth using just a few portions of the legacy stylesheet?
- Yes we can use some styling like colors, font sizes, line heights, etc.
- Can we maintain consistency across pages without two-folds and over-styling?
- Yes by using the Tailwind Typography plugin
- Is it worth using the legacy theme?
- A big no because the markup will never be consistent
- What about content, views, and web forms?
- We migrate them gradually, although this turned out to be a slippery slope, especially with views and incompatible modules for Drupal 9
Tailwind allowed us to overcome the first three with ease, shelling almost 50% of the front-end woes for styling and consistency. We went with customizing Tailwind configuration for breakpoints, grids, fonts, colors, backgrounds, and typography at first ending up with a robust and consistent styling using just defaults. Where we felt the need for consistent typography, i.e. in node single pages the Typography plugin worked without flaw. Here’s a glimpse of typography customization:
Though Tailwind was fairly new for most of our dev team, yet it got the ball rolling and carried the day. One can argue over similar customizations in Bootstrap, UiKit, or Foundation, still not at this speed and alleviation.