From 8567c2fde39a144d5eb1536ae77db58468a4bb38 Mon Sep 17 00:00:00 2001 From: Puneet Dixit Date: Thu, 21 May 2026 18:22:49 +0530 Subject: [PATCH] fix: reset grid area spans when overriding areas --- lib/hacks/grid-utils.js | 65 ++++++++++++++++++++++++++++++++++++--- test/autoprefixer.test.js | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/lib/hacks/grid-utils.js b/lib/hacks/grid-utils.js index d423bfc0c..2d0b16285 100644 --- a/lib/hacks/grid-utils.js +++ b/lib/hacks/grid-utils.js @@ -358,6 +358,30 @@ function selectorsEqual(ruleA, ruleB) { }) } +function selectorStartsWith(base, selector) { + if (!selector.startsWith(base)) { + return false + } + + let next = selector[base.length] + return !next || /[\s.#[:>+~]/.test(next) +} + +function selectorMayOverlap(previous, current) { + let base = previous.replace(/\s*\*\s*/g, ' ').replace(/\s+/g, ' ').trim() + let selector = current.replace(/\s+/g, ' ').trim() + + return !base || selectorStartsWith(base, selector) +} + +function selectorsMayOverlap(previous, current) { + return previous.some(previousSelector => { + return current.some(currentSelector => { + return selectorMayOverlap(previousSelector, currentSelector) + }) + }) +} + /** * Parse data from all grid-template(-areas) declarations * @param {Root} css css root @@ -483,6 +507,7 @@ function insertAreas(css, isDisabled) { // we need to store the rules that we will insert later let rulesToInsert = {} + let previousGridAreas = [] css.walkDecls('grid-area', gridArea => { let gridAreaRule = gridArea.parent @@ -515,6 +540,20 @@ function insertAreas(css, isDisabled) { return false } + function shouldResetSpan(rule, area, dimension) { + return previousGridAreas.some(previous => { + if (previous.data !== data) { + return false + } + if (!selectorsMayOverlap(previous.selectors, gridAreaRule.selectors)) { + return false + } + + let previousArea = rule.areas[previous.value] + return previousArea && previousArea[dimension].span > area[dimension].span + }) + } + // create the empty object with the key as the last area name // e.g if we have templates with "a b c" values, "c" will be the last area if (!rulesToInsert[lastArea]) { @@ -553,8 +592,10 @@ function insertAreas(css, isDisabled) { if ((!rule.hasDuplicates || !hasDuplicateName) && !rule.params) { // grid-template has no duplicates and not inside media rule + let addRowSpan = shouldResetSpan(rule, area, 'row') + let addColumnSpan = shouldResetSpan(rule, area, 'column') - getMSDecls(area, false, false) + getMSDecls(area, addRowSpan, addColumnSpan) .reverse() .forEach(i => gridAreaRule.prepend( @@ -572,8 +613,11 @@ function insertAreas(css, isDisabled) { // grid-template has duplicates and not inside media rule let cloned = gridAreaRule.clone() cloned.removeAll() + let addRowSpan = area.row.updateSpan || shouldResetSpan(rule, area, 'row') + let addColumnSpan = + area.column.updateSpan || shouldResetSpan(rule, area, 'column') - getMSDecls(area, area.row.updateSpan, area.column.updateSpan) + getMSDecls(area, addRowSpan, addColumnSpan) .reverse() .forEach(i => cloned.prepend( @@ -604,7 +648,11 @@ function insertAreas(css, isDisabled) { // grid-template has duplicates and not inside media rule // and the selector is complex gridAreaRule.walkDecls(/-ms-grid-(row|column)/, d => d.remove()) - getMSDecls(area, area.row.updateSpan, area.column.updateSpan) + let addRowSpan = area.row.updateSpan || shouldResetSpan(rule, area, 'row') + let addColumnSpan = + area.column.updateSpan || shouldResetSpan(rule, area, 'column') + + getMSDecls(area, addRowSpan, addColumnSpan) .reverse() .forEach(i => gridAreaRule.prepend( @@ -622,8 +670,11 @@ function insertAreas(css, isDisabled) { // rules and merge them easily let cloned = gridAreaRule.clone() cloned.removeAll() + let addRowSpan = area.row.updateSpan || shouldResetSpan(rule, area, 'row') + let addColumnSpan = + area.column.updateSpan || shouldResetSpan(rule, area, 'column') - getMSDecls(area, area.row.updateSpan, area.column.updateSpan) + getMSDecls(area, addRowSpan, addColumnSpan) .reverse() .forEach(i => cloned.prepend( @@ -661,6 +712,12 @@ function insertAreas(css, isDisabled) { } } + previousGridAreas.push({ + data, + selectors: gridAreaRule.selectors, + value + }) + return undefined }) diff --git a/test/autoprefixer.test.js b/test/autoprefixer.test.js index d5ba847ad..d6801bb74 100644 --- a/test/autoprefixer.test.js +++ b/test/autoprefixer.test.js @@ -1125,6 +1125,60 @@ test('should merge complex duplicate grid-area rules successfully', () => { equal(result.css, output) }) +test('resets grid-area spans when overriding a broader area', () => { + let input = `/* autoprefixer grid: autoplace */ +body { + display: grid; + grid-template: + "other other" + "one two"; +} + +body > * { + grid-area: other; +} + +body > .one { + grid-area: one; +} + +body > .two { + grid-area: two; +}` + let output = `/* autoprefixer grid: autoplace */ +body { + display: -ms-grid; + display: grid; + -ms-grid-rows: auto; + grid-template: + "other other" + "one two"; +} + +body > * { + -ms-grid-row: 1; + -ms-grid-column: 1; + -ms-grid-column-span: 2; + grid-area: other; +} + +body > .one { + -ms-grid-row: 2; + -ms-grid-column: 1; + -ms-grid-column-span: 1; + grid-area: one; +} + +body > .two { + -ms-grid-row: 2; + -ms-grid-column: 2; + -ms-grid-column-span: 1; + grid-area: two; +}` + let result = postcss([prefixer('grid-area')]).process(input) + equal(result.css, output) +}) + test('ignores values for CSS3PIE props', () => { let css = read('pie') equal(postcss([compiler]).process(css).css, css)