[13.x] Omit empty class/style attribute from @class and @style directives#59751
Open
damianlewis wants to merge 1 commit intolaravel:13.xfrom
Open
[13.x] Omit empty class/style attribute from @class and @style directives#59751damianlewis wants to merge 1 commit intolaravel:13.xfrom
class/style attribute from @class and @style directives#59751damianlewis wants to merge 1 commit intolaravel:13.xfrom
Conversation
|
Thanks for submitting a PR! Note that draft PRs are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface. Pull requests that are abandoned in draft may be closed due to inactivity. |
59ce410 to
0b26483
Compare
class/style attribute from @class and @style directivesclass/style attribute from @class and @style directives
0b26483 to
13fceb7
Compare
13fceb7 to
6f0ec4e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@class([...])and@style([...])currently renderclass=""/style=""on the target element when all conditions evaluate tofalse(or the input array is empty). The compiler traits hardcode the attribute wrapper around the result ofArr::toCssClasses/Arr::toCssStyles, so an empty string from the helper still produces an empty-valued attribute in the rendered HTML.This PR changes both compilers to emit the attribute only when the helper returns a non-empty string.
Before / after
Given:
When
$requiredisfalse:<span class=""></span><span></span>When
$requiredistrue, output is unchanged:<span class="highlight"></span>. Same shape for@style.Why this is a bug, not just cosmetic
The directive's API contract is "apply classes/styles conditionally." Emitting an empty attribute when no classes/styles apply leaks the compiler's internal wrapping into HTML, where downstream tooling observes it differently from the absent-attribute state:
[class]/[style]attribute selectors unexpectedly match elements with no styling intentelement.hasAttribute('class')returnstrueandgetAttribute('class')returns"", rather thanfalse/nullScope
Only the two compiler traits (
CompilesClasses,CompilesStyles) are changed.ComponentAttributeBag::__toString()is intentionally untouched because boolean HTML attributes (disabled,checked,required, etc.) flow through it and legitimately render with empty values per the HTML spec.classandstyleare value attributes (not boolean), so their dedicated compiler traits are the correct place for this fix.Related work
This PR complements #57467 ("Improved empty and whitespace-only string handling in Blade component attributes"), which takes a parallel approach inside
ComponentAttributeBag. Both changes move toward Vue-aligned attribute-emission behavior.Related: #57463, #57235.
Why this doesn't break typical usage
The observable change only occurs when every class in a
@class([...])or@style([...])call is conditional and all conditions evaluate tofalse. Templates with at least one unconditional class — the common pattern like@class(['base', 'active' => $cond])— are entirely unaffected.In the narrow case that is affected, the rendered output is semantically identical to the previous behavior: CSS rules matching specific class names still don't match (no classes are present either way); visual layout is identical. The only observable difference is attribute presence for tooling that specifically inspects it (e.g. CSS
[class]attribute selectors, JavaScripthasAttributechecks). This is uncommon enough that no existing tests in the framework exercised it, and the new behavior aligns with the Vue-style attribute emission referenced on #57467.Tests
BladeClassTest: updated the existing compilation assertion and added four runtime tests (empty array, all-false, mixed true/false, base class + all-false).BladeStyleTest: parallel coverage for@style.BladePhpStatementsTest: updated nine existing assertions whose expected strings referenced the old compiled output. No semantic change to those tests.Runtime tests use the
ob_start()+eval('?>'.$compiled)pattern already established inBladeBoolTest,BladeComponentsTest, andBladeEchoHandlerTest.