DEV Community

ZhenShuo2021
ZhenShuo2021

Posted on • Edited on

Tailwind CSS in Hugo is NOT friendly to Downstream Users

This is translate by Claude Sonnet 4, Original Article

Tailwind CSS is a revolutionary tool in modern frontend development that frees developers from traditional CSS constraints, allowing them to quickly build websites using concise atomic classes. When Tailwind is combined with Hugo, the lightweight and fast static site generator, it seems like a perfect match made in heaven. However, beneath this seemingly beautiful combination lies a serious problem: developers enjoy an efficient development experience while users bear the heavy technical burden.

Let's start by discussing Hugo's features, advantages, and design philosophy, then explain Tailwind's benefits. This context will help us better understand the source of the problem.

Hugo: The Dependency-Free Website Generator

What is Hugo?

Imagine you just want to write articles and share ideas without getting trapped in complex technical quagmires. Hugo was born for exactly this purpose.

Hugo is a static site generator with an incredibly simple philosophy: download one file, build a website. No Node.js required, no dependency hell to manage, no version conflicts to worry about. You only need:

  1. Download Hugo (standalone executable, no dependencies)
  2. hugo new site to create a project
  3. Choose a theme and download it with git submodule add
  4. Write articles in Markdown
  5. hugo

Done! Your website is live, and it's pure binary distribution plus HTML with no dependencies—it can run anywhere.

Hugo Themes and Customization

Hugo's system is special. Its unique lookup system allows you to easily override theme files. For example, if a user thinks the theme's 404 page looks bad, the override process is incredibly simple:

  1. Copy theme/MY_THEME/layouts/404.html to the root directory's layouts/404.html
  2. Start customizing

Everything is that simple! The same task in Docusaurus requires you to read documentation, find swizzle targets, confirm swizzle support, write JS, understand the React ecosystem, and figure out various built-in components like @theme/ThemedImage, @docusaurus/Link, BrowserOnly, etc. Users need extensive knowledge to complete an override, while Hugo is simple: find the HTML, modify the HTML, and you're done.

Like most static site generators, many Hugo themes also provide a custom.css entry point to override the theme's built-in CSS, so simple modifications only require users to write a few lines of CSS without needing to override entire HTML files.

A Brief Overview of CSS

Next, we'll explain the problems that arise from Hugo + Tailwind. To help readers unfamiliar with frontend understand Tailwind's power, here's a brief introduction to recent CSS framework evolution.

Farewell to Bootstrap’s Uniformity

Bootstrap, as a mature CSS framework, offers a complete set of components and a grid system, but it also brings certain limitations.

The first is the issue of design homogenization. Bootstrap’s default styles have obvious characteristics, causing many websites to appear similar. Although this can be addressed through customization, it requires additional development time.

The second is the issue of file size. Bootstrap includes a large number of prebuilt styles and components. Even if a project uses only a small portion, the entire framework file still needs to be loaded, which affects page loading speed.

Lastly, there is the complexity of customization. Deeply customizing Bootstrap requires understanding its internal structure and Sass variable system, which increases the learning curve for developers who are used to writing CSS directly.

Tailwind: Lego Bricks

Tailwind is a completely different approach. It breaks CSS down into small building blocks. Using bg-blue-500 can intuitively set a blue background, and using rounded can create rounded corners. All configurations are highly intuitive and atomic.

Combine these blocks <div class="bg-blue-500 text-white px-4 py-2 rounded shadow-lg"> and you get a beautiful button. No naming required, no CSS files to write—everything is done directly in HTML, making it incredibly easy for developers:

  • Lightning-fast development speed, easily creating card layouts
  • Consistent styling with all sizes and colors following Tailwind's naming standards, reducing CSS management pain
  • Easy maintenance—want to change the theme color? Modify the config file and everything updates without checking for missing pieces

Even better, Tailwind has built-in Purge functionality that automatically removes unused Tailwind classes from your website, ensuring the final CSS file is very small and dramatically speeding up website loading.

