From 7677c593ec9a614e3ce311c184293ee8bba799b1 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 4 Oct 2022 17:55:10 +0200 Subject: [PATCH] Implement the `supports` variant (#9453) * implement a `supports` variant * update changelog * use `--tw` instead of `--tw-empty` --- CHANGELOG.md | 1 + src/corePlugins.js | 32 +++++++++++ src/lib/setupContextUtils.js | 1 + tests/arbitrary-variants.test.js | 92 ++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32dc8876f5f7..75ce2d2033bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `@config` support ([#9405](https://github.com/tailwindlabs/tailwindcss/pull/9405)) - Add `fill-none` and `stroke-none` utilities by default ([#9403](https://github.com/tailwindlabs/tailwindcss/pull/9403)) - Support `sort` function in `matchVariant` ([#9423](https://github.com/tailwindlabs/tailwindcss/pull/9423)) +- Implement the `supports` variant ([#9453](https://github.com/tailwindlabs/tailwindcss/pull/9453)) ### Fixed diff --git a/src/corePlugins.js b/src/corePlugins.js index 5cf27287128e..352737c703d8 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -16,6 +16,7 @@ import { normalizeScreens } from './util/normalizeScreens' import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadowValue' import { removeAlphaVariables } from './util/removeAlphaVariables' import { flagEnabled } from './featureFlags' +import { normalize } from './util/dataTypes' export let variantPlugins = { pseudoElementVariants: ({ addVariant }) => { @@ -215,6 +216,37 @@ export let variantPlugins = { } }, + supportsVariants: ({ matchVariant, theme, config }) => { + if (!flagEnabled(config(), 'matchVariant')) return + + matchVariant( + 'supports', + ({ value = '' }) => { + let check = normalize(value) + let isRaw = /^\w*\s*\(/.test(check) + + // Chrome has a bug where `(condtion1)or(condition2)` is not valid + // But `(condition1) or (condition2)` is supported. + check = isRaw ? check.replace(/\b(and|or|not)\b/g, ' $1 ') : check + + if (isRaw) { + return `@supports ${check}` + } + + if (!check.includes(':')) { + check = `${check}: var(--tw)` + } + + if (!(check.startsWith('(') && check.endsWith(')'))) { + check = `(${check})` + } + + return `@supports ${check} ` + }, + { values: theme('supports') ?? {} } + ) + }, + orientationVariants: ({ addVariant }) => { addVariant('portrait', '@media (orientation: portrait)') addVariant('landscape', '@media (orientation: landscape)') diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 5a9e8a247e6e..874b7f7058cb 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -655,6 +655,7 @@ function resolvePlugins(context, root) { variantPlugins['pseudoClassVariants'], ] let afterVariants = [ + variantPlugins['supportsVariants'], variantPlugins['directionVariants'], variantPlugins['reducedMotionVariants'], variantPlugins['prefersContrastVariants'], diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index c17016a5bccb..a947dbe435c4 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -615,3 +615,95 @@ test('classes in the same arbitrary variant should not be prefixed', () => { `) }) }) + +it('should support supports', () => { + let config = { + experimental: { matchVariant: true }, + theme: { + supports: { + grid: 'display: grid', + }, + }, + content: [ + { + raw: html` +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @supports (display: grid) { + .supports-grid\:underline { + text-decoration-line: underline; + } + } + + @supports (display: grid) { + .supports-\[display\:grid\]\:grid { + display: grid; + } + } + + @supports (transform-origin: 5% 5%) { + .supports-\[transform-origin\:5\%_5\%\]\:underline { + text-decoration-line: underline; + } + } + + @supports selector(A > B) { + .supports-\[selector\(A\>B\)\]\:underline { + text-decoration-line: underline; + } + } + + @supports not (foo: bar) { + .supports-\[not\(foo\:bar\)\]\:underline { + text-decoration-line: underline; + } + } + + @supports (foo: bar) or (bar: baz) { + .supports-\[\(foo\:bar\)or\(bar\:baz\)\]\:underline { + text-decoration-line: underline; + } + } + + @supports (foo: bar) and (bar: baz) { + .supports-\[\(foo\:bar\)and\(bar\:baz\)\]\:underline { + text-decoration-line: underline; + } + } + + @supports (container-type: var(--tw)) { + .supports-\[container-type\]\:underline { + text-decoration-line: underline; + } + } + `) + }) +})