diff --git a/src/lib/expandTailwindAtRules.js b/src/lib/expandTailwindAtRules.js index b24fe527b10c..07632bd65985 100644 --- a/src/lib/expandTailwindAtRules.js +++ b/src/lib/expandTailwindAtRules.js @@ -205,9 +205,6 @@ export default function expandTailwindAtRules(context) { if (layerNodes.base) { layerNodes.base.before(cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source)) - } - - if (layerNodes.base) { layerNodes.base.remove() } @@ -221,16 +218,34 @@ export default function expandTailwindAtRules(context) { layerNodes.utilities.remove() } + // We do post-filtering to not alter the emitted order of the variants + const variantNodes = Array.from(screenNodes).filter((node) => { + const parentLayer = node.raws.tailwind?.parentLayer + + if (parentLayer === 'components') { + return !!layerNodes.components + } + + if (parentLayer === 'utilities') { + return !!layerNodes.utilities + } + + return true + }) + if (layerNodes.variants) { - layerNodes.variants.before(cloneNodes([...screenNodes], layerNodes.variants.source)) + layerNodes.variants.before(cloneNodes(variantNodes, layerNodes.variants.source)) layerNodes.variants.remove() - } else { - root.append(cloneNodes([...screenNodes], root.source)) + } else if (variantNodes.length > 0) { + root.append(cloneNodes(variantNodes, root.source)) } // If we've got a utility layer and no utilities are generated there's likely something wrong - // TODO: Detect utility variants only - if (layerNodes.utilities && utilityNodes.size === 0 && screenNodes.size === 0) { + const hasUtilityVariants = variantNodes.some( + (node) => node.raws.tailwind?.parentLayer === 'utilities' + ) + + if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) { log.warn('content-problems', [ 'No utilities were generated there is likely a problem with the `content` key in the tailwind config. For more information see the documentation: https://tailwindcss.com/docs/content-configuration', ]) diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index 150bf6851732..7eb80fdc671d 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -216,6 +216,12 @@ function applyVariant(variant, matches, context) { }) } + // This tracks the originating layer for the variant + // For example: + // .sm:underline {} is a variant of something in the utilities layer + // .sm:container {} is a variant of the container component + clone.nodes[0].raws.tailwind = { parentLayer: meta.layer } + let withOffset = [ { ...meta, diff --git a/tests/variants.test.js b/tests/variants.test.js index 058dd5522b08..72defcfe0dcf 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -1,7 +1,7 @@ import fs from 'fs' import path from 'path' -import { run, css, html } from './util/run' +import { run, css, html, defaults } from './util/run' test('variants', () => { let config = { @@ -467,3 +467,75 @@ it('should be possible to use responsive modifiers that are defined with special `) }) }) + +it('including just the base layer should not produce variants', () => { + let config = { + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind base', config).then((result) => { + return expect(result.css).toMatchFormattedCss( + css` + ${defaults} + ` + ) + }) +}) + +it('variants for components should not be produced in a file without a components layer', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .sm\:underline { + text-decoration-line: underline; + } + } + `) + }) +}) + +it('variants for utilities should not be produced in a file without a utilities layer', () => { + let config = { + content: [{ raw: html`
` }], + } + + return run('@tailwind components', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @media (min-width: 640px) { + .sm\:container { + width: 100%; + } + @media (min-width: 640px) { + .sm\:container { + max-width: 640px; + } + } + @media (min-width: 768px) { + .sm\:container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .sm\:container { + max-width: 1024px; + } + } + @media (min-width: 1280px) { + .sm\:container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .sm\:container { + max-width: 1536px; + } + } + } + `) + }) +})