From 128030fcfa1b784e30474f18f88efaa41450fb51 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Fri, 20 May 2022 11:20:20 -0400 Subject: [PATCH] Only apply hover styles when supported (future) (#8394) * Only apply hover styles when supported (future) Co-Authored-By: Andrew Brown * update changelog Co-authored-by: Andrew Brown Co-authored-by: Robin Malfait --- CHANGELOG.md | 1 + src/corePlugins.js | 32 +++++++++++++++++++------------- src/featureFlags.js | 2 +- tests/variants.test.js | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d40c96cf3eb..ca3c9d8cc011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Try using local `postcss` installation first in the CLI ([#8270](https://github.com/tailwindlabs/tailwindcss/pull/8270)) +- Only apply hover styles when supported (future) ([#8394](https://github.com/tailwindlabs/tailwindcss/pull/8394)) ### Added diff --git a/src/corePlugins.js b/src/corePlugins.js index e76fc03610e9..58a528a16c4f 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -14,6 +14,7 @@ import { version as tailwindVersion } from '../package.json' import log from './util/log' import { normalizeScreens } from './util/normalizeScreens' import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadowValue' +import { flagEnabled } from './featureFlags' export let variantPlugins = { pseudoElementVariants: ({ addVariant }) => { @@ -60,14 +61,14 @@ export let variantPlugins = { }) }, - pseudoClassVariants: ({ addVariant }) => { + pseudoClassVariants: ({ addVariant, config }) => { let pseudoVariants = [ // Positional - ['first', ':first-child'], - ['last', ':last-child'], - ['only', ':only-child'], - ['odd', ':nth-child(odd)'], - ['even', ':nth-child(even)'], + ['first', '&:first-child'], + ['last', '&:last-child'], + ['only', '&:only-child'], + ['odd', '&:nth-child(odd)'], + ['even', '&:nth-child(even)'], 'first-of-type', 'last-of-type', 'only-of-type', @@ -92,11 +93,11 @@ export let variantPlugins = { } }) - return ':visited' + return '&:visited' }, ], 'target', - ['open', '[open]'], + ['open', '&[open]'], // Forms 'default', @@ -116,19 +117,24 @@ export let variantPlugins = { // Interactive 'focus-within', - 'hover', + [ + 'hover', + !flagEnabled(config(), 'hoverOnlyWhenSupported') + ? '&:hover' + : '@media (hover: hover) and (pointer: fine) { &:hover }', + ], 'focus', 'focus-visible', 'active', 'enabled', 'disabled', - ].map((variant) => (Array.isArray(variant) ? variant : [variant, `:${variant}`])) + ].map((variant) => (Array.isArray(variant) ? variant : [variant, `&:${variant}`])) for (let [variantName, state] of pseudoVariants) { addVariant(variantName, (ctx) => { let result = typeof state === 'function' ? state(ctx) : state - return `&${result}` + return result }) } @@ -136,7 +142,7 @@ export let variantPlugins = { addVariant(`group-${variantName}`, (ctx) => { let result = typeof state === 'function' ? state(ctx) : state - return `:merge(.group)${result} &` + return result.replace(/&(\S+)/, ':merge(.group)$1 &') }) } @@ -144,7 +150,7 @@ export let variantPlugins = { addVariant(`peer-${variantName}`, (ctx) => { let result = typeof state === 'function' ? state(ctx) : state - return `:merge(.peer)${result} ~ &` + return result.replace(/&(\S+)/, ':merge(.peer)$1 ~ &') }) } }, diff --git a/src/featureFlags.js b/src/featureFlags.js index 45c4a9f51879..c34788ea1c2b 100644 --- a/src/featureFlags.js +++ b/src/featureFlags.js @@ -6,7 +6,7 @@ let defaults = { } let featureFlags = { - future: [], + future: ['hoverOnlyWhenSupported'], experimental: ['optimizeUniversalDefaults'], } diff --git a/tests/variants.test.js b/tests/variants.test.js index c3031fd11d67..9959073f833d 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -769,3 +769,39 @@ it('variants only picks the used selectors in a group (apply)', () => { `) }) }) + +test('hoverOnlyWhenSupported adds hover and pointer media features by default', () => { + let config = { + future: { + hoverOnlyWhenSupported: true, + }, + content: [ + { raw: html`
` }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + + @media (hover: hover) and (pointer: fine) { + .hover\:underline:hover { + text-decoration-line: underline; + } + .group:hover .group-hover\:underline { + text-decoration-line: underline; + } + .peer:hover ~ .peer-hover\:underline { + text-decoration-line: underline; + } + } + `) + }) +})