Skip to content

"--opacity-*: initial" doesn't disable default opacity utilities like other theme namespaces #19310

@felipenario

Description

@felipenario

What version of Tailwind CSS are you using?

v4.1.17

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

Rolldown-Vite 7.2.2

What version of Node.js are you using?

v22.14.0

What browser are you using?

N/A (affects CSS generation, not browser-specific)

What operating system are you using?

Linux

Reproduction URL

https://github.com/felipenario/tailwind-issue-repo

Describe your issue

When using --opacity-*: initial in the @theme block to clear the opacity namespace (as documented for disabling default utilities), the opacity-related utilities continue to accept bare values like opacity-50, opacity-75, etc.

@import "tailwindcss";

@theme {
  /* This should disable default opacity utilities */
  --opacity-*: initial;
}
<!-- These classes should NOT work but they do -->
<div class="opacity-50">This works but shouldn't</div>
<div class="opacity-75">This works but shouldn't</div>
<div class="backdrop-opacity-50">This works but shouldn't</div>

This behavior is inconsistent with other utilities like spacing, which correctly respect the namespace clearing mechanism.

Comparison with spacing utility (working correctly):

@theme {
  --spacing-*: initial;
}
<!-- These classes correctly DON'T work -->
<div class="p-4">❌ Doesn't generate</div>
<div class="m-8">❌ Doesn't generate</div>

Root Cause

After investigating the source code in packages/tailwindcss/src/utilities.ts, I found that:

spacing utility checks if theme exists (~line 567):

values: theme.get(['--spacing']) ? DEFAULT_SPACING_SUGGESTIONS : []

opacity utility doesn't check (~line 4997):

handleBareValue: ({ value }) => {
  // Missing: if (!theme.get(['--opacity'])) return null
  if (!isValidOpacityValue(value)) return null
  return `${value}%`
},

The spacing utility verifies the theme namespace exists before accepting bare values, while opacity and backdrop-opacity always accept bare values regardless of theme configuration.

Possible Fix

Add theme existence checks to match the behavior of other utilities:

For opacity (lines ~4994-5010):

functionalUtility('opacity', {
  themeKeys: ['--opacity'],
  handleBareValue: ({ value }) => {
+   if (!theme.get(['--opacity'])) return null
    if (!isValidOpacityValue(value)) return null
    return `${value}%`
  },
  handle: (value) => [decl('opacity', value)],
})

suggest('opacity', () => [
  {
-   values: Array.from({ length: 21 }, (_, i) => `${i * 5}`),
+   values: theme.get(['--opacity']) ? Array.from({ length: 21 }, (_, i) => `${i * 5}`) : [],
    valueThemeKeys: ['--opacity'],
  },
])

This fix was tested and it worked locally

Question

Is this inconsistency intentional, or should opacity utilities behave the same way as other utilities when their theme namespace is cleared with --namespace-*: initial?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions