-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Description
Technical Configuration
| What version of Tailwind CSS are you using? |
|---|
| Next (v4, latest) |
| What build tool (or framework if it abstracts the build tool) are you using? |
| `pnpm build && pnpm test` |
| What version of Node.js are you using? |
| v21.6.1 |
| What browser are you using? |
| N/A |
| What operating system are you using? |
| macOS |
Reproduction URL
The accepted test results from #13596
Describe your issue
attn @adamwathan @RobinMalfait
This issue delves into some of the issues surrounding why the parser doesn't properly handle supports-[content:"("]:grid, first explored in #13596. This specific case turned out to be quite the rabbit hole.
From what I can tell, Tailwind's Rust parser runs into issues related to its quote_stack and bracket_stack when encountering a construct like '('.
None of these process correctly, apparently for this same reason:
supports-[content:'(']supports-[content:'[']supports-[content:'"']supports-[content:"'"]
This Tailwind Play example demonstrates the issue I'm describing: https://play.tailwindcss.com/EAhSfQFfTa
I can think of a few potential solutions, all of which come with trade-offs:
-
We could interpret everything after an open quote as a string until we hit the closing quote. This would break some things, but we'd be able to handle brackets or quotes within strings.
-
We could always ignore
bracket_stackcharacters withinquote_stackcharacters, but not ignorequote_stackcharacters withinbracket_stackcharacters. -
Specifically enforce escaping characters related to
quote_stackandbracket_stackwhen they're in a string context. This would mean thatsupports-[content:"("]would need to be written assupports-[content:"\("]to work.supports-[content:'(']->supports-[content:'\(']supports-[content:'[']->supports-[content:'\[']supports-[content:'"']->supports-[content:'\"']supports-[content:\"'\"]->supports-[content:\"\'\"]
☝🏼 this last one might be problematic for more reasons than one, related to escaping the same quotes used when opening theclassattribute as well as escaping the innerquote_stackcharacter
-
We could strictly enforce escaping special characters like these in string contexts.
This might be most consistent and with common escape patterns, but it would also be one of the most disruptive solutions.
In the case of my Multi plugin, for example, I was surprised—when chatting with @RobinMalfait (see discussion)—to discover that nesting quotes in arbitrary values is not only supported but that they appear to be infinitely nestable.
In other words, this works:
multi-['multi-['multi-['font-bold']'];text-green-700;before:content-['$']']If escapes were required to resolve this issue, each level of nesting might require a greater level of escape characters, which could get out of hand quickly. For example, the above would need to be written like this:
multi-['multi-[\'multi-[\\\'font-bold\\\']\'];text-green-700;before:content-[\'$\']']Of course, it would be convenient for me not to need to update my plugin(s) with new syntax and safety measures again, and such escaping would probably render my plugin too cumbersome to justify using. However, greater parity with CSS is probably a higher and more evergreen standard here.
Simply wrapping the arbitrary value in quotes was an easy fix, but the magic around it allowing nested quotes may be the culprit behind
"("not working, to some degree.
I'm new to Rust, so all of this is "to the best of my current knowledge" from what I could grok reading through the parser. I also consulted a colleague who is more experienced with Rust to corroborate my findings and theories, and they came to roughly the same conclusions.