diff --git a/CHANGELOG.md b/CHANGELOG.md index 107d0433c8b8..7f2b779da8cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New `place-items`, `place-content`, `place-self`, `justify-items`, and `justify-self` utilities ([#2306](https://github.com/tailwindlabs/tailwindcss/pull/2306)) - Support configuring variants as functions ([#2309](https://github.com/tailwindlabs/tailwindcss/pull/2309)) +### Changed + +- CSS within `@layer` at-rules are now grouped with the corresponding `@tailwind` at-rule ([#2312](https://github.com/tailwindlabs/tailwindcss/pull/2312)) + ### Deprecated - `conservative` purge mode, deprecated in favor of `layers` diff --git a/__tests__/layerAtRule.test.js b/__tests__/layerAtRule.test.js new file mode 100644 index 000000000000..1653bf768dfc --- /dev/null +++ b/__tests__/layerAtRule.test.js @@ -0,0 +1,93 @@ +import postcss from 'postcss' +import tailwind from '../src/index' + +function run(input, config = {}) { + return postcss([tailwind({ corePlugins: [], ...config })]).process(input, { from: undefined }) +} + +test('layers are grouped and inserted at the matching @tailwind rule', () => { + const input = ` + @layer vanilla { + strong { font-weight: medium } + } + + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer components { + .btn { background: blue } + } + + @layer utilities { + .align-banana { text-align: banana } + } + + @layer base { + h1 { font-weight: bold } + } + + @layer components { + .card { border-radius: 12px } + } + + @layer base { + p { font-weight: normal } + } + + @layer utilities { + .align-sandwich { text-align: sandwich } + } + + @layer chocolate { + a { text-decoration: underline } + } + ` + + const expected = ` + @layer vanilla { + strong { font-weight: medium } + } + + body { margin: 0 } + h1 { font-weight: bold } + p { font-weight: normal } + + .input { background: white } + .btn { background: blue } + .card { border-radius: 12px } + + .float-squirrel { float: squirrel } + .align-banana { text-align: banana } + .align-sandwich { text-align: sandwich } + + @layer chocolate { + a { text-decoration: underline } + } + ` + + expect.assertions(2) + + return run(input, { + plugins: [ + function({ addBase, addComponents, addUtilities }) { + addBase({ + body: { + margin: 0, + }, + }) + + addComponents({ + '.input': { background: 'white' }, + }) + + addUtilities({ + '.float-squirrel': { float: 'squirrel' }, + }) + }, + ], + }).then(result => { + expect(result.css).toMatchCss(expected) + expect(result.warnings().length).toBe(0) + }) +}) diff --git a/src/lib/convertLayerAtRulesToControlComments.js b/src/lib/convertLayerAtRulesToControlComments.js index d2bcf0a7e5c2..e7ecc0ef9124 100644 --- a/src/lib/convertLayerAtRulesToControlComments.js +++ b/src/lib/convertLayerAtRulesToControlComments.js @@ -4,6 +4,11 @@ export default function convertLayerAtRulesToControlComments() { return function(css) { css.walkAtRules('layer', atRule => { const layer = atRule.params + + if (!['base', 'components', 'utilities'].includes(layer)) { + return + } + atRule.before(postcss.comment({ text: `tailwind start ${layer}` })) atRule.before(atRule.nodes) atRule.before(postcss.comment({ text: `tailwind end ${layer}` })) diff --git a/src/lib/substituteTailwindAtRules.js b/src/lib/substituteTailwindAtRules.js index ef9d36bf6b4a..759b81001629 100644 --- a/src/lib/substituteTailwindAtRules.js +++ b/src/lib/substituteTailwindAtRules.js @@ -41,6 +41,19 @@ export default function( }) let includesScreensExplicitly = false + const layers = { + base: [], + components: [], + utilities: [], + } + + css.walkAtRules('layer', atRule => { + if (!['base', 'components', 'utilities'].includes(atRule.params)) { + return + } + + layers[atRule.params].push(atRule) + }) css.walkAtRules('tailwind', atRule => { if (atRule.params === 'preflight') { @@ -49,14 +62,17 @@ export default function( } if (atRule.params === 'base') { + atRule.after(layers.base) atRule.after(updateSource(pluginBase, atRule.source)) } if (atRule.params === 'components') { + atRule.after(layers.components) atRule.after(updateSource(pluginComponents, atRule.source)) } if (atRule.params === 'utilities') { + atRule.after(layers.utilities) atRule.after(updateSource(pluginUtilities, atRule.source)) }