diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d6d51f66a6..e13ddabb96bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure complex variants with multiple classes work ([#6311](https://github.com/tailwindlabs/tailwindcss/pull/6311)) - Re-add `default` interop to public available functions ([#6348](https://github.com/tailwindlabs/tailwindcss/pull/6348)) - Detect circular dependencies when using `@apply` ([#6365](https://github.com/tailwindlabs/tailwindcss/pull/6365)) +- Fix defaults optimization when vendor prefixes are involved ([#6369](https://github.com/tailwindlabs/tailwindcss/pull/6369)) ## [3.0.0] - 2021-12-09 diff --git a/src/lib/resolveDefaultsAtRules.js b/src/lib/resolveDefaultsAtRules.js index 59b769264679..a02df8e907f3 100644 --- a/src/lib/resolveDefaultsAtRules.js +++ b/src/lib/resolveDefaultsAtRules.js @@ -71,6 +71,8 @@ function extractElementSelector(selector) { export default function resolveDefaultsAtRules({ tailwindConfig }) { return (root) => { let variableNodeMap = new Map() + + /** @type {Set} */ let universals = new Set() root.walkAtRules('defaults', (rule) => { @@ -90,31 +92,50 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) { }) for (let universal of universals) { - let selectors = new Set() + /** @type {Map>} */ + let selectorGroups = new Map() let rules = variableNodeMap.get(universal.params) ?? [] for (let rule of rules) { for (let selector of extractElementSelector(rule.selector)) { + // If selector contains a vendor prefix after a pseudo element or class, + // we consider them separately because merging the declarations into + // a single rule will cause browsers that do not understand the + // vendor prefix to throw out the whole rule + let selectorGroupName = + selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__' + + let selectors = selectorGroups.get(selectorGroupName) ?? new Set() + selectorGroups.set(selectorGroupName, selectors) + selectors.add(selector) } } - if (selectors.size === 0) { + if (selectorGroups.size === 0) { universal.remove() continue } - let universalRule = postcss.rule() - if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) { - universalRule.selectors = [...selectors] + for (let [, selectors] of selectorGroups) { + let universalRule = postcss.rule() + + universalRule.selectors = [...selectors] + + universalRule.append(universal.nodes.map((node) => node.clone())) + universal.before(universalRule) + } } else { + let universalRule = postcss.rule() + universalRule.selectors = ['*', '::before', '::after'] + + universalRule.append(universal.nodes) + universal.before(universalRule) } - universalRule.append(universal.nodes) - universal.before(universalRule) universal.remove() } } diff --git a/tests/apply.test.js b/tests/apply.test.js index d8e75138f9ba..12e389937cef 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -577,3 +577,42 @@ it('should throw when trying to apply an indirect circular dependency with a mod expect(err.reason).toBe('Circular dependency detected when using: `@apply a`') }) }) + +it('rules with vendor prefixes are still separate when optimizing defaults rules', () => { + let config = { + experimental: { optimizeUniversalDefaults: true }, + content: [{ raw: html`
` }], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer components { + input[type='range']::-moz-range-thumb { + @apply border; + } + } + ` + + return run(input, config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + [type='range']::-moz-range-thumb { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); + } + .border { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); + } + input[type='range']::-moz-range-thumb { + border-width: 1px; + } + .border { + border-width: 1px; + } + `) + }) +})