From 903eacb1ee5dc9536a7c590a9585d9ca404936ae Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 16 Feb 2023 10:13:42 -0500 Subject: [PATCH 1/6] Fix use of `:where(.btn)` when matching `!btn` (#10601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cleanup code This makes it more explicit that we’re parsing a string selector, modifying it, and turning it back into a string * Fix important modifier when :where is involved * Only parse selector list once when handling the important modifier * Fix import * Fix lint errors --- src/lib/generateRules.js | 30 ++++++++++++++-------- src/util/formatVariantSelector.js | 2 +- src/util/pluginUtils.js | 39 ++++++++-------------------- tests/important-modifier.test.js | 42 +++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index 0533a9e86c51..f7096e556602 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -3,10 +3,14 @@ import selectorParser from 'postcss-selector-parser' import parseObjectStyles from '../util/parseObjectStyles' import isPlainObject from '../util/isPlainObject' import prefixSelector from '../util/prefixSelector' -import { updateAllClasses, filterSelectorsForClass, getMatchingTypes } from '../util/pluginUtils' +import { updateAllClasses, getMatchingTypes } from '../util/pluginUtils' import log from '../util/log' import * as sharedState from './sharedState' -import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector' +import { + formatVariantSelector, + finalizeSelector, + eliminateIrrelevantSelectors, +} from '../util/formatVariantSelector' import { asClass } from '../util/nameClass' import { normalize } from '../util/dataTypes' import { isValidVariantFormatString, parseVariant } from './setupContextUtils' @@ -111,22 +115,28 @@ function applyImportant(matches, classCandidate) { if (matches.length === 0) { return matches } + let result = [] for (let [meta, rule] of matches) { let container = postcss.root({ nodes: [rule.clone()] }) + container.walkRules((r) => { - r.selector = updateAllClasses( - filterSelectorsForClass(r.selector, classCandidate), - (className) => { - if (className === classCandidate) { - return `!${className}` - } - return className - } + let ast = selectorParser().astSync(r.selector) + + // Remove extraneous selectors that do not include the base candidate + ast.each((sel) => eliminateIrrelevantSelectors(sel, classCandidate)) + + // Update all instances of the base candidate to include the important marker + updateAllClasses(ast, (className) => + className === classCandidate ? `!${className}` : className ) + + r.selector = ast.toString() + r.walkDecls((d) => (d.important = true)) }) + result.push([{ ...meta, important: true }, container.nodes[0]]) } diff --git a/src/util/formatVariantSelector.js b/src/util/formatVariantSelector.js index ee6350b8ef33..da5e2c78dfbc 100644 --- a/src/util/formatVariantSelector.js +++ b/src/util/formatVariantSelector.js @@ -120,7 +120,7 @@ function resortSelector(sel) { * @param {Selector} ast * @param {string} base */ -function eliminateIrrelevantSelectors(sel, base) { +export function eliminateIrrelevantSelectors(sel, base) { let hasClassesMatchingCandidate = false sel.walk((child) => { diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index 5c4821d3cbca..ecaaf0c90870 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -1,4 +1,3 @@ -import selectorParser from 'postcss-selector-parser' import escapeCommas from './escapeCommas' import { withAlphaValue } from './withAlphaVariable' import { @@ -21,37 +20,19 @@ import negateValue from './negateValue' import { backgroundSize } from './validateFormalSyntax' import { flagEnabled } from '../featureFlags.js' +/** + * @param {import('postcss-selector-parser').Container} selectors + * @param {(className: string) => string} updateClass + * @returns {string} + */ export function updateAllClasses(selectors, updateClass) { - let parser = selectorParser((selectors) => { - selectors.walkClasses((sel) => { - let updatedClass = updateClass(sel.value) - sel.value = updatedClass - if (sel.raws && sel.raws.value) { - sel.raws.value = escapeCommas(sel.raws.value) - } - }) - }) - - let result = parser.processSync(selectors) + selectors.walkClasses((sel) => { + sel.value = updateClass(sel.value) - return result -} - -export function filterSelectorsForClass(selectors, classCandidate) { - let parser = selectorParser((selectors) => { - selectors.each((sel) => { - const containsClass = sel.nodes.some( - (node) => node.type === 'class' && node.value === classCandidate - ) - if (!containsClass) { - sel.remove() - } - }) + if (sel.raws && sel.raws.value) { + sel.raws.value = escapeCommas(sel.raws.value) + } }) - - let result = parser.processSync(selectors) - - return result } function resolveArbitraryValue(modifier, validate) { diff --git a/tests/important-modifier.test.js b/tests/important-modifier.test.js index bde7eee2ea84..6d30fd0bceb3 100644 --- a/tests/important-modifier.test.js +++ b/tests/important-modifier.test.js @@ -108,4 +108,46 @@ crosscheck(() => { `) }) }) + + test('the important modifier works on utilities using :where()', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + plugins: [ + function ({ addComponents }) { + addComponents({ + ':where(.btn)': { + backgroundColor: '#00f', + }, + }) + }, + ], + } + + let input = css` + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + :where(.\!btn) { + background-color: #00f !important; + } + :where(.btn) { + background-color: #00f; + } + :where(.hover\:btn:hover) { + background-color: #00f; + } + :where(.hover\:focus\:disabled\:\!btn:disabled:focus:hover) { + background-color: #00f !important; + } + `) + }) + }) }) From 742c699307ff75cbf53316c5e17b0e5c4e489746 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 16 Feb 2023 10:16:36 -0500 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24ac901cef2c..11618340d319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Fix use of `:where(.btn)` when matching `!btn` ([#10601](https://github.com/tailwindlabs/tailwindcss/pull/10601)) ## [3.2.6] - 2023-02-08 From 04c5cacf058fbfdb91f5df0034bb17388848f242 Mon Sep 17 00:00:00 2001 From: Jonathan Reinink Date: Thu, 16 Feb 2023 13:46:43 -0500 Subject: [PATCH 3/6] Revert including `outline-color` in `transition` and `transition-colors` by default (#10604) * Remove `outline-color` from default color properties to transition (Reverts #10385) * Update changelog --- CHANGELOG.md | 1 + stubs/defaultConfig.stub.js | 4 ++-- tests/basic-usage.oxide.test.css | 4 ++-- tests/basic-usage.test.css | 4 ++-- tests/kitchen-sink.test.js | 15 ++++++--------- tests/raw-content.oxide.test.css | 4 ++-- tests/raw-content.test.css | 4 ++-- 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11618340d319..5dd5318fcc0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix use of `:where(.btn)` when matching `!btn` ([#10601](https://github.com/tailwindlabs/tailwindcss/pull/10601)) +- Revert including `outline-color` in `transition` and `transition-colors` by default ([#10604](https://github.com/tailwindlabs/tailwindcss/pull/10604)) ## [3.2.6] - 2023-02-08 diff --git a/stubs/defaultConfig.stub.js b/stubs/defaultConfig.stub.js index 21a82cd4869b..d084d9e1f061 100644 --- a/stubs/defaultConfig.stub.js +++ b/stubs/defaultConfig.stub.js @@ -877,8 +877,8 @@ module.exports = { none: 'none', all: 'all', DEFAULT: - 'color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter', - colors: 'color, background-color, border-color, outline-color, text-decoration-color, fill, stroke', + 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter', + colors: 'color, background-color, border-color, text-decoration-color, fill, stroke', opacity: 'opacity', shadow: 'box-shadow', transform: 'transform', diff --git a/tests/basic-usage.oxide.test.css b/tests/basic-usage.oxide.test.css index 9484dfdd698b..cdb842806bf0 100644 --- a/tests/basic-usage.oxide.test.css +++ b/tests/basic-usage.oxide.test.css @@ -1014,8 +1014,8 @@ backdrop-filter: none; } .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } diff --git a/tests/basic-usage.test.css b/tests/basic-usage.test.css index 9484dfdd698b..cdb842806bf0 100644 --- a/tests/basic-usage.test.css +++ b/tests/basic-usage.test.css @@ -1014,8 +1014,8 @@ backdrop-filter: none; } .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } diff --git a/tests/kitchen-sink.test.js b/tests/kitchen-sink.test.js index fbe4fc9dfda9..0cda8471de0a 100644 --- a/tests/kitchen-sink.test.js +++ b/tests/kitchen-sink.test.js @@ -622,9 +622,8 @@ crosscheck(() => { } @media (prefers-reduced-motion: no-preference) { .motion-safe\:transition { - transition-property: color, background-color, border-color, outline-color, - text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, - backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, + stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @@ -634,9 +633,8 @@ crosscheck(() => { } @media (prefers-reduced-motion: reduce) { .motion-reduce\:transition { - transition-property: color, background-color, border-color, outline-color, - text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, - backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, + stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @@ -683,9 +681,8 @@ crosscheck(() => { } @media (prefers-reduced-motion: no-preference) { .md\:motion-safe\:hover\:transition:hover { - transition-property: color, background-color, border-color, outline-color, - text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, - backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, + fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } diff --git a/tests/raw-content.oxide.test.css b/tests/raw-content.oxide.test.css index 402f90a345a4..b7cc098c84bb 100644 --- a/tests/raw-content.oxide.test.css +++ b/tests/raw-content.oxide.test.css @@ -755,8 +755,8 @@ backdrop-filter: none; } .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } diff --git a/tests/raw-content.test.css b/tests/raw-content.test.css index 402f90a345a4..b7cc098c84bb 100644 --- a/tests/raw-content.test.css +++ b/tests/raw-content.test.css @@ -755,8 +755,8 @@ backdrop-filter: none; } .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } From ca02a7aab77eb531673256ae6442a9159fd65341 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:56:47 -0500 Subject: [PATCH 4/6] Add v3.2.7 to changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dd5318fcc0f..4d7497ca17de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Nothing yet! + +## [3.2.7] - 2023-02-16 + ### Fixed - Fix use of `:where(.btn)` when matching `!btn` ([#10601](https://github.com/tailwindlabs/tailwindcss/pull/10601)) @@ -2172,7 +2176,8 @@ No release notes - Everything! -[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.6...HEAD +[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.7...HEAD +[3.2.7]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.6...v3.2.7 [3.2.6]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.5...v3.2.6 [3.2.5]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.4...v3.2.5 [3.2.4]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.3...v3.2.4 From a9917a69b9df53ac6dc01adc4358c061ef3c5ae8 Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:57:05 -0500 Subject: [PATCH 5/6] 3.2.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9706c573602..d421b333d162 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tailwindcss", - "version": "3.2.6", + "version": "3.2.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tailwindcss", - "version": "3.2.6", + "version": "3.2.7", "license": "MIT", "workspaces": [ "integrations/*", diff --git a/package.json b/package.json index 23702274d009..f09ae74640f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tailwindcss", - "version": "3.2.6", + "version": "3.2.7", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "main": "lib/index.js", From 2b255a36cfcd5281bfddcaf7cc6bac66f22695bb Mon Sep 17 00:00:00 2001 From: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Date: Thu, 16 Feb 2023 14:07:22 -0500 Subject: [PATCH 6/6] Update version in package.stable.json --- package.stable.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.stable.json b/package.stable.json index 1ff633751777..3c2efc25fd19 100644 --- a/package.stable.json +++ b/package.stable.json @@ -1,6 +1,6 @@ { "name": "tailwindcss", - "version": "3.2.6", + "version": "3.2.7", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "main": "lib/index.js",