Skip to content

Preserve block-less at-rules when keep_at_rules is enabled - #768

Merged
Stranger6667 merged 3 commits into
Stranger6667:masterfrom
jmortlock:fix-keep-at-rules-without-block
Aug 8, 2026
Merged

Preserve block-less at-rules when keep_at_rules is enabled#768
Stranger6667 merged 3 commits into
Stranger6667:masterfrom
jmortlock:fix-keep-at-rules-without-block

Conversation

@jmortlock

Copy link
Copy Markdown
Contributor

Problem

With keep_at_rules: true, an at-rule terminated by a semicolon instead of a block — @import being the common case — loses its prelude and terminator. AtRuleFilteringParser writes @ + the rule name in parse_prelude and appends the prelude/body in parse_block, but never implements rule_without_block (whose default rejects the rule), so the already-written bare @import fuses with the following rule:

/* input */
@import url("print.css");
@media (max-width: 600px) { h1 { font-size: 18px; } }

/* output */
@import@media (max-width: 600px) { h1 { font-size: 18px; } }

Browsers treat the fused text as one invalid rule and discard it — in Chromium the resulting <style> sheet parses to cssRules.length === 0 — so a single @import silently disables every at-rule the option was meant to keep. We hit this inlining MJML email output, where the responsive @media block stopped applying on mobile clients.

Fix

Implement rule_without_block to emit the prelude and a terminating semicolon, mirroring how parse_block emits blocked at-rules:

@import url("print.css"); @media (max-width: 600px) { h1 { font-size: 18px; } }

Includes a regression test (fails on master with the fused output above) and a changelog entry. Full suite passes locally apart from the four tests that need the 127.0.0.1:1234 stylesheet server.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.05%. Comparing base (5142700) to head (62a1e25).

Files with missing lines Patch % Lines
css-inline/src/html/selectors/attr_value.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #768      +/-   ##
==========================================
+ Coverage   92.02%   92.05%   +0.02%     
==========================================
  Files          17       17              
  Lines        2584     2593       +9     
==========================================
+ Hits         2378     2387       +9     
  Misses        206      206              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented Aug 8, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 12 untouched benchmarks


Comparing jmortlock:fix-keep-at-rules-without-block (62a1e25) with master (5142700)

Open in CodSpeed

AtRuleFilteringParser writes `@` + the rule name in parse_prelude and
appends the prelude and body in parse_block, but never implements
rule_without_block, so an at-rule terminated by a semicolon instead of
a block (e.g. `@import`) left a bare `@import` fused to the following
rule: `@import@media (max-width: 600px) { ... }`. Browsers treat that
as one invalid rule and discard the entire stylesheet, so enabling
keep_at_rules could remove every kept at-rule's effect whenever an
`@import` preceded them.

Implement rule_without_block to emit the prelude and a terminating
semicolon, matching how parse_block emits blocked at-rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jmortlock
jmortlock force-pushed the fix-keep-at-rules-without-block branch from b44dbe2 to 08b9e0b Compare August 8, 2026 08:04
Pre-existing CI failures unrelated to this change: clippy 1.97's new
question_mark and useless_borrows_in_formatting lints fire on
element.rs sibling traversal, attr_value.rs and the profiler's panic
message under -D warnings, and the pre-commit prettier hook
(v4.0.0-alpha.8) reformats bindings/javascript/wasm/index.html.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jmortlock
jmortlock force-pushed the fix-keep-at-rules-without-block branch from 08b9e0b to 5a6e381 Compare August 8, 2026 08:09
@Stranger6667

Copy link
Copy Markdown
Owner

Awesome! Thank you :)

Could you please add the same changelog entry to all the bindings? as it is a user-visible change for non-rust users

The fix is user-visible in every language binding, so the Python,
Ruby, Java, C, PHP and JavaScript changelogs each get the same entry
under Unreleased, using each binding's own option spelling
(keep_at_rules / keepAtRules).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jmortlock

Copy link
Copy Markdown
Contributor Author

Done — added the same entry under [Unreleased] in all six binding changelogs (Python, Ruby, Java, C, PHP, JavaScript), using each binding's own option spelling (keep_at_rules / keepAtRules).

@Stranger6667
Stranger6667 merged commit 9a3e93d into Stranger6667:master Aug 8, 2026
84 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants