diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1528b096e6..56a904cec130 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 - Fixed use of `raw` content in the CLI ([#9773](https://github.com/tailwindlabs/tailwindcss/pull/9773)) - Pick up changes from files that are both context and content deps ([#9787](https://github.com/tailwindlabs/tailwindcss/pull/9787)) - Sort pseudo-elements ONLY after classes when using variants and `@apply` ([#9765](https://github.com/tailwindlabs/tailwindcss/pull/9765)) +- Support important utilities in the safelist (pattern must include a `!`) ([#9791](https://github.com/tailwindlabs/tailwindcss/pull/9791)) ## [3.2.2] - 2022-11-04 diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 0d092b3517e7..a5f8d3fdfb67 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -825,6 +825,7 @@ function registerPlugins(plugins, context) { if (checks.length > 0) { let patternMatchingCount = new Map() let prefixLength = context.tailwindConfig.prefix.length + let checkImportantUtils = checks.some((check) => check.pattern.source.includes('!')) for (let util of classList) { let utils = Array.isArray(util) @@ -861,6 +862,10 @@ function registerPlugins(plugins, context) { ] } + if (checkImportantUtils && options?.respectImportant) { + classes = [...classes, ...classes.map((cls) => '!' + cls)] + } + return classes })() : [util] diff --git a/tests/safelist.test.js b/tests/safelist.test.js index d71c8b28eaaa..430f10331cc9 100644 --- a/tests/safelist.test.js +++ b/tests/safelist.test.js @@ -195,6 +195,35 @@ it('should not safelist when an sparse/holey list is provided', () => { }) }) +it('should not safelist any invalid variants if provided', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^bg-(red)-(100|200)$/, + variants: ['foo', 'bar'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + it('should safelist negatives based on a pattern regex', () => { let config = { content: [{ raw: html`
` }], @@ -304,3 +333,202 @@ it('should safelist negatives based on a pattern regex', () => { `) }) }) + +it('should safelist pattern regex with !important selector', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [{ pattern: /^!grid-cols-(4|5|6)$/ }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .\!grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)) !important; + } + + .\!grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)) !important; + } + + .\!grid-cols-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)) !important; + } + + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist pattern regex with custom prefix along with !important selector', () => { + let config = { + prefix: 'tw-', + content: [{ raw: html`
` }], + safelist: [{ pattern: /^!tw-grid-cols-(4|5|6)$/ }], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .\!tw-grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)) !important; + } + + .\!tw-grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)) !important; + } + + .\!tw-grid-cols-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)) !important; + } + + .tw-uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist pattern regex having !important selector with variants', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^!bg-gray-(500|600|700|800)$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .\!bg-gray-500 { + --tw-bg-opacity: 1 !important; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important; + } + + .\!bg-gray-600 { + --tw-bg-opacity: 1 !important; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important; + } + + .\!bg-gray-700 { + --tw-bg-opacity: 1 !important; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; + } + + .\!bg-gray-800 { + --tw-bg-opacity: 1 !important; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important; + } + + .uppercase { + text-transform: uppercase; + } + + .hover\:\!bg-gray-500:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important; + } + + .hover\:\!bg-gray-600:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important; + } + + .hover\:\!bg-gray-700:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; + } + + .hover\:\!bg-gray-800:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important; + } + `) + }) +}) + +it('should safelist multiple patterns with !important selector', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: /^!text-gray-(700|800|900)$/, + variants: ['hover'], + }, + { + pattern: /^!bg-gray-(200|300|400)$/, + variants: ['hover'], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .\!bg-gray-200 { + --tw-bg-opacity: 1 !important; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important; + } + + .\!bg-gray-300 { + --tw-bg-opacity: 1 !important; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important; + } + + .\!bg-gray-400 { + --tw-bg-opacity: 1 !important; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important; + } + + .uppercase { + text-transform: uppercase; + } + + .\!text-gray-700 { + --tw-text-opacity: 1 !important; + color: rgb(55 65 81 / var(--tw-text-opacity)) !important; + } + + .\!text-gray-800 { + --tw-text-opacity: 1 !important; + color: rgb(31 41 55 / var(--tw-text-opacity)) !important; + } + + .\!text-gray-900 { + --tw-text-opacity: 1 !important; + color: rgb(17 24 39 / var(--tw-text-opacity)) !important; + } + + .hover\:\!bg-gray-200:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important; + } + + .hover\:\!bg-gray-300:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important; + } + + .hover\:\!bg-gray-400:hover { + --tw-bg-opacity: 1 !important; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important; + } + + .hover\:\!text-gray-700:hover { + --tw-text-opacity: 1 !important; + color: rgb(55 65 81 / var(--tw-text-opacity)) !important; + } + + .hover\:\!text-gray-800:hover { + --tw-text-opacity: 1 !important; + color: rgb(31 41 55 / var(--tw-text-opacity)) !important; + } + + .hover\:\!text-gray-900:hover { + --tw-text-opacity: 1 !important; + color: rgb(17 24 39 / var(--tw-text-opacity)) !important; + } + `) + }) +})