diff --git a/packages/tailwindcss-react-aria-components/fixtures/variants.html b/packages/tailwindcss-react-aria-components/fixtures/variants.html index e66a577aa7d..3be3ce5bdfb 100644 --- a/packages/tailwindcss-react-aria-components/fixtures/variants.html +++ b/packages/tailwindcss-react-aria-components/fixtures/variants.html @@ -1,3 +1,3 @@
+ className="hover:bg-red focus:bg-red focus-visible:bg-red focus-within:bg-red pressed:bg-red active:bg-red disabled:bg-red pending:bg-red drop-target:bg-red dragging:bg-red empty:bg-red allows-dragging:bg-red allows-removing:bg-red allows-sorting:bg-red has-submenu:bg-red has-child-items:bg-red placeholder-shown:bg-red selected:bg-red indeterminate:bg-red read-only:bg-red required:bg-red entering:bg-red exiting:bg-red open:bg-red expanded:bg-red unavailable:bg-red outside-month:bg-red outside-visible-range:bg-red selection-start:bg-red selection-end:bg-red current:bg-red invalid:bg-red resizing:bg-red placement-left:bg-red placement-right:bg-red placement-top:bg-red placement-bottom:bg-red type-literal:bg-red type-year:bg-red type-month:bg-red type-day:bg-red layout-grid:bg-red layout-stack:bg-red orientation-horizontal:bg-red orientation-vertical:bg-red selection-single:bg-red selection-multiple:bg-red resizable-right:bg-red resizable-left:bg-red resizable-both:bg-red sort-ascending:bg-red sort-descending:bg-red group-pressed:bg-red peer-pressed:bg-red group-hover:bg-red group/custom-name group-hover/custom-name:bg-red peer-pressed/custom-name:bg-red not-disabled:bg-red not-focus:bg-red not-invalid:bg-red not-required:bg-red not-placeholder-shown:bg-red not-empty:bg-red not-focus-visible:bg-red not-focus-within:bg-red not-open:bg-red not-expanded:bg-red has-disabled:bg-red has-focus:bg-red has-invalid:bg-red not-hover:bg-red group-not-disabled:bg-red group-not-invalid:bg-red group-not-pressed:bg-red peer-not-disabled:bg-red has-not-disabled:bg-red not-group-hover:bg-red peer-hover:bg-red in-hover:bg-red in-disabled:bg-red"> diff --git a/packages/tailwindcss-react-aria-components/src/index.js b/packages/tailwindcss-react-aria-components/src/index.js index 9c948a49d33..25b9b1ef2a8 100644 --- a/packages/tailwindcss-react-aria-components/src/index.js +++ b/packages/tailwindcss-react-aria-components/src/index.js @@ -94,40 +94,32 @@ let getSelector = (prefix, attributeName, attributeValue) => { : `[data-${attributeName}]`; let nativeSelector = nativeVariantSelectors.get(attributeName); if (prefix === '' && nativeSelector) { - let wrappedNativeSelector = `&:where(:not([data-rac]))${nativeSelector}`; - let nativeSelectorGenerator = wrappedNativeSelector; + // Collapse the RAC and native branches into a single `:is()` so `not-*` + // works — Tailwind's `not` walker bails on a dual-selector (array) shape. + // `:where()` keeps specificity at (0,1,0). + let unifiedSelector = `&:is(:where([data-rac])${baseSelector}, :where(:not([data-rac]))${nativeSelector})`; + // Hover needs `@media (hover: hover)` to avoid sticky styles on touch. + // Emitted as CSS-in-JS (not `@media ... { & }`) so Tailwind reports + // compounds as `StyleRules | AtRules`, keeping `group-hover:`/`peer-hover:` + // composable while still letting `not-hover:` invert cleanly. if (nativeSelector === ':hover') { - nativeSelectorGenerator = wrap => `@media (hover: hover) { ${wrap(wrappedNativeSelector)} }`; + return { + '@media (hover: hover)': { + [unifiedSelector]: '@slot' + } + }; } - return [`&:where([data-rac])${baseSelector}`, nativeSelectorGenerator]; + return unifiedSelector; } else if (prefix === '' && nativeMergeSelectors.has(attributeName)) { - return [`&${baseSelector}`, `&${nativeMergeSelectors.get(attributeName)}`]; + // Same `:is()` collapse for merge selectors (e.g. placeholder). + return `&:is(${baseSelector}, ${nativeMergeSelectors.get(attributeName)})`; } else { return `&${baseSelector}`; } }; -let mapSelector = (selector, fn) => { - if (Array.isArray(selector)) { - return selector.map(fn); - } else { - return fn(selector); - } -}; - -let wrapSelector = (selector, wrap) => { - if (typeof selector === 'function') { - return selector(wrap); - } else { - return wrap(selector); - } -}; - let addVariants = (variantName, selectors, addVariant) => { - addVariant( - variantName, - mapSelector(selectors, selector => wrapSelector(selector, s => s)) - ); + addVariant(variantName, selectors); }; module.exports = plugin.withOptions(options => ({addVariant}) => { diff --git a/packages/tailwindcss-react-aria-components/test/__snapshots__/index.test.js.snap b/packages/tailwindcss-react-aria-components/test/__snapshots__/index.test.js.snap index 46da6dd0a41..fbab4cbb959 100644 --- a/packages/tailwindcss-react-aria-components/test/__snapshots__/index.test.js.snap +++ b/packages/tailwindcss-react-aria-components/test/__snapshots__/index.test.js.snap @@ -2,22 +2,97 @@ exports[`variants 1`] = ` "/*! tailwindcss v4.0.17 | MIT License | https://tailwindcss.com */ -.group-hover\\:bg-red { - &:is(:where(.group):where([data-rac])[data-hovered] *) { +.not-group-hover\\:bg-red { + &:not(*:is(:where(.group):is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover) *)) { + background-color: var(--color-red); + } + @media not (hover: hover) { background-color: var(--color-red); } +} +.not-open\\:bg-red { + &:not(*:is(:where([data-rac])[data-open], :where(:not([data-rac]))[open])) { + background-color: var(--color-red); + } +} +.not-placeholder-shown\\:bg-red { + &:not(*:is([data-placeholder], :placeholder-shown)) { + background-color: var(--color-red); + } +} +.not-required\\:bg-red { + &:not(*:is(:where([data-rac])[data-required], :where(:not([data-rac])):required)) { + background-color: var(--color-red); + } +} +.not-invalid\\:bg-red { + &:not(*:is(:where([data-rac])[data-invalid], :where(:not([data-rac])):invalid)) { + background-color: var(--color-red); + } +} +.not-empty\\:bg-red { + &:not(*:is(:where([data-rac])[data-empty], :where(:not([data-rac])):empty)) { + background-color: var(--color-red); + } +} +.not-focus-within\\:bg-red { + &:not(*:is(:where([data-rac])[data-focus-within], :where(:not([data-rac])):focus-within)) { + background-color: var(--color-red); + } +} +.not-hover\\:bg-red { + &:not(*:is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover)) { + background-color: var(--color-red); + } + @media not (hover: hover) { + background-color: var(--color-red); + } +} +.not-focus\\:bg-red { + &:not(*:is(:where([data-rac])[data-focused], :where(:not([data-rac])):focus)) { + background-color: var(--color-red); + } +} +.not-focus-visible\\:bg-red { + &:not(*:is(:where([data-rac])[data-focus-visible], :where(:not([data-rac])):focus-visible)) { + background-color: var(--color-red); + } +} +.not-disabled\\:bg-red { + &:not(*:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled)) { + background-color: var(--color-red); + } +} +.not-expanded\\:bg-red { + &:not(*:is(:where([data-rac])[data-expanded], :where(:not([data-rac]))[expanded])) { + background-color: var(--color-red); + } +} +.group-not-invalid\\:bg-red { + &:is(:where(.group):not(*:is(:where([data-rac])[data-invalid], :where(:not([data-rac])):invalid)) *) { + background-color: var(--color-red); + } +} +.group-not-disabled\\:bg-red { + &:is(:where(.group):not(*:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled)) *) { + background-color: var(--color-red); + } +} +.group-not-pressed\\:bg-red { + &:is(:where(.group):not(*[data-pressed]) *) { + background-color: var(--color-red); + } +} +.group-hover\\:bg-red { @media (hover: hover) { - &:is(:where(.group):where(:not([data-rac])):hover *) { + &:is(:where(.group):is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover) *) { background-color: var(--color-red); } } } .group-hover\\/custom-name\\:bg-red { - &:is(:where(.group\\/custom-name):where([data-rac])[data-hovered] *) { - background-color: var(--color-red); - } @media (hover: hover) { - &:is(:where(.group\\/custom-name):where(:not([data-rac])):hover *) { + &:is(:where(.group\\/custom-name):is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover) *) { background-color: var(--color-red); } } @@ -27,6 +102,18 @@ exports[`variants 1`] = ` background-color: var(--color-red); } } +.peer-not-disabled\\:bg-red { + &:is(:where(.peer):not(*:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled)) ~ *) { + background-color: var(--color-red); + } +} +.peer-hover\\:bg-red { + @media (hover: hover) { + &:is(:where(.peer):is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover) ~ *) { + background-color: var(--color-red); + } + } +} .peer-pressed\\:bg-red { &:is(:where(.peer)[data-pressed] ~ *) { background-color: var(--color-red); @@ -38,108 +125,101 @@ exports[`variants 1`] = ` } } .open\\:bg-red { - &:where([data-rac])[data-open] { - background-color: var(--color-red); - } - &:where(:not([data-rac]))[open] { + &:is(:where([data-rac])[data-open], :where(:not([data-rac]))[open]) { background-color: var(--color-red); } } .indeterminate\\:bg-red { - &:where([data-rac])[data-indeterminate] { - background-color: var(--color-red); - } - &:where(:not([data-rac])):indeterminate { + &:is(:where([data-rac])[data-indeterminate], :where(:not([data-rac])):indeterminate) { background-color: var(--color-red); } } .placeholder-shown\\:bg-red { - &[data-placeholder] { - background-color: var(--color-red); - } - &:placeholder-shown { + &:is([data-placeholder], :placeholder-shown) { background-color: var(--color-red); } } .required\\:bg-red { - &:where([data-rac])[data-required] { - background-color: var(--color-red); - } - &:where(:not([data-rac])):required { + &:is(:where([data-rac])[data-required], :where(:not([data-rac])):required) { background-color: var(--color-red); } } .invalid\\:bg-red { - &:where([data-rac])[data-invalid] { - background-color: var(--color-red); - } - &:where(:not([data-rac])):invalid { + &:is(:where([data-rac])[data-invalid], :where(:not([data-rac])):invalid) { background-color: var(--color-red); } } .read-only\\:bg-red { - &:where([data-rac])[data-readonly] { - background-color: var(--color-red); - } - &:where(:not([data-rac])):read-only { + &:is(:where([data-rac])[data-readonly], :where(:not([data-rac])):read-only) { background-color: var(--color-red); } } .empty\\:bg-red { - &:where([data-rac])[data-empty] { - background-color: var(--color-red); - } - &:where(:not([data-rac])):empty { + &:is(:where([data-rac])[data-empty], :where(:not([data-rac])):empty) { background-color: var(--color-red); } } .focus-within\\:bg-red { - &:where([data-rac])[data-focus-within] { - background-color: var(--color-red); - } - &:where(:not([data-rac])):focus-within { + &:is(:where([data-rac])[data-focus-within], :where(:not([data-rac])):focus-within) { background-color: var(--color-red); } } .hover\\:bg-red { - &:where([data-rac])[data-hovered] { - background-color: var(--color-red); - } @media (hover: hover) { - &:where(:not([data-rac])):hover { + &:is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover) { background-color: var(--color-red); } } } .focus\\:bg-red { - &:where([data-rac])[data-focused] { + &:is(:where([data-rac])[data-focused], :where(:not([data-rac])):focus) { background-color: var(--color-red); } - &:where(:not([data-rac])):focus { +} +.focus-visible\\:bg-red { + &:is(:where([data-rac])[data-focus-visible], :where(:not([data-rac])):focus-visible) { background-color: var(--color-red); } } -.focus-visible\\:bg-red { - &:where([data-rac])[data-focus-visible] { +.active\\:bg-red { + &:is(:where([data-rac])[data-active], :where(:not([data-rac])):active) { background-color: var(--color-red); } - &:where(:not([data-rac])):focus-visible { +} +.disabled\\:bg-red { + &:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled) { background-color: var(--color-red); } } -.active\\:bg-red { - &:where([data-rac])[data-active] { +.in-hover\\:bg-red { + @media (hover: hover) { + :where(*:is(:where([data-rac])[data-hovered], :where(:not([data-rac])):hover)) & { + background-color: var(--color-red); + } + } +} +.in-disabled\\:bg-red { + :where(*:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled)) & { background-color: var(--color-red); } - &:where(:not([data-rac])):active { +} +.has-not-disabled\\:bg-red { + &:has(*:not(*:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled))) { background-color: var(--color-red); } } -.disabled\\:bg-red { - &:where([data-rac])[data-disabled] { +.has-invalid\\:bg-red { + &:has(*:is(:where([data-rac])[data-invalid], :where(:not([data-rac])):invalid)) { background-color: var(--color-red); } - &:where(:not([data-rac])):disabled { +} +.has-focus\\:bg-red { + &:has(*:is(:where([data-rac])[data-focused], :where(:not([data-rac])):focus)) { + background-color: var(--color-red); + } +} +.has-disabled\\:bg-red { + &:has(*:is(:where([data-rac])[data-disabled], :where(:not([data-rac])):disabled)) { background-color: var(--color-red); } } @@ -264,10 +344,7 @@ exports[`variants 1`] = ` } } .expanded\\:bg-red { - &:where([data-rac])[data-expanded] { - background-color: var(--color-red); - } - &:where(:not([data-rac]))[expanded] { + &:is(:where([data-rac])[data-expanded], :where(:not([data-rac]))[expanded]) { background-color: var(--color-red); } }