When Tailwind Meets Hugo Theme Distribution

Hugo is great, and Tailwind is fantastic, but this is like putting soy sauce on pizza—combining these two is a complete disaster. When you develop a personal theme for your own use, Tailwind is perfectly fine. However, when this theme is distributed downstream, it becomes a disaster for users.

I've identified five options, each with serious problems that violate the design principles of both Hugo and Tailwind.

[!NOTE]
Note that we're discussing the scenario where Hugo + Tailwind is used as themes distributed to downstream users.

Option 1: Pre-compile + Purge + User Custom CSS

Theme developers pre-compile Tailwind into traditional CSS, purge unused styles, and distribute the compiled small CSS to users. This is the most Tailwind-spirit-compliant usage and is currently the CSS distribution method for themes like Blowfish and hugo-theme-tailwind. This method results in small final CSS files, users don't need to install anything extra, maintaining Hugo's zero-dependency design. If users need customization, they write their own custom.css, which is the same usage pattern as non-Tailwind themes. So what's the problem?

Understanding Difficulties: Users face a massive CSS file. Want to override a style? First you need to:

  • Understand Tailwind's naming conventions
  • Find the corresponding CSS selector (maybe .bg-blue-500 corresponds to background-color: #3b82f6)
  • Or have sufficient Tailwind knowledge to understand the structure from main.css
  • Or treat the compiled pure CSS as regular CSS, but that's at least 2,000 lines of CSS—Blowfish theme even bloats to 5,000 lines

This is like giving someone a car repair manual and saying "all the parts you need are in here, find them yourself."

Not only is overriding CSS extremely difficult, but customizing elements that don't require overrides is also troublesome. Due to Tailwind's characteristics, all HTML elements lack semantic class names—you'll almost never see something like class="card__item". Users only see the LEGO blocks mentioned earlier. This makes CSS selectors become strings of incomprehensible Tailwind classes, so every class in the CSS file needs comments or it's completely incomprehensible. Not only that, user custom CSS has an even bigger problem: upstream can hardly modify HTML classes, or all downstream custom CSS becomes invalid.

Option 2: Pre-compile + Purge + User Override Hugo Templates

Since CSS overrides are problematic, what about directly using Hugo's override system to override files? This also has major problems. One of Tailwind's core values—dynamic extensibility—completely disappears after upstream purging. Users want to add a text-red-500 class? Sorry, this class was removed during compilation.

So users completely lose Tailwind's dynamic extensibility, and even custom CSS becomes extremely difficult.

Option 3: Pre-compile + No Purge

The previous approach of following standard Tailwind usage (pre-compile + purge) caused low extensibility problems. So what problems arise when developers choose not to remove any Tailwind classes to preserve extensibility?

  1. Massive CSS files: Complete Tailwind CSS can reach several MB, which is disastrous for website performance.
  2. Impossible to predict: Developers must guess which classes users might need. Need bg-pink-300? Need text-7xl? Need color transparency? Sorry, nobody knows—these requirements are impossible to predict.

Moreover, even without purging, you still can't escape the original problems. Custom CSS selectors are still difficult to specify, and overriding HTML files still can't freely specify all classes.

Option 4: No Compilation

Since pre-compilation causes so many problems, let's just not compile at all. Developers directly provide Tailwind's source files and require users to compile themselves.

Significantly Raised Technical Barriers: Users now need to:

  • Install Node.js and npm
  • Understand package.json and dependency management
  • Learn to use Tailwind CLI or PostCSS
  • Handle version compatibility issues

For developers, these aren't problems, but for theme users, these are major issues. Most people find using the command line painful enough—how can you ask them to install so many dependencies and frontend knowledge? This also creates a logical contradiction: if users need knowledge of the JavaScript ecosystem, why not just use Next.js, Nuxt.js, or Astro? This seriously violates Hugo's philosophy—Hugo's core competitive advantage of zero dependencies and out-of-the-box usage completely disappears.

Besides this, there's an obvious problem: theme installation becomes extremely tedious. Originally, you only needed simple submodule installation, but now you need to install Node.js and npm, copy tailwind.config.js + all CSS files + package.json from the theme directory to the user's root directory.

For developers, this might be very simple, but such tedious installation is disastrous for a product. Your user base is general users, not developers. Even if they are developers acting as users, they want simple methods.

Option 5: Hugo Integrated Compilation

Maybe someone will say "you didn't read the documentation—Hugo has built-in Tailwind integration!" Indeed, Hugoplate uses this distribution method, perfectly combining two worlds with Hugo's built-in TailwindCSS function. This eliminates the need-to-pre-compile-or-not problem. Although users still need to install Node.js and npm, they don't need to understand complex build systems. Developers can enjoy Tailwind's convenience while users can see style changes in real-time.

It seems perfect, but it actually causes the same disaster. This solution summarizes the entire article's problem: "The fundamental conflict between Hugo's theme system and Tailwind":

Traditional CSS Customization Flow:

  1. User wants to modify button color
  2. Write .btn { background: red; } in static/css/custom.css
  3. Done

Tailwind Customization Flow:

  1. User wants to modify button color
  2. Discovers they need to modify class attributes in HTML templates
  3. Finds button definition in themes/mytheme/layouts/partials/button.html
  4. Copies file to layouts/partials/button.html
  5. Changes bg-blue-500 to bg-red-500
  6. Every time the theme updates, manually merge changes to this file

Maintenance Hell: Imagine a user customizes 10 partial files, then the theme releases a new version. They need to:

  • Compare differences in each file
  • Manually merge changes
  • Test to ensure no functionality is broken
  • Handle potential conflicts

This is an order of magnitude more complex than simple CSS overrides.

Here we discover that regardless of whether Tailwind is pre-compiled, it can never be user-friendly in Hugo's architecture. When users try to adjust small details, they find themselves at a loss or forced to become frontend developers.

The Contradiction

Both Hugo and Tailwind are based on the concept of independence, but combining them becomes soy sauce pizza. Applying both tools simultaneously creates serious logical conflict problems, especially when publishing as upstream themes:

  • Hugo's override system requires style and structure separation; Tailwind requires their coupling
  • Hugo themes assume styles can be overridden through CSS; Tailwind requires modifying HTML
  • Hugo's value lies in simplification; Tailwind's value lies in dynamism

So I don't believe this fundamental design thinking difference allows these two tools to each achieve maximum efficiency. Using both simultaneously will definitely sacrifice one party's core value, ultimately leading to a project that's neither fish nor fowl.

Conclusion

Since Tailwind has issues in Hugo, what should be the next step?

My strongest advice to developers is to give up on using Tailwind with Hugo. These two tools have fundamental design logic conflicts that cannot be resolved. I also strongly recommend investigating how platforms like Docusaurus, Vitepress, and Next.js handle the "user" aspect of design. For example, Docusaurus, based on the Infima system, allows specifying element colors (see here) and even provides a color palette for users to easily customize. Vitepress uses a similar concept. Additionally, I suggest using Hugo’s built-in PostCSS/Sass capabilities, which seamlessly integrate modern CSS features without causing HTML and CSS coupling issues.

For users, to put it nicely, you can do simple checks based on your own conditions:

  1. Important: Does the theme provide CSS custom properties to adjust colors and spacing?
  2. Does the theme require a Node.js environment?
  3. Does customization require extensive HTML template modifications? (This issue basically only appears in themes using Tailwind)

But who thinks this much when choosing themes? Still pick whichever theme looks good. Even if maintenance burden increases, at least that theme looks good—a theme you don't like isn't useful no matter how easy it is to maintain.

The biggest lesson we can learn from this is "always do your research in advance." Once you choose your tech stack and start developing a project, it's too late to regret after you're committed.

Top comments (0)