Need Help: HTML Validator Failing on Tailwind CSS Classes in Hugo Templates

Hi

I’m working on a Hugo project that uses Tailwind CSS, and I’m running into an issue with my pre-commit hooks. The HTML validator (html-validate) is failing because Tailwind’s utility classes don’t follow the kebab-case pattern that our linter enforces.

The Problem:

My CI/CD pipeline is failing with errors like:

class "h-[76px]" does not match the configured pattern "kebabcase"
class "top-1/2" does not match the configured pattern "kebabcase"  
class "-translate-y-1/2" does not match the configured pattern "kebabcase"

These are valid Tailwind classes in my Hugo templates (layouts/**/*.html), but they use:

  • Arbitrary values: h-[76px], text-[#001C3F]

  • Fractions: top-1/2, w-1/3

  • Decimals: right-2.5

  • Negative values: -translate-y-1/2

What I’ve Tried:

I know I could disable the class-pattern rule entirely, but my team wants to keep HTML validation enabled to catch actual issues while allowing Tailwind’s syntax.

My Question:

Has anyone successfully configured html-validate (or similar HTML linters) to work with Tailwind CSS in Hugo templates? I’m looking for a way to:

  1. Keep the kebab-case validation for regular HTML classes

  2. Allow Tailwind’s special class patterns

  3. Not disable HTML validation completely

My Current Setup:

  • Hugo (latest version)

  • Tailwind CSS via PostCSS

  • html-validate in pre-commit hooks

  • CI/CD with GitHub Actions

Any configuration examples or alternative approaches would be greatly appreciated! Thanks in advance!

from the html-validate docs for class-pattern

This rule takes an optional object:

{
  "pattern": "kebabcase"
}

Pattern

  • type: string | string[]
  • default: "kebabcase"

Either one of the presets or a custom regular expression.

sounds as you may use a list of patterns
a pattern may be a regex.

with this you should be able to allow your custom (css invalid) class names.


I’m not used to tailwind, but for curiosity.
where do that classes come from? is that a common pattern with tailwind?

How would you expect a validator to be useful if it were to accept “arbitrary values”? That is, IMO, logically impossible.

Not to mention that h-[76px] is weird. That sets the height of an element to a fixed pixel value – how’s that going to work with responsive design?

The validation is about the format of the classnames. Kebabcase vs. camel case and such. Tailwind works. Responsive design works via classnames that are separated via (tadah) :. I think OP might be advised best to not check for these, the regexp will be crazy. I had to do one for Astro filenames with slugs in it (also brackets) and ended up with a monster like this:

regex:(?:[a-z][a-z0-9]*|\[[a-z][a-z0-9]*(?:-[a-z0-9]+)*\])(?:-(?:[a-z][a-z0-9]*|\[[a-z][a-z0-9]*(?:-[a-z0-9]+)*\]))*

tailwind needs more than that to take : into account between those groups. Also it should be only one group of [something] in brackets and it must be the last item.

To check for these classes it will end up something like this (placeholders for the actual regexpes):

group1 (somestring including -, not :)
group2 (: and (somestring, not : or -))*  (none, one or more of these)
group3 (somestring including -, not : OR somestring in brackets) (none (if group2 has 1 or more), one or more of these)

Tip 1: Go to the Tailwind discord, you might find people there that had this issue too
Tip 2: Use a vscode plugin to do these linting jobs while working on the files and remove it from CI - by the way, the tailwind plugin might have the actual regexp in it somewhere. Might make sense to look into that code.

:+1: for 1 and 2 @davidsneighbour

and some thoughts:

according to the docs the patternS are positive matches - and the first matching pattern results in valid.

No need for a one shot Monster here, depending on what “arbitrary” values are and how dynamic that is

so a bunch of simple regexes will do maybe we need a dozen of that :wink:

  • kebabcase
  • h-[\d+px]
  • text-[#[\dA-F]+]
  • (?:top|w)-1/[1-4]
  • right-\d.\d
  • -translate-[xy]-1/[1-4]

Without knowing what exactly happens there in tailwind I would consider:

  • WHO is writing these - If it’s all in the templates, than it’s a limited number of users …
  • If this is by users styling everything on their own using classes in markdown, well…
  • Validating a “source” (tailwind) using target format validator (html-validate) seems complicated and needs more validation exceptions: <div {{ id $cond }}class="yes"{{end}}>...</div>
  • maybe you could do some test build for the templates on a PR…

right: