diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1a71587..3793da744 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix perf regression when checking for changed content ([#10234](https://github.com/tailwindlabs/tailwindcss/pull/10234)) - Fix missing `blocklist` member in the `Config` type ([#10239](https://github.com/tailwindlabs/tailwindcss/pull/10239)) - Escape group names in selectors ([#10276](https://github.com/tailwindlabs/tailwindcss/pull/10276)) +- Consider earlier variants before sorting functions ([#10288](https://github.com/tailwindlabs/tailwindcss/pull/10288)) ### Changed diff --git a/src/lib/offsets.js b/src/lib/offsets.js index 70afdb619..4e6d0ee9f 100644 --- a/src/lib/offsets.js +++ b/src/lib/offsets.js @@ -13,6 +13,7 @@ import { remapBitfield } from './remap-bitfield.js' * @property {function | undefined} sort The sort function * @property {string|null} value The value we want to compare * @property {string|null} modifier The modifier that was used (if any) + * @property {bigint} variant The variant bitmask */ /** @@ -127,6 +128,8 @@ export class Offsets { * @returns {RuleOffset} */ applyVariantOffset(rule, variant, options) { + options.variant = variant.variants + return { ...rule, layer: 'variants', @@ -211,6 +214,19 @@ export class Offsets { for (let bOptions of b.options) { if (aOptions.id !== bOptions.id) continue if (!aOptions.sort || !bOptions.sort) continue + + let maxFnVariant = max([aOptions.variant, bOptions.variant]) ?? 0n + + // Create a mask of 0s from bits 1..N where N represents the mask of the Nth bit + let mask = ~(maxFnVariant | (maxFnVariant - 1n)) + let aVariantsAfterFn = a.variants & mask + let bVariantsAfterFn = b.variants & mask + + // If the variants the same, we _can_ sort them + if (aVariantsAfterFn !== bVariantsAfterFn) { + continue + } + let result = aOptions.sort( { value: aOptions.value, diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index 5ef6520f7..52edef562 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -1158,3 +1158,201 @@ it('Invalid arbitrary variants selectors should produce nothing instead of faili expect(result.css).toMatchFormattedCss(css``) }) }) + +it('should output responsive variants + stacked variants in the right order', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @media (min-width: 1280px) { + .xl\:p-1 { + padding: 0.25rem; + } + } + .\[\&_ul\]\:flex ul { + display: flex; + } + .\[\&_ul\]\:flex-col ul { + flex-direction: column; + } + @media (min-width: 768px) { + .md\:\[\&_ul\]\:flex-row ul { + flex-direction: row; + } + } + `) + }) +}) + +it('should sort multiple variant fns with normal variants between them', () => { + /** @type {string[]} */ + let lines = [] + + for (let a of [1, 2]) { + for (let b of [2, 1]) { + for (let c of [1, 2]) { + for (let d of [2, 1]) { + for (let e of [1, 2]) { + lines.push(`
`) + } + } + } + } + } + + // Fisher-Yates shuffle + for (let i = lines.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * i) + ;[lines[i], lines[j]] = [lines[j], lines[i]] + } + + let config = { + content: [ + { + raw: lines.join('\n'), + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addVariant, matchVariant }) { + addVariant('foo1', '&[data-foo=1]') + addVariant('foo2', '&[data-foo=2]') + + matchVariant('bar', (value) => `&[data-bar=${value}]`, { + sort: (a, b) => b.value - a.value, + }) + + addVariant('baz1', '&[data-baz=1]') + addVariant('baz2', '&[data-baz=2]') + + matchVariant('qux', (value) => `&[data-qux=${value}]`, { + sort: (a, b) => b.value - a.value, + }) + + addVariant('fred1', '&[data-fred=1]') + addVariant('fred2', '&[data-fred=2]') + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] { + padding: 0.25rem; + } + `) + }) +})