fix: improve no-unreachable-media-conditions for nested at-rules#98
Merged
bartveneman merged 5 commits intomainfrom Mar 26, 2026
Merged
fix: improve no-unreachable-media-conditions for nested at-rules#98bartveneman merged 5 commits intomainfrom
bartveneman merged 5 commits intomainfrom
Conversation
Detects when nested @media or @container rules create an impossible AND condition across rule boundaries — e.g. an outer min-width: 1000px combined with an inner max-width: 500px is equivalent to a single rule with both conditions, which can never match. The detection logic is extracted into a new shared utility collect_bounds_from_prelude() in media-conditions.ts, and the rule reuses find_contradictory_feature() from the same module to avoid duplicate detection code. Edge cases handled: - Comma-separated (OR) ancestor queries: skipped, too complex to AND - not/or operators in any query: skipped - @container unnamed nesting: skipped (each applies to different containers) - @container named nesting: only flagged when both rules have the same name - Already-contradictory inner rules: skipped, handled by no-unreachable-media-conditions - Three-level deep conflicts: detected by accumulating ancestor bounds https://claude.ai/code/session_019rwTo8FJ5pULsuchZ3f2PN
Bundle ReportChanges will increase total bundle size by 3.42kB (5.37%) ⬆️
Affected Assets, Files, and Routes:view changes for bundle: stylelintPlugin-esmAssets Changed:
Files in
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #98 +/- ##
==========================================
- Coverage 98.15% 97.85% -0.30%
==========================================
Files 34 34
Lines 920 981 +61
Branches 234 250 +16
==========================================
+ Hits 903 960 +57
- Misses 14 18 +4
Partials 3 3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Rather than a separate rule, the detection of impossible conditions across nested @media rules now lives in no-unreachable-media-conditions alongside the existing flat-rule detection. Nested @media rules implicitly AND their conditions with ancestor @media rules, so this pattern is equivalent to a single rule with both conditions: @media (min-width: 1000px) { @media (max-width: 500px) { ... } ← now flagged } A new rejected_nested message distinguishes these from intra-rule contradictions. The shared collect_bounds_from_prelude() utility (added to media-conditions.ts) handles prelude parsing for the ancestor chain traversal. https://claude.ai/code/session_019rwTo8FJ5pULsuchZ3f2PN
Instead of bailing out when an ancestor has comma-separated queries (OR), take the cartesian product across all nesting levels and flag only when every possible combination is impossible. @media (min-width: 1000px), (min-width: 900px) { @media (max-width: 500px) { } ← flagged: both branches impossible } @media screen, (min-width: 1000px) { @media (max-width: 500px) { } ← not flagged: screen branch is fine } collect_bounds_from_prelude now returns (Bound[] | null)[] — one entry per comma-separated branch (null for branches containing `not`/`or`, which remain too complex to analyse and are conservatively treated as satisfiable). https://claude.ai/code/session_019rwTo8FJ5pULsuchZ3f2PN
…sible Previously the nested check only reported when every cartesian-product combination was contradictory. But a single impossible combination is already a bug — that branch of the outer @media will never reach the inner rule, regardless of whether other branches are fine: @media screen, (min-width: 1000px) { @media (max-width: 500px) {} ← now flagged } ↑ (min-width: 1000px) ∧ (max-width: 500px) is impossible even though the screen branch is still reachable. The ancestor-already-contradictory guard is also tightened: instead of checking each ancestor branch individually, we check the combined ancestor bounds for the specific combination path — this avoids noisy derivative reports when a higher-level nesting already created the contradiction. https://claude.ai/code/session_019rwTo8FJ5pULsuchZ3f2PN
- Cast node.parent as ContainerWithChildren | undefined: the PostCSS type includes Document in the union but Document nodes only appear in HTML-embedded CSS, never in regular stylesheet traversal. - Replace map+spread in cartesian() with explicit loops using concat, fixing the oxlint no-map-spread performance rule. https://claude.ai/code/session_019rwTo8FJ5pULsuchZ3f2PN
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.
closes #97
Summary
This PR improves
no-unreachable-media-conditionsthat detects when nested@mediaand@containerqueries create contradictory conditions that make the inner rule unreachable.Key Changes
**improve rule:
no-unreachable-media-conditions@mediaor@containerat-rulesmin-width: 100px) and range syntax (width >= 100px)not/oroperators)Comprehensive test suite
@mediawith width/height featuresUtility function:
collect_bounds_from_prelude(src/utils/media-conditions.ts)nullfor complex queries that can't be safely analyzed@mediaand@containerat-rulesImplementation Details
@containerrules, only ANDs bounds when both parent and child use the same non-empty container name. Unnamed containers are treated independently since they apply to different ancestor container elements.find_contradictory_featureutility to identify impossible conditionsno-unreachable-media-conditions)not/oroperators to prevent false positiveshttps://claude.ai/code/session_019rwTo8FJ5pULsuchZ3f2PN