8000 Merge pull request #648 from tailwindcss/responsive-nested-at-rules · stacywebb/tailwindcss@c2118be · GitHub
Skip to content

Commit c2118be

Browse files
authored
Merge pull request tailwindlabs#648 from tailwindcss/responsive-nested-at-rules
Properly handle deeply nested at-rules inside of `@responsive`
2 parents a38f15f + b544feb commit c2118be

File tree

2 files changed

+136
-133
lines changed

2 files changed

+136
-133
lines changed

__tests__/responsiveAtRule.test.js

Lines changed: 130 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -6,114 +6,6 @@ function run(input, opts = config) {
66
return postcss([plugin(opts)]).process(input, { from: undefined })
77
}
88

9-
test('it can generate responsive variants for nested at rules', () => {
10-
const input = `
11-
@responsive {
12-
.banana { color: yellow; }
13-
.chocolate { color: brown; }
14-
15-
@supports(display: grid) {
16-
.grid\\:banana { color: blue; }
17-
.grid\\:chocolate { color: green; }
18-
}
19-
}
20-
`
21-
22-
const output = `
23-
.banana {
24-
color: yellow;
25-
}
26-
27-
.chocolate {
28-
color: brown;
29-
}
30-
31-
@supports(display: grid) {
32-
.grid\\:banana {
33-
color: blue;
34-
}
35-
36-
.grid\\:chocolate {
37-
color: green;
38-
}
39-
}
40-
41-
@media (min-width: 500px) {
42-
.sm\\:banana {
43-
color: yellow;
44-
}
45-
46-
.sm\\:chocolate {
47-
color: brown;
48-
}
49-
50-
@supports(display: grid) {
51-
.sm\\:grid\\:banana {
52-
color: blue;
53-
}
54-
55-
.sm\\:grid\\:chocolate {
56-
color: green;
57-
}
58-
}
59-
}
60-
61-
@media (min-width: 750px) {
62-
.md\\:banana {
63-
color: yellow;
64-
}
65-
66-
.md\\:chocolate {
67-
color: brown;
68-
}
69-
70-
@supports(display: grid) {
71-
.md\\:grid\\:banana {
72-
color: blue;
73-
}
74-
75-
.md\\:grid\\:chocolate {
76-
color: green;
77-
}
78-
}
79-
}
80-
81-
@media (min-width: 1000px) {
82-
.lg\\:banana {
83-
color: yellow;
84-
}
85-
86-
.lg\\:chocolate {
87-
color: brown;
88-
}
89-
90-
@supports(display: grid) {
91-
.lg\\:grid\\:banana {
92-
color: blue;
93-
}
94-
95-
.lg\\:grid\\:chocolate {
96-
color: green;
97-
}
98-
}
99-
}
100-
`
101-
102-
return run(input, {
103-
screens: {
104-
sm: '500px',
105-
md: '750px',
106-
lg: '1000px',
107-
},
108-
options: {
109-
separator: ':',
110-
},
111-
}).then(result => {
112-
expect(result.css).toMatchCss(output)
113-
expect(result.warnings().length).toBe(0)
114-
})
115-
})
116-
1179
test('it can generate responsive variants', () => {
11810
const input = `
11911
@responsive {
@@ -280,6 +172,136 @@ test('responsive variants are grouped', () => {
280172
})
281173
})
282174

175+
test('it can generate responsive variants for nested at-rules', () => {
176+
const input = `
177+
@responsive {
178+
.banana { color: yellow; }
179+
180+
@supports(display: grid) {
181+
.grid\\:banana { color: blue; }
182+
}
183+
}
184+
`
185+
186+
const output = `
187+
.banana {
188+
color: yellow;
189+
}
190+
191+
@supports(display: grid) {
192+
.grid\\:banana {
193+
color: blue;
194+
}
195+
}
196+
197+
@media (min-width: 500px) {
198+
.sm\\:banana {
199+
color: yellow;
200+
}
201+
202+
@supports(display: grid) {
203+
.sm\\:grid\\:banana {
204+
color: blue;
205+
}
206+
}
207+
}
208+
209+
@media (min-width: 1000px) {
210+
.lg\\:banana {
211+
color: yellow;
212+
}
213+
214+
@supports(display: grid) {
215+
.lg\\:grid\\:banana {
216+
color: blue;
217+
}
218+
}
219+
}
220+
`
221+
222+
return run(input, {
223+
screens: {
224+
sm: '500px',
225+
lg: '1000px',
226+
},
227+
options: {
228+
separator: ':',
229+
},
230+
}).then(result => {
231+
expect(result.css).toMatchCss(output)
232+
expect(result.warnings().length).toBe(0)
233+
})
234+
})
235+
236+
test('it can generate responsive variants for deeply nested at-rules', () => {
237+
const input = `
238+
@responsive {
239+
.banana { color: yellow; }
240+
241+
@supports(display: grid) {
242+
@supports(display: flex) {
243+
.flex-grid\\:banana { color: blue; }
244+
}
245+
}
246+
}
247+
`
248+
249+
const output = `
250+
.banana {
251+
color: yellow;
252+
}
253+
254+
@supports(display: grid) {
255+
@supports(display: flex) {
256+
.flex-grid\\:banana {
257+
color: blue;
258+
}
259+
}
260+
}
261+
262+
@media (min-width: 500px) {
263+
.sm\\:banana {
264+
color: yellow;
265+
}
266+
267+
@supports(display: grid) {
268+
@supports(display: flex) {
269+
.sm\\:flex-grid\\:banana {
270+
color: blue;
271+
}
272+
}
273+
}
274+
}
275+
276+
@media (min-width: 1000px) {
277+
.lg\\:banana {
278+
color: yellow;
279+
}
280+
281+
@supports(display: grid) {
282+
@supports(display: flex) {
283+
.lg\\:flex-grid\\:banana {
284+
color: blue;
285+
}
286+
}
287+
}
288+
}
289+
`
290+
291+
return run(input, {
292+
screens: {
293+
sm: '500px',
294+
lg: '1000px',
295+
},
296+
options: {
297+
separator: ':',
298+
},
299+
}).then(result => {
300+
expect(result.css).toMatchCss(output)
301+
expect(result.warnings().length).toBe(0)
302+
})
303+
})
304+
283305
test('screen prefix is only applied to the last class in a selector', () => {
284306
const input = `
285307
@responsive {

src/lib/substituteResponsiveAtRules.js

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ export default function(config) {
88
return function(css) {
99
const screens = config.screens
1010
const separator = config.options.separator
11-
const responsiveRules = []
11+
const responsiveRules = postcss.root()
1212
let finalRules = []
1313

1414
css.walkAtRules('responsive', atRule => {
1515
const nodes = atRule.nodes
16-
responsiveRules.push(...cloneNodes(nodes))
16+
responsiveRules.append(...cloneNodes(nodes))
1717
atRule.before(nodes)
1818
atRule.remove()
1919
})
@@ -24,34 +24,15 @@ export default function(config) {
2424
params: buildMediaQuery(screens[screen]),
2525
})
2626

27-
mediaQuery.append(
28-
// Filter out nested `atRules`; we'll process those separately
29-
responsiveRules.filter(rule => rule.type !== 'atrule').map(rule => {
30-
const cloned = rule.clone()
31-
cloned.selectors = _.map(rule.selectors, selector =>
27+
mediaQuery.append(_.tap(responsiveRules.clone(), clonedRoot => {
28+
clonedRoot.walkRules(rule => {
29+
rule.selectors = _.map(rule.selectors, selector =>
3230
buildSelectorVariant(selector, screen, separator, message => {
3331
throw rule.error(message)
3432
})
3533
)
36-
return cloned
3734
})
38-
)
39-
40-
mediaQuery.append(
41-
// Process nested `atRules`.
42-
responsiveRules.filter(rule => rule.type === 'atrule').map(atRule => {
43-
const clonedAtRule = atRule.clone()
44-
clonedAtRule.nodes.forEach(rule => {
45-
rule.selectors = _.map(rule.selectors, selector => {
46-
const selectorVariant = buildSelectorVariant(selector, screen, separator, message => {
47-
throw rule.error(message)
48-
})
49-
return selectorVariant
50-
})
51-
})
52-
return clonedAtRule
53-
})
54-
)
35+
}))
5536

5637
finalRules.push(mediaQuery)
5738
})

0 commit comments

Comments
 (0)