## Summary `shadow-*`, `text-shadow-*`, `drop-shadow-*`, and `inset-shadow-*` accept a bare (non-arbitrary) opacity modifier like `/50`, but the named-size branch of all four utilities validates it with `isPositiveInteger(candidate.modifier.value)` instead of `isValidOpacityValue(candidate.modifier.value)` — the helper every other opacity/alpha modifier in the codebase uses (`asColor`, used by `bg-*`, `text-*`, `border-*`, `ring-*`, `fill-*`, `stroke-*`, `decoration-*`, `accent-*`, `caret-*`, `outline-*`, `placeholder-*`, `divide-*`). This means a fractional modifier like `/12.5` is silently ignored for a *named size* (`shadow-sm/12.5` behaves exactly like `shadow-sm`, dropping the modifier), while the exact same `/12.5` modifier works correctly on the *color* variant of the same utility (`shadow-red-500/12.5` → `color-mix(in oklab, var(--color-red-500) 12.5%, transparent)`), since that path already goes through `asColor`. `drop-shadow-*` is worse: its named-size branch has an extra guard (`if (candidate.modifier && !alpha) return`) that bails out of the *entire* utility when a modifier is present but couldn't be resolved to an alpha — so `drop-shadow-sm/12.5` produces no CSS at all. This isn't a case of fractional percentages being unsupported by design — the CHANGELOG entry that introduced `shadow-*/<alpha>` explicitly describes it as controlling shadow **opacity**, and the color branch of these same utilities already supports fractional values (`shadow-red-500/2.25`, `/2.5`, `/2.75` are covered by existing tests). The named-size branch just never got the same treatment. **Repro** (verified with `pnpm --filter tailwindcss exec vitest run`): - `shadow-red-500/12.5` → `color-mix(in oklab, var(--color-red-500) 12.5%, transparent)` (correct) - `shadow-sm/12.5` → identical output to plain `shadow-sm` (modifier silently dropped) - `drop-shadow-sm/12.5` → **no CSS generated at all** - `shadow-sm/50` (integer, control) → works correctly ## Fix Replaced `isPositiveInteger(candidate.modifier.value)` with `isValidOpacityValue(candidate.modifier.value)` in the four affected utility definitions in `packages/tailwindcss/src/utilities.ts` (`shadow`, `text-shadow`, `drop-shadow`, `inset-shadow`). No other changes were needed — once `alpha` resolves correctly, `drop-shadow`'s existing `if (candidate.modifier && !alpha) return` guard naturally stops bailing out, since `alpha` is no longer `undefined` for valid fractional modifiers. ## Test plan - Added a regression test in `packages/tailwindcss/src/utilities.test.ts` covering `shadow-sm/12.5`, `text-shadow-sm/12.5`, `drop-shadow-sm/12.5`, and `inset-shadow-sm/12.5`. - Verified via `pnpm --filter tailwindcss exec vitest run` that this test fails (modifier dropped / empty output) with the fix reverted, and passes with it applied. - Ran the full `tailwindcss` package test suite (`pnpm --filter tailwindcss exec vitest run`) — 4685 tests passing, no regressions. - Verified formatting on the changed files with `npx prettier --check`. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|---|---|---|
| .. | ||
| src | ||
| tests | ||
| index.css | ||
| package.json | ||
| playwright.config.ts | ||
| preflight.css | ||
| README.md | ||
| theme.css | ||
| tsconfig.json | ||
| tsup.config.ts | ||
| utilities.css | ||
| vitest.config.ts | ||
A utility-first CSS framework for rapidly building custom user interfaces.
Documentation
For full documentation, visit tailwindcss.com.
Community
For help, discussion about best practices, or feature ideas:
Discuss Tailwind CSS on GitHub
Contributing
If you're interested in contributing to Tailwind CSS, please read our contributing docs before submitting a pull request.