Skip to content

[v4] Variant classes (hover:, disabled:, etc.) are not generated when using prefix() - Breaking for any project using CSS prefix isolation #20284

Description

@mk-startialab

What version of Tailwind CSS are you using?

v4.3.1

What build tool (or framework if it abstracts the build tool) are you using?

@tailwindcss/vite 4.3.1, Vite 6.x, Vue 3 (also reproducible in Tailwind Play)

What version of Node.js are you using?

v24.15.0

What browser are you using?

Chrome

What operating system are you using?

macOS

Reproduction URL

https://play.tailwindcss.com/aG2zTlpta0

Describe your issue

When using prefix() in the @import "tailwindcss" directive, variant modifier classes (hover:, disabled:, focus:, etc.) are completely absent from the generated CSS. Base utility classes (without variants) generate correctly.

CSS:

@import "tailwindcss" prefix(myapp);

HTML:

<button class="myapp:bg-blue-500 hover:myapp:bg-blue-700 myapp:text-white myapp:px-4 myapp:py-2">
  Hover me
</button>
<button class="myapp:bg-gray-300 disabled:myapp:bg-gray-200" disabled>
  Disabled button
</button>

Expected behavior:

CSS generates .hover\:myapp\:bg-blue-700:hover and .disabled\:myapp\:bg-gray-200:disabled, making interactive states functional.

Actual behavior:

Only base classes like .myapp\:bg-blue-500 are generated. All variant selectors produce 0 rules. Hover, disabled, and focus states are completely non-functional visually.

You can verify this in the reproduction URL above: open the Generated CSS panel and observe that no :hover or :disabled rules are present, even though hover:myapp:bg-blue-700 and disabled:myapp:bg-gray-200 are explicitly used in the HTML.

@source inline("hover:myapp:bg-blue-700") was also tested as a workaround but had no effect.

Impact:

This bug silently breaks all interactive states (hover, disabled, focus, focus-visible, placeholder, etc.) in any project using prefix(). Since base classes still generate normally, the issue is not immediately obvious and can go undetected until visual inspection.

The prefix() feature is the primary mechanism for CSS namespace isolation — used when integrating with CMS platforms, design systems, or any environment where global CSS collisions are a concern. Making prefix() incompatible with variants effectively removes its utility for real-world production use.

Workaround:

Manually writing CSS selectors in @layer utilities:

@layer utilities {
  @media (hover: hover) {
    .hover\:myapp\:bg-blue-700:hover { background-color: #1d4ed8; }
  }
  .disabled\:myapp\:bg-gray-200:disabled { background-color: #e5e7eb; }
}

This is not a scalable solution as every new variant class requires a manual CSS entry.

Related:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions