diff --git a/.eslintrc.js b/.eslintrc.js index 11ce1a55..98f996c8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,7 +19,7 @@ module.exports = { 'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0, maxBOF: 0 }], 'no-unused-expressions': 0, 'no-negated-condition': 0, - 'no-warning-comments': 0, + 'no-warning-comments': ['warn', { terms: ['todo'], location: 'start' }], 'operator-assignment': 0, 'import/no-unresolved': 0, 'chai-friendly/no-unused-expressions': 0, diff --git a/__fixtures__/!custom.js b/__fixtures__/!custom.js deleted file mode 100644 index 4c0588a6..00000000 --- a/__fixtures__/!custom.js +++ /dev/null @@ -1,10 +0,0 @@ -import tw from './macro' - -/** - * Test custom Twin classes - */ - -tw`group-hocus:bg-red-500` -tw`group-focus:bg-red-500` -tw`group-active:bg-red-500` -tw`group-visited:bg-red-500` diff --git a/__fixtures__/!variants.js b/__fixtures__/!variants.js index 04d79ab3..2573684f 100644 --- a/__fixtures__/!variants.js +++ b/__fixtures__/!variants.js @@ -1,157 +1,158 @@ import tw from './macro' -// Before/after pseudo elements -tw`before:flex` -tw`after:flex` - -// Interactive links/buttons -tw`hover:flex` -tw`focus:flex` -tw`active:flex` -tw`visited:flex` -tw`hocus:flex` // Twin only -tw`link:flex` -tw`target:flex` -tw`focus-visible:flex` - -// Form elements -tw`file:flex` - -// Form element states -tw`autofill:flex` -tw`focus-within:flex` -tw`disabled:flex` -tw`checked:flex` -tw`not-checked:flex` -tw`default:flex` -tw`enabled:flex` -tw`indeterminate:flex` -tw`in-range:flex` -tw`invalid:flex` -tw`valid:flex` -tw`optional:flex` -tw`out-of-range:flex` -tw`required:flex` -tw`placeholder:flex` -tw`placeholder-shown:flex` -tw`not-placeholder-shown:flex` -tw`read-only:flex` -tw`read-write:flex` -tw`open:flex` -tw`not-open:flex` - -// Child selectors -tw`not-disabled:flex` -tw`first-of-type:flex` -tw`not-first-of-type:flex` -tw`last-of-type:flex` -tw`not-last-of-type:flex` -tw`first-letter:flex` -tw`first-line:flex` -tw`first:flex` -tw`not-first:flex` -tw`last:flex` -tw`not-last:flex` -tw`only:flex` -tw`not-only:flex` -tw`only-of-type:flex` -tw`not-only-of-type:flex` -tw`even:flex` -tw`odd:flex` -tw`odd-of-type:flex` -tw`even-of-type:flex` -tw`svg:flex` -tw`all:flex` -tw`all-child:flex` -tw`sibling:flex` +// Pseudo element variants +tw`first-letter:block` +tw`first-line:block` +tw`marker:block` +tw`selection:block` +tw`file:block` +tw`placeholder:block` +tw`backdrop:block` +tw`before:block` +tw`after:block` +tw`before:(content block)` +tw`after:(content block)` + +// Positional +tw`first:block` +tw`last:block` +tw`only:block` +tw`odd:block` +tw`even:block` +tw`first-of-type:block` +tw`last-of-type:block` +tw`only-of-type:block` + +// State +tw`visited:block` +tw`target:block` +tw`open:block` + +// Forms +tw`default:block` +tw`checked:block` +tw`indeterminate:block` +tw`placeholder-shown:block` +tw`autofill:block` +tw`optional:block` +tw`required:block` +tw`valid:block` +tw`invalid:block` +tw`in-range:block` +tw`out-of-range:block` +tw`read-only:block` // Content -tw`empty:flex` - -// Group states -tw`group-hover:flex` -tw`group-focus:flex` - -// Media types -tw`screen:flex` -tw`print:flex` - -// Group -tw`group-hocus:flex` // Twin only -tw`group-first:shadow-md` -tw`group-last:shadow-md` -tw`group-only:shadow-md` -tw`group-even:shadow-md` -tw`group-odd:shadow-md` -tw`group-first-of-type:shadow-md` -tw`group-last-of-type:shadow-md` -tw`group-only-of-type:shadow-md` -tw`group-hover:shadow-md` -tw`group-focus:shadow-md` -tw`group-disabled:shadow-md` -tw`group-active:shadow-md` -tw`group-target:shadow-md` -tw`group-visited:shadow-md` -tw`group-default:shadow-md` -tw`group-checked:shadow-md` -tw`group-indeterminate:shadow-md` -tw`group-placeholder-shown:shadow-md` -tw`group-autofill:shadow-md` -tw`group-focus-within:shadow-md` -tw`group-focus-visible:shadow-md` -tw`group-required:shadow-md` -tw`group-valid:shadow-md` -tw`group-invalid:shadow-md` -tw`group-in-range:shadow-md` -tw`group-out-of-range:shadow-md` -tw`group-read-only:shadow-md` -tw`group-empty:shadow-md` -tw`group-open:shadow-md` -tw`group-not-open:shadow-md` - -// Direction -tw`rtl:shadow-md` -tw`ltr:shadow-md` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion -tw`motion-safe:flex` -tw`motion-reduce:flex` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer -tw`any-pointer-none:flex` -tw`any-pointer-fine:flex` -tw`any-pointer-coarse:flex` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer -tw`pointer-none:flex` -tw`pointer-fine:flex` -tw`pointer-coarse:flex` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-hover -tw`any-hover-none:flex` -tw`any-hover:flex` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover -tw`can-hover:flex` -tw`cant-hover:flex` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation -tw`landscape:flex` -tw`portrait:flex` - -// Dark/Light themes -tw`dark:bg-black` -tw`light:bg-black` -tw`dark:sm:bg-black` -tw`light:sm:bg-black` -tw`dark:group-hover:sm:bg-black` -tw`light:group-hocus:sm:bg-black` - -// Selection -tw`selection:bg-black` - -// Lists -tw`marker:bg-black` +tw`empty:block` + +// Interactive +tw`focus-within:block` +tw`hover:block` +tw`focus:block` +tw`focus-visible:block` +tw`active:block` +tw`enabled:block` +tw`disabled:block` + +// Twin custom +tw`all:block` +tw`all-child:block` +tw`sibling:block` +tw`hocus:block` +tw`link:block` +tw`read-write:block` +tw`svg:block` +tw`even-of-type:block` +tw`odd-of-type:block` + +// Not versions of the above + +// Positional +tw`not-first:block` +tw`not-last:block` +tw`not-only:block` +tw`not-odd:block` +tw`not-even:block` +tw`not-first-of-type:block` +tw`not-last-of-type:block` +tw`not-only-of-type:block` + +// State +tw`not-visited:block` +tw`not-target:block` +tw`not-open:block` + +// Forms +tw`not-default:block` +tw`not-checked:block` +tw`not-indeterminate:block` +tw`not-placeholder-shown:block` +tw`not-autofill:block` +tw`not-optional:block` +tw`not-required:block` +tw`not-valid:block` +tw`not-invalid:block` +tw`not-in-range:block` +tw`not-out-of-range:block` +tw`not-read-only:block` + +// Content +tw`not-empty:block` + +// Interactive +tw`not-focus-within:block` +tw`not-hover:block` +tw`not-focus:block` +tw`not-focus-visible:block` +tw`not-active:block` +tw`not-enabled:block` +tw`not-disabled:block` + +// Twin custom +tw`not-all:block` +tw`not-all-child:block` +tw`not-sibling:block` +tw`not-hocus:block` +tw`not-link:block` +tw`not-read-write:block` +tw`not-svg:block` +tw`not-even-of-type:block` +tw`not-odd-of-type:block` + +tw`ltr:block` +tw`rtl:block` + +tw`motion-safe:block` +tw`motion-reduce:block` + +tw`dark:block` +tw`light:block` +tw`dark:sm:block` +tw`light:sm:block` +tw`dark:group-hover:sm:block` +tw`light:group-hocus:sm:block` + +tw`print:block` +tw`screen:block` + +tw`portrait:block` +tw`landscape:block` +tw`contrast-more:block` +tw`contrast-less:block` + +tw`any-pointer-none:block` +tw`any-pointer-fine:block` +tw`any-pointer-coarse:block` + +tw`pointer-none:block` +tw`pointer-fine:block` +tw`pointer-coarse:block` + +tw`any-hover-none:block` +tw`any-hover:block` + +tw`can-hover:block` +tw`cant-hover:block` // Arbitrary values tw`first:inset-[50px]` diff --git a/__fixtures__/darkLightModeArray/darkLightMode.js b/__fixtures__/darkLightModeArray/darkLightMode.js new file mode 100644 index 00000000..9414ae8b --- /dev/null +++ b/__fixtures__/darkLightModeArray/darkLightMode.js @@ -0,0 +1,4 @@ +import tw from './macro' // twinImport + +tw`dark:block` +tw`light:block` diff --git a/__fixtures__/darkLightModeArray/tailwind.config.js b/__fixtures__/darkLightModeArray/tailwind.config.js new file mode 100644 index 00000000..71c4a21f --- /dev/null +++ b/__fixtures__/darkLightModeArray/tailwind.config.js @@ -0,0 +1,4 @@ +module.exports = { + darkMode: ['class', '.test-dark'], + lightMode: ['class', '.test-light'], +} diff --git a/__fixtures__/group/group.js b/__fixtures__/group/group.js new file mode 100644 index 00000000..e5ab78ff --- /dev/null +++ b/__fixtures__/group/group.js @@ -0,0 +1,107 @@ +import tw from './macro' + +// Positional +tw`group-first:block` +tw`group-last:block` +tw`group-only:block` +tw`group-odd:block` +tw`group-even:block` +tw`group-first-of-type:block` +tw`group-last-of-type:block` +tw`group-only-of-type:block` + +// State +tw`group-visited:block` +tw`group-target:block` +tw`group-open:block` + +// Forms +tw`group-default:block` +tw`group-checked:block` +tw`group-indeterminate:block` +tw`group-placeholder-shown:block` +tw`group-autofill:block` +tw`group-optional:block` +tw`group-required:block` +tw`group-valid:block` +tw`group-invalid:block` +tw`group-in-range:block` +tw`group-out-of-range:block` +tw`group-read-only:block` + +// Content +tw`group-empty:block` + +// Interactive +tw`group-focus-within:block` +tw`group-hover:block` +tw`group-focus:block` +tw`group-focus-visible:block` +tw`group-active:block` +tw`group-enabled:block` +tw`group-disabled:block` + +// Twin custom +tw`group-all:block` +tw`group-all-child:block` +tw`group-sibling:block` +tw`group-hocus:block` +tw`group-link:block` +tw`group-read-write:block` +tw`group-svg:block` +tw`group-even-of-type:block` +tw`group-odd-of-type:block` + +// Not versions of the above + +// Positional +tw`group-not-first:block` +tw`group-not-last:block` +tw`group-not-only:block` +tw`group-not-odd:block` +tw`group-not-even:block` +tw`group-not-first-of-type:block` +tw`group-not-last-of-type:block` +tw`group-not-only-of-type:block` + +// State +tw`group-not-visited:block` +tw`group-not-target:block` +tw`group-not-open:block` + +// Forms +tw`group-not-default:block` +tw`group-not-checked:block` +tw`group-not-indeterminate:block` +tw`group-not-placeholder-shown:block` +tw`group-not-autofill:block` +tw`group-not-optional:block` +tw`group-not-required:block` +tw`group-not-valid:block` +tw`group-not-invalid:block` +tw`group-not-in-range:block` +tw`group-not-out-of-range:block` +tw`group-not-read-only:block` + +// Content +tw`group-not-empty:block` + +// Interactive +tw`group-not-focus-within:block` +tw`group-not-hover:block` +tw`group-not-focus:block` +tw`group-not-focus-visible:block` +tw`group-not-active:block` +tw`group-not-enabled:block` +tw`group-not-disabled:block` + +// Twin custom +tw`group-not-all:block` +tw`group-not-all-child:block` +tw`group-not-sibling:block` +tw`group-not-hocus:block` +tw`group-not-link:block` +tw`group-not-read-write:block` +tw`group-not-svg:block` +tw`group-not-even-of-type:block` +tw`group-not-odd-of-type:block` diff --git a/__fixtures__/peers/peers.js b/__fixtures__/peers/peers.js index b645ef7e..2d05f671 100644 --- a/__fixtures__/peers/peers.js +++ b/__fixtures__/peers/peers.js @@ -1,37 +1,107 @@ import tw from './macro' +// Positional tw`peer-first:block` tw`peer-last:block` tw`peer-only:block` tw`peer-odd:block` +tw`peer-even:block` tw`peer-first-of-type:block` tw`peer-last-of-type:block` tw`peer-only-of-type:block` + +// State tw`peer-visited:block` tw`peer-target:block` +tw`peer-open:block` + +// Forms tw`peer-default:block` tw`peer-checked:block` tw`peer-indeterminate:block` tw`peer-placeholder-shown:block` -tw`peer-not-placeholder-shown:block` tw`peer-autofill:block` +tw`peer-optional:block` tw`peer-required:block` tw`peer-valid:block` tw`peer-invalid:block` tw`peer-in-range:block` tw`peer-out-of-range:block` tw`peer-read-only:block` + +// Content tw`peer-empty:block` + +// Interactive tw`peer-focus-within:block` tw`peer-hover:block` tw`peer-focus:block` tw`peer-focus-visible:block` tw`peer-active:block` +tw`peer-enabled:block` tw`peer-disabled:block` -tw`peer-open:block` + +// Twin custom +tw`peer-all:block` +tw`peer-all-child:block` +tw`peer-sibling:block` +tw`peer-hocus:block` +tw`peer-link:block` +tw`peer-read-write:block` +tw`peer-svg:block` +tw`peer-even-of-type:block` +tw`peer-odd-of-type:block` + +// Not versions of the above + +// Positional +tw`peer-not-first:block` +tw`peer-not-last:block` +tw`peer-not-only:block` +tw`peer-not-odd:block` +tw`peer-not-even:block` +tw`peer-not-first-of-type:block` +tw`peer-not-last-of-type:block` +tw`peer-not-only-of-type:block` + +// State +tw`peer-not-visited:block` +tw`peer-not-target:block` tw`peer-not-open:block` -tw`peer-focus:peer-hover:block` -tw`peer-disabled:peer-focus:peer-hover:first:block` -tw`first:peer-focus:peer-hover:block` -tw`peer-focus:first:peer-hover:peer-active:block` +// Forms +tw`peer-not-default:block` +tw`peer-not-checked:block` +tw`peer-not-indeterminate:block` +tw`peer-not-placeholder-shown:block` +tw`peer-not-autofill:block` +tw`peer-not-optional:block` +tw`peer-not-required:block` +tw`peer-not-valid:block` +tw`peer-not-invalid:block` +tw`peer-not-in-range:block` +tw`peer-not-out-of-range:block` +tw`peer-not-read-only:block` + +// Content +tw`peer-not-empty:block` + +// Interactive +tw`peer-not-focus-within:block` +tw`peer-not-hover:block` +tw`peer-not-focus:block` +tw`peer-not-focus-visible:block` +tw`peer-not-active:block` +tw`peer-not-enabled:block` +tw`peer-not-disabled:block` + +// Twin custom +tw`peer-not-all:block` +tw`peer-not-all-child:block` +tw`peer-not-sibling:block` +tw`peer-not-hocus:block` +tw`peer-not-link:block` +tw`peer-not-read-write:block` +tw`peer-not-svg:block` +tw`peer-not-even-of-type:block` +tw`peer-not-odd-of-type:block` diff --git a/__fixtures__/pluginExamples/pluginExamples.js b/__fixtures__/pluginExamples/pluginExamples.js new file mode 100644 index 00000000..f7eee1c0 --- /dev/null +++ b/__fixtures__/pluginExamples/pluginExamples.js @@ -0,0 +1,22 @@ +import tw, { globalStyles } from './macro' + +tw`content-auto` +tw`content-hidden` +tw`content-visible` + +tw`tab-1` +tw`tab-2` +tw`tab-4` +tw`tab-8` + +tw`btn` +tw`btn-blue` +tw`btn-red` +tw`btn btn-blue btn-red` + +globalStyles + +tw`test-1:block` +tw`test-2:block` +tw`test-3:block` +tw`test-4:block` diff --git a/__fixtures__/pluginExamples/tailwind.config.js b/__fixtures__/pluginExamples/tailwind.config.js new file mode 100644 index 00000000..b632d46c --- /dev/null +++ b/__fixtures__/pluginExamples/tailwind.config.js @@ -0,0 +1,126 @@ +// https://tailwindcss.com/docs/plugins +const plugin = require('tailwindcss/plugin') + +const addUtilities = function ({ addUtilities }) { + addUtilities({ + '.content-auto': { + 'content-visibility': 'auto', + }, + '.content-hidden': { + 'content-visibility': 'hidden', + }, + '.content-visible': { + 'content-visibility': 'visible', + }, + }) +} + +const defaultValues = plugin( + function ({ matchUtilities, theme }) { + matchUtilities( + { + tab: value => ({ + tabSizeTest: value, + }), + }, + { values: theme('tabSizeTest') } + ) + }, + { + theme: { + tabSizeTest: { + 1: '1', + 2: '2', + 4: '4', + 8: '8', + }, + }, + } +) + +const addComponents = function ({ addComponents }) { + addComponents({ + '.btn': { + padding: '.5rem 1rem', + borderRadius: '.25rem', + fontWeight: '600', + }, + '.btn-blue': { + backgroundColor: '#3490dc', + color: '#fff', + '&:hover': { + backgroundColor: '#2779bd', + }, + }, + '.btn-red': { + backgroundColor: '#e3342f', + color: '#fff', + '&:hover': { + backgroundColor: '#cc1f1a', + }, + }, + }) +} + +const addBase = function ({ addBase, theme }) { + addBase({ + h1: { fontSize: theme('fontSize.2xl') }, + h2: { fontSize: theme('fontSize.xl') }, + h3: { fontSize: theme('fontSize.lg') }, + }) +} + +const addVariant = function ({ addVariant }) { + addVariant('test-1', '&:test1') + addVariant('test-2', ['&:hover', '&:focus']) + addVariant('test-3', '@supports (display: grid)') + addVariant('test-4', 'html.dark &.something') +} + +// https://github.com/tailwindlabs/tailwindcss/blob/master/tests/match-variants.test.js +// const matchVariant = ({ matchVariant }) => { +// matchVariant({ +// potato: flavor => `.potato-${flavor} &`, +// carrot: flavor => `@media (carrot: ${flavor})`, +// beetroot: flavor => `@media (beetroot: ${flavor}) { &:beetroot }`, +// }) +// matchVariant( +// { +// tooltip: side => `&${side}`, +// }, +// { +// values: { +// bottom: '[data-location="bottom"]', +// top: '[data-location="top"]', +// }, +// } +// ) +// matchVariant( +// { +// alphabet: side => `&${side}`, +// }, +// { +// values: { +// a: '[data-value="a"]', +// b: '[data-value="b"]', +// c: '[data-value="c"]', +// d: '[data-value="d"]', +// }, +// } +// ) +// matchVariant({ +// test: selector => selector.split(',').map(selector => `&.${selector} > *`), +// }) +// } + +module.exports = { + corePlugins: { preflight: false }, + plugins: [ + addUtilities, + defaultValues, + addComponents, + addBase, + addVariant, + // matchVariant, + ], +} diff --git a/__fixtures__/variantOrdering/variantOrdering.js b/__fixtures__/variantOrdering/variantOrdering.js new file mode 100644 index 00000000..850f5b2c --- /dev/null +++ b/__fixtures__/variantOrdering/variantOrdering.js @@ -0,0 +1,5 @@ +import tw from './macro' + +tw`before:valid:rtl:motion-safe:contrast-more:dark:print:portrait:any-pointer-fine:block` + +tw`any-pointer-fine:portrait:print:dark:contrast-more:motion-safe:rtl:valid:before:mt-5` diff --git a/__snapshots__/plugin.test.js.snap b/__snapshots__/plugin.test.js.snap index 31f7ad6c..8982bcc0 100644 --- a/__snapshots__/plugin.test.js.snap +++ b/__snapshots__/plugin.test.js.snap @@ -1,51 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`twin.macro !custom.js: !custom.js 1`] = ` - -import tw from './macro' - -/** - * Test custom Twin classes - */ - -tw\`group-hocus:bg-red-500\` -tw\`group-focus:bg-red-500\` -tw\`group-active:bg-red-500\` -tw\`group-visited:bg-red-500\` - - ↓ ↓ ↓ ↓ ↓ ↓ - -/** - * Test custom Twin classes - */ -;({ - '.group:hover &, .group:focus &': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(239 68 68 / var(--tw-bg-opacity))', - }, -}) -;({ - '.group:focus &': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(239 68 68 / var(--tw-bg-opacity))', - }, -}) -;({ - '.group:active &': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(239 68 68 / var(--tw-bg-opacity))', - }, -}) -;({ - '.group:visited &': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(239 68 68 / var(--tw-bg-opacity))', - }, -}) - - -`; - exports[`twin.macro !general.js: !general.js 1`] = ` import tw from './macro' @@ -365,8 +319,8 @@ const VariantImportantPrefixMergeCheck = _styled.div({ }) const MultiVariantImportantPrefixMergeCheck = _styled.div({ - ':first-child': { - '@media (min-width: 768px)': { + '@media (min-width: 768px)': { + ':first-child': { '--tw-gradient-from': '#000 !important', '--tw-gradient-stops': 'var(--tw-gradient-from), var(--tw-gradient-to, rgb(0 0 0 / 0) !important)', @@ -1052,10 +1006,12 @@ const basic = { }, } const subMediaQuery = { - ':focus-within': { - '@media (min-width: 768px)': { + '@media (min-width: 768px)': { + ':focus-within': { display: 'flex', }, + }, + ':focus-within': { marginTop: '1.25rem', }, } @@ -1124,10 +1080,12 @@ const multipleGroups = { '--tw-bg-opacity': '1', backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', }, - ':focus-within': { - '@media (min-width: 768px)': { + '@media (min-width: 768px)': { + ':focus-within': { display: 'flex', }, + }, + ':focus-within': { marginTop: '1.25rem', }, } @@ -1259,158 +1217,159 @@ exports[`twin.macro !variants.js: !variants.js 1`] = ` import tw from './macro' -// Before/after pseudo elements -tw\`before:flex\` -tw\`after:flex\` - -// Interactive links/buttons -tw\`hover:flex\` -tw\`focus:flex\` -tw\`active:flex\` -tw\`visited:flex\` -tw\`hocus:flex\` // Twin only -tw\`link:flex\` -tw\`target:flex\` -tw\`focus-visible:flex\` - -// Form elements -tw\`file:flex\` - -// Form element states -tw\`autofill:flex\` -tw\`focus-within:flex\` -tw\`disabled:flex\` -tw\`checked:flex\` -tw\`not-checked:flex\` -tw\`default:flex\` -tw\`enabled:flex\` -tw\`indeterminate:flex\` -tw\`in-range:flex\` -tw\`invalid:flex\` -tw\`valid:flex\` -tw\`optional:flex\` -tw\`out-of-range:flex\` -tw\`required:flex\` -tw\`placeholder:flex\` -tw\`placeholder-shown:flex\` -tw\`not-placeholder-shown:flex\` -tw\`read-only:flex\` -tw\`read-write:flex\` -tw\`open:flex\` -tw\`not-open:flex\` - -// Child selectors -tw\`not-disabled:flex\` -tw\`first-of-type:flex\` -tw\`not-first-of-type:flex\` -tw\`last-of-type:flex\` -tw\`not-last-of-type:flex\` -tw\`first-letter:flex\` -tw\`first-line:flex\` -tw\`first:flex\` -tw\`not-first:flex\` -tw\`last:flex\` -tw\`not-last:flex\` -tw\`only:flex\` -tw\`not-only:flex\` -tw\`only-of-type:flex\` -tw\`not-only-of-type:flex\` -tw\`even:flex\` -tw\`odd:flex\` -tw\`odd-of-type:flex\` -tw\`even-of-type:flex\` -tw\`svg:flex\` -tw\`all:flex\` -tw\`all-child:flex\` -tw\`sibling:flex\` +// Pseudo element variants +tw\`first-letter:block\` +tw\`first-line:block\` +tw\`marker:block\` +tw\`selection:block\` +tw\`file:block\` +tw\`placeholder:block\` +tw\`backdrop:block\` +tw\`before:block\` +tw\`after:block\` +tw\`before:(content block)\` +tw\`after:(content block)\` + +// Positional +tw\`first:block\` +tw\`last:block\` +tw\`only:block\` +tw\`odd:block\` +tw\`even:block\` +tw\`first-of-type:block\` +tw\`last-of-type:block\` +tw\`only-of-type:block\` + +// State +tw\`visited:block\` +tw\`target:block\` +tw\`open:block\` + +// Forms +tw\`default:block\` +tw\`checked:block\` +tw\`indeterminate:block\` +tw\`placeholder-shown:block\` +tw\`autofill:block\` +tw\`optional:block\` +tw\`required:block\` +tw\`valid:block\` +tw\`invalid:block\` +tw\`in-range:block\` +tw\`out-of-range:block\` +tw\`read-only:block\` + +// Content +tw\`empty:block\` + +// Interactive +tw\`focus-within:block\` +tw\`hover:block\` +tw\`focus:block\` +tw\`focus-visible:block\` +tw\`active:block\` +tw\`enabled:block\` +tw\`disabled:block\` + +// Twin custom +tw\`all:block\` +tw\`all-child:block\` +tw\`sibling:block\` +tw\`hocus:block\` +tw\`link:block\` +tw\`read-write:block\` +tw\`svg:block\` +tw\`even-of-type:block\` +tw\`odd-of-type:block\` + +// Not versions of the above + +// Positional +tw\`not-first:block\` +tw\`not-last:block\` +tw\`not-only:block\` +tw\`not-odd:block\` +tw\`not-even:block\` +tw\`not-first-of-type:block\` +tw\`not-last-of-type:block\` +tw\`not-only-of-type:block\` + +// State +tw\`not-visited:block\` +tw\`not-target:block\` +tw\`not-open:block\` + +// Forms +tw\`not-default:block\` +tw\`not-checked:block\` +tw\`not-indeterminate:block\` +tw\`not-placeholder-shown:block\` +tw\`not-autofill:block\` +tw\`not-optional:block\` +tw\`not-required:block\` +tw\`not-valid:block\` +tw\`not-invalid:block\` +tw\`not-in-range:block\` +tw\`not-out-of-range:block\` +tw\`not-read-only:block\` // Content -tw\`empty:flex\` - -// Group states -tw\`group-hover:flex\` -tw\`group-focus:flex\` - -// Media types -tw\`screen:flex\` -tw\`print:flex\` - -// Group -tw\`group-hocus:flex\` // Twin only -tw\`group-first:shadow-md\` -tw\`group-last:shadow-md\` -tw\`group-only:shadow-md\` -tw\`group-even:shadow-md\` -tw\`group-odd:shadow-md\` -tw\`group-first-of-type:shadow-md\` -tw\`group-last-of-type:shadow-md\` -tw\`group-only-of-type:shadow-md\` -tw\`group-hover:shadow-md\` -tw\`group-focus:shadow-md\` -tw\`group-disabled:shadow-md\` -tw\`group-active:shadow-md\` -tw\`group-target:shadow-md\` -tw\`group-visited:shadow-md\` -tw\`group-default:shadow-md\` -tw\`group-checked:shadow-md\` -tw\`group-indeterminate:shadow-md\` -tw\`group-placeholder-shown:shadow-md\` -tw\`group-autofill:shadow-md\` -tw\`group-focus-within:shadow-md\` -tw\`group-focus-visible:shadow-md\` -tw\`group-required:shadow-md\` -tw\`group-valid:shadow-md\` -tw\`group-invalid:shadow-md\` -tw\`group-in-range:shadow-md\` -tw\`group-out-of-range:shadow-md\` -tw\`group-read-only:shadow-md\` -tw\`group-empty:shadow-md\` -tw\`group-open:shadow-md\` -tw\`group-not-open:shadow-md\` - -// Direction -tw\`rtl:shadow-md\` -tw\`ltr:shadow-md\` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion -tw\`motion-safe:flex\` -tw\`motion-reduce:flex\` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer -tw\`any-pointer-none:flex\` -tw\`any-pointer-fine:flex\` -tw\`any-pointer-coarse:flex\` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer -tw\`pointer-none:flex\` -tw\`pointer-fine:flex\` -tw\`pointer-coarse:flex\` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-hover -tw\`any-hover-none:flex\` -tw\`any-hover:flex\` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover -tw\`can-hover:flex\` -tw\`cant-hover:flex\` - -// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation -tw\`landscape:flex\` -tw\`portrait:flex\` - -// Dark/Light themes -tw\`dark:bg-black\` -tw\`light:bg-black\` -tw\`dark:sm:bg-black\` -tw\`light:sm:bg-black\` -tw\`dark:group-hover:sm:bg-black\` -tw\`light:group-hocus:sm:bg-black\` - -// Selection -tw\`selection:bg-black\` - -// Lists -tw\`marker:bg-black\` +tw\`not-empty:block\` + +// Interactive +tw\`not-focus-within:block\` +tw\`not-hover:block\` +tw\`not-focus:block\` +tw\`not-focus-visible:block\` +tw\`not-active:block\` +tw\`not-enabled:block\` +tw\`not-disabled:block\` + +// Twin custom +tw\`not-all:block\` +tw\`not-all-child:block\` +tw\`not-sibling:block\` +tw\`not-hocus:block\` +tw\`not-link:block\` +tw\`not-read-write:block\` +tw\`not-svg:block\` +tw\`not-even-of-type:block\` +tw\`not-odd-of-type:block\` + +tw\`ltr:block\` +tw\`rtl:block\` + +tw\`motion-safe:block\` +tw\`motion-reduce:block\` + +tw\`dark:block\` +tw\`light:block\` +tw\`dark:sm:block\` +tw\`light:sm:block\` +tw\`dark:group-hover:sm:block\` +tw\`light:group-hocus:sm:block\` + +tw\`print:block\` +tw\`screen:block\` + +tw\`portrait:block\` +tw\`landscape:block\` +tw\`contrast-more:block\` +tw\`contrast-less:block\` + +tw\`any-pointer-none:block\` +tw\`any-pointer-fine:block\` +tw\`any-pointer-coarse:block\` + +tw\`pointer-none:block\` +tw\`pointer-fine:block\` +tw\`pointer-coarse:block\` + +tw\`any-hover-none:block\` +tw\`any-hover:block\` + +tw\`can-hover:block\` +tw\`cant-hover:block\` // Arbitrary values tw\`first:inset-[50px]\` @@ -1421,782 +1380,619 @@ tw\`xl:placeholder-red-500! first:md:block sm:disabled:flex\` ↓ ↓ ↓ ↓ ↓ ↓ -// Before/after pseudo elements +// Pseudo element variants ;({ - ':before': { - content: '""', - display: 'flex', + '::first-letter': { + display: 'block', }, }) ;({ - ':after': { - content: '""', - display: 'flex', + '::first-line': { + display: 'block', }, -}) // Interactive links/buttons - +}) ;({ - ':hover': { - display: 'flex', + '*::marker, ::marker': { + display: 'block', }, }) ;({ - ':focus': { - display: 'flex', + '*::selection, ::selection': { + display: 'block', }, }) ;({ - ':active': { - display: 'flex', + '::file-selector-button': { + display: 'block', }, }) ;({ - ':visited': { - display: 'flex', + '::placeholder': { + display: 'block', }, }) ;({ - ':hover, :focus': { - display: 'flex', + '::backdrop': { + display: 'block', }, -}) // Twin only - +}) ;({ - ':link': { - display: 'flex', + ':before': { + content: '""', + display: 'block', }, }) ;({ - ':target': { - display: 'flex', + ':after': { + content: '""', + display: 'block', }, }) ;({ - ':focus-visible': { - display: 'flex', + ':before': { + content: '""', + display: 'block', }, -}) // Form elements - +}) ;({ - '::file-selector-button': { - display: 'flex', + ':after': { + content: '""', + display: 'block', }, -}) // Form element states +}) // Positional ;({ - ':autofill': { - display: 'flex', + ':first-child': { + display: 'block', }, }) ;({ - ':focus-within': { - display: 'flex', + ':last-child': { + display: 'block', }, }) ;({ - ':disabled': { - display: 'flex', + ':only-child': { + display: 'block', }, }) ;({ - ':checked': { - display: 'flex', + ':nth-child(odd)': { + display: 'block', }, }) ;({ - ':not(:checked)': { - display: 'flex', + ':nth-child(even)': { + display: 'block', }, }) ;({ - ':default': { - display: 'flex', + ':first-of-type': { + display: 'block', }, }) ;({ - ':enabled': { - display: 'flex', + ':last-of-type': { + display: 'block', }, }) ;({ - ':indeterminate': { - display: 'flex', + ':only-of-type': { + display: 'block', }, -}) +}) // State + ;({ - ':in-range': { - display: 'flex', + ':visited': { + display: 'block', }, }) ;({ - ':invalid': { - display: 'flex', + ':target': { + display: 'block', }, }) ;({ - ':valid': { - display: 'flex', + '[open]': { + display: 'block', }, -}) +}) // Forms + ;({ - ':optional': { - display: 'flex', + ':default': { + display: 'block', }, }) ;({ - ':out-of-range': { - display: 'flex', + ':checked': { + display: 'block', }, }) ;({ - ':required': { - display: 'flex', + ':indeterminate': { + display: 'block', }, }) ;({ - '::placeholder': { - display: 'flex', + ':placeholder-shown': { + display: 'block', }, }) ;({ - ':placeholder-shown': { - display: 'flex', + ':autofill': { + display: 'block', }, }) ;({ - ':not(:placeholder-shown)': { - display: 'flex', + ':optional': { + display: 'block', }, }) ;({ - ':read-only': { - display: 'flex', + ':required': { + display: 'block', }, }) ;({ - ':read-write': { - display: 'flex', + ':valid': { + display: 'block', }, }) ;({ - ':open': { - display: 'flex', + ':invalid': { + display: 'block', }, }) ;({ - ':not(:open)': { - display: 'flex', + ':in-range': { + display: 'block', }, -}) // Child selectors - +}) ;({ - ':not(:disabled)': { - display: 'flex', + ':out-of-range': { + display: 'block', }, }) ;({ - ':first-of-type': { - display: 'flex', + ':read-only': { + display: 'block', }, -}) +}) // Content + ;({ - ':not(:first-of-type)': { - display: 'flex', + ':empty': { + display: 'block', }, -}) +}) // Interactive + ;({ - ':last-of-type': { - display: 'flex', + ':focus-within': { + display: 'block', }, }) ;({ - ':not(:last-of-type)': { - display: 'flex', + ':hover': { + display: 'block', }, }) ;({ - '::first-letter': { - display: 'flex', + ':focus': { + display: 'block', }, }) ;({ - '::first-line': { - display: 'flex', + ':focus-visible': { + display: 'block', }, }) ;({ - ':first-child': { - display: 'flex', + ':active': { + display: 'block', }, }) ;({ - ':not(:first-child)': { - display: 'flex', + ':enabled': { + display: 'block', }, }) ;({ - ':last-child': { - display: 'flex', + ':disabled': { + display: 'block', + }, +}) // Twin custom + +;({ + '*': { + display: 'block', }, }) ;({ - ':not(:last-child)': { - display: 'flex', + '> *': { + display: 'block', }, }) ;({ - ':only-child': { - display: 'flex', + '~ *': { + display: 'block', }, }) ;({ - ':not(:only-child)': { - display: 'flex', + ':hover, :focus': { + display: 'block', }, }) ;({ - ':only-of-type': { - display: 'flex', + ':link': { + display: 'block', }, }) ;({ - ':not(:only-of-type)': { - display: 'flex', + ':read-write': { + display: 'block', }, }) ;({ - ':nth-child(even)': { - display: 'flex', + svg: { + display: 'block', }, }) ;({ - ':nth-child(odd)': { - display: 'flex', + ':nth-of-type(even)': { + display: 'block', }, }) ;({ ':nth-of-type(odd)': { - display: 'flex', + display: 'block', }, -}) +}) // Not versions of the above +// Positional + ;({ - ':nth-of-type(even)': { - display: 'flex', + ':not(:first-child)': { + display: 'block', }, }) ;({ - svg: { - display: 'flex', + ':not(:last-child)': { + display: 'block', }, }) ;({ - '*': { - display: 'flex', + ':not(:only-child)': { + display: 'block', }, }) ;({ - '> *': { - display: 'flex', + ':not(:nth-child(odd))': { + display: 'block', }, }) ;({ - '~ *': { - display: 'flex', + ':not(:nth-child(even))': { + display: 'block', }, -}) // Content - +}) ;({ - ':empty': { - display: 'flex', + ':not(:first-of-type)': { + display: 'block', }, -}) // Group states - +}) ;({ - '.group:hover &': { - display: 'flex', + ':not(:last-of-type)': { + display: 'block', }, }) ;({ - '.group:focus &': { - display: 'flex', + ':not(:only-of-type)': { + display: 'block', }, -}) // Media types +}) // State ;({ - '@media screen': { - display: 'flex', + ':not(:visited)': { + display: 'block', }, }) ;({ - '@media print': { - display: 'flex', + ':not(:target)': { + display: 'block', }, -}) // Group - +}) ;({ - '.group:hover &, .group:focus &': { - display: 'flex', + ':not([open])': { + display: 'block', }, -}) // Twin only +}) // Forms ;({ - '.group:first-child &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:default)': { + display: 'block', }, }) ;({ - '.group:last-child &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:checked)': { + display: 'block', }, }) ;({ - '.group:only-child &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:indeterminate)': { + display: 'block', }, }) ;({ - '.group:nth-child(even) &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:placeholder-shown)': { + display: 'block', }, }) ;({ - '.group:nth-child(odd) &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:autofill)': { + display: 'block', }, }) ;({ - '.group:first-of-type &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:optional)': { + display: 'block', }, }) ;({ - '.group:last-of-type &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:required)': { + display: 'block', }, }) ;({ - '.group:not(:first-of-type) &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:valid)': { + display: 'block', }, }) ;({ - '.group:hover &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:invalid)': { + display: 'block', }, }) ;({ - '.group:focus &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:in-range)': { + display: 'block', }, }) ;({ - '.group:disabled &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:out-of-range)': { + display: 'block', }, }) ;({ - '.group:active &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:read-only)': { + display: 'block', }, -}) +}) // Content + ;({ - '.group:target &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:empty)': { + display: 'block', }, -}) +}) // Interactive + ;({ - '.group:visited &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:focus-within)': { + display: 'block', }, }) ;({ - '.group:default &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:hover)': { + display: 'block', }, }) ;({ - '.group:checked &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:focus)': { + display: 'block', }, }) ;({ - '.group:indeterminate &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:focus-visible)': { + display: 'block', }, }) ;({ - '.group:placeholder-shown &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:active)': { + display: 'block', }, }) ;({ - '.group:autofill &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:enabled)': { + display: 'block', }, }) ;({ - '.group:focus-within &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:disabled)': { + display: 'block', }, -}) +}) // Twin custom + ;({ - '.group:focus-visible &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(*)': { + display: 'block', }, }) ;({ - '.group:required &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(> *)': { + display: 'block', }, }) ;({ - '.group:valid &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(~ *)': { + display: 'block', }, }) ;({ - '.group:invalid &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:hover), :not(:focus)': { + display: 'block', }, }) ;({ - '.group:in-range &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:link)': { + display: 'block', }, }) ;({ - '.group:out-of-range &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:read-write)': { + display: 'block', }, }) ;({ - '.group:read-only &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(svg)': { + display: 'block', }, }) ;({ - '.group:empty &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:nth-of-type(even))': { + display: 'block', }, }) ;({ - '.group:open &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + ':not(:nth-of-type(odd))': { + display: 'block', }, }) ;({ - '.group:not(:open) &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + '[dir="ltr"] &': { + display: 'block', }, -}) // Direction - +}) ;({ '[dir="rtl"] &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', + display: 'block', }, }) -;({ - '[dir="ltr"] &': { - '--tw-shadow': - '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', - '--tw-shadow-colored': - '0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)', - boxShadow: - 'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)', - }, -}) // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion - ;({ '@media (prefers-reduced-motion: no-preference)': { - display: 'flex', + display: 'block', }, }) ;({ '@media (prefers-reduced-motion: reduce)': { - display: 'flex', + display: 'block', }, -}) // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer - +}) ;({ - '@media (any-pointer: none)': { - display: 'flex', + '@media (prefers-color-scheme: dark)': { + display: 'block', }, }) ;({ - '@media (any-pointer: fine)': { - display: 'flex', + '@media (prefers-color-scheme: light)': { + display: 'block', }, }) ;({ - '@media (any-pointer: coarse)': { - display: 'flex', + '@media (prefers-color-scheme: dark)': { + '@media (min-width: 640px)': { + display: 'block', + }, }, -}) // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer - +}) ;({ - '@media (pointer: none)': { - display: 'flex', + '@media (prefers-color-scheme: light)': { + '@media (min-width: 640px)': { + display: 'block', + }, }, }) ;({ - '@media (pointer: fine)': { - display: 'flex', + '@media (prefers-color-scheme: dark)': { + '@media (min-width: 640px)': { + '.group:hover &': { + display: 'block', + }, + }, }, }) ;({ - '@media (pointer: coarse)': { - display: 'flex', + '@media (prefers-color-scheme: light)': { + '@media (min-width: 640px)': { + '.group:hover &, .group:focus &': { + display: 'block', + }, + }, }, -}) // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-hover - +}) ;({ - '@media (any-hover: none)': { - display: 'flex', + '@media print': { + display: 'block', }, }) ;({ - '@media (any-hover: hover)': { - display: 'flex', + '@media screen': { + display: 'block', }, -}) // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover - +}) ;({ - '@media (hover: hover)': { - display: 'flex', + '@media (orientation: portrait)': { + display: 'block', }, }) ;({ - '@media (hover: none)': { - display: 'flex', + '@media (orientation: landscape)': { + display: 'block', }, -}) // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation - +}) ;({ - '@media (orientation: landscape)': { - display: 'flex', + '@media (prefers-contrast: more)': { + display: 'block', }, }) ;({ - '@media (orientation: portrait)': { - display: 'flex', + '@media (prefers-contrast: less)': { + display: 'block', }, -}) // Dark/Light themes - +}) ;({ - '@media (prefers-color-scheme: dark)': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', + '@media (any-pointer: none)': { + display: 'block', }, }) ;({ - '@media (prefers-color-scheme: light)': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', + '@media (any-pointer: fine)': { + display: 'block', }, }) ;({ - '@media (prefers-color-scheme: dark)': { - '@media (min-width: 640px)': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', - }, + '@media (any-pointer: coarse)': { + display: 'block', }, }) ;({ - '@media (prefers-color-scheme: light)': { - '@media (min-width: 640px)': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', - }, + '@media (pointer: none)': { + display: 'block', }, }) ;({ - '@media (prefers-color-scheme: dark)': { - '.group:hover &': { - '@media (min-width: 640px)': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', - }, - }, + '@media (pointer: fine)': { + display: 'block', }, }) ;({ - '@media (prefers-color-scheme: light)': { - '.group:hover &, .group:focus &': { - '@media (min-width: 640px)': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', - }, - }, + '@media (pointer: coarse)': { + display: 'block', }, -}) // Selection - +}) ;({ - '::selection': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', + '@media (any-hover: none)': { + display: 'block', }, -}) // Lists - +}) ;({ - '::marker, *::marker': { - '--tw-bg-opacity': '1', - backgroundColor: 'rgb(0 0 0 / var(--tw-bg-opacity))', + '@media (any-hover: hover)': { + display: 'block', + }, +}) +;({ + '@media (hover: hover)': { + display: 'block', + }, +}) +;({ + '@media (hover: none)': { + display: 'block', }, }) // Arbitrary values @@ -2216,8 +2012,8 @@ tw\`xl:placeholder-red-500! first:md:block sm:disabled:flex\` }) // Random ;({ - ':first-child': { - '@media (min-width: 768px)': { + '@media (min-width: 768px)': { + ':first-child': { display: 'block', }, }, @@ -21202,6 +20998,30 @@ tw\`cursor-[url(hand.cur), pointer]\` }) +`; + +exports[`twin.macro darkLightMode.js: darkLightMode.js 1`] = ` + +import tw from './macro' // twinImport + +tw\`dark:block\` +tw\`light:block\` + + ↓ ↓ ↓ ↓ ↓ ↓ + +// twinImport +;({ + '.test-dark &': { + display: 'block', + }, +}) +;({ + '.test-light &': { + display: 'block', + }, +}) + + `; exports[`twin.macro directionalBorders.js: directionalBorders.js 1`] = ` @@ -31374,6 +31194,535 @@ tw\`grid-rows-[200px minmax(900px, 1fr) 100px]\` }) +`; + +exports[`twin.macro group.js: group.js 1`] = ` + +import tw from './macro' + +// Positional +tw\`group-first:block\` +tw\`group-last:block\` +tw\`group-only:block\` +tw\`group-odd:block\` +tw\`group-even:block\` +tw\`group-first-of-type:block\` +tw\`group-last-of-type:block\` +tw\`group-only-of-type:block\` + +// State +tw\`group-visited:block\` +tw\`group-target:block\` +tw\`group-open:block\` + +// Forms +tw\`group-default:block\` +tw\`group-checked:block\` +tw\`group-indeterminate:block\` +tw\`group-placeholder-shown:block\` +tw\`group-autofill:block\` +tw\`group-optional:block\` +tw\`group-required:block\` +tw\`group-valid:block\` +tw\`group-invalid:block\` +tw\`group-in-range:block\` +tw\`group-out-of-range:block\` +tw\`group-read-only:block\` + +// Content +tw\`group-empty:block\` + +// Interactive +tw\`group-focus-within:block\` +tw\`group-hover:block\` +tw\`group-focus:block\` +tw\`group-focus-visible:block\` +tw\`group-active:block\` +tw\`group-enabled:block\` +tw\`group-disabled:block\` + +// Twin custom +tw\`group-all:block\` +tw\`group-all-child:block\` +tw\`group-sibling:block\` +tw\`group-hocus:block\` +tw\`group-link:block\` +tw\`group-read-write:block\` +tw\`group-svg:block\` +tw\`group-even-of-type:block\` +tw\`group-odd-of-type:block\` + +// Not versions of the above + +// Positional +tw\`group-not-first:block\` +tw\`group-not-last:block\` +tw\`group-not-only:block\` +tw\`group-not-odd:block\` +tw\`group-not-even:block\` +tw\`group-not-first-of-type:block\` +tw\`group-not-last-of-type:block\` +tw\`group-not-only-of-type:block\` + +// State +tw\`group-not-visited:block\` +tw\`group-not-target:block\` +tw\`group-not-open:block\` + +// Forms +tw\`group-not-default:block\` +tw\`group-not-checked:block\` +tw\`group-not-indeterminate:block\` +tw\`group-not-placeholder-shown:block\` +tw\`group-not-autofill:block\` +tw\`group-not-optional:block\` +tw\`group-not-required:block\` +tw\`group-not-valid:block\` +tw\`group-not-invalid:block\` +tw\`group-not-in-range:block\` +tw\`group-not-out-of-range:block\` +tw\`group-not-read-only:block\` + +// Content +tw\`group-not-empty:block\` + +// Interactive +tw\`group-not-focus-within:block\` +tw\`group-not-hover:block\` +tw\`group-not-focus:block\` +tw\`group-not-focus-visible:block\` +tw\`group-not-active:block\` +tw\`group-not-enabled:block\` +tw\`group-not-disabled:block\` + +// Twin custom +tw\`group-not-all:block\` +tw\`group-not-all-child:block\` +tw\`group-not-sibling:block\` +tw\`group-not-hocus:block\` +tw\`group-not-link:block\` +tw\`group-not-read-write:block\` +tw\`group-not-svg:block\` +tw\`group-not-even-of-type:block\` +tw\`group-not-odd-of-type:block\` + + ↓ ↓ ↓ ↓ ↓ ↓ + +// Positional +;({ + '.group:first-child &': { + display: 'block', + }, +}) +;({ + '.group:last-child &': { + display: 'block', + }, +}) +;({ + '.group:only-child &': { + display: 'block', + }, +}) +;({ + '.group:nth-child(odd) &': { + display: 'block', + }, +}) +;({ + '.group:nth-child(even) &': { + display: 'block', + }, +}) +;({ + '.group:first-of-type &': { + display: 'block', + }, +}) +;({ + '.group:last-of-type &': { + display: 'block', + }, +}) +;({ + '.group:only-of-type &': { + display: 'block', + }, +}) // State + +;({ + '.group:visited &': { + display: 'block', + }, +}) +;({ + '.group:target &': { + display: 'block', + }, +}) +;({ + '.group[open] &': { + display: 'block', + }, +}) // Forms + +;({ + '.group:default &': { + display: 'block', + }, +}) +;({ + '.group:checked &': { + display: 'block', + }, +}) +;({ + '.group:indeterminate &': { + display: 'block', + }, +}) +;({ + '.group:placeholder-shown &': { + display: 'block', + }, +}) +;({ + '.group:autofill &': { + display: 'block', + }, +}) +;({ + '.group:optional &': { + display: 'block', + }, +}) +;({ + '.group:required &': { + display: 'block', + }, +}) +;({ + '.group:valid &': { + display: 'block', + }, +}) +;({ + '.group:invalid &': { + display: 'block', + }, +}) +;({ + '.group:in-range &': { + display: 'block', + }, +}) +;({ + '.group:out-of-range &': { + display: 'block', + }, +}) +;({ + '.group:read-only &': { + display: 'block', + }, +}) // Content + +;({ + '.group:empty &': { + display: 'block', + }, +}) // Interactive + +;({ + '.group:focus-within &': { + display: 'block', + }, +}) +;({ + '.group:hover &': { + display: 'block', + }, +}) +;({ + '.group:focus &': { + display: 'block', + }, +}) +;({ + '.group:focus-visible &': { + display: 'block', + }, +}) +;({ + '.group:active &': { + display: 'block', + }, +}) +;({ + '.group:enabled &': { + display: 'block', + }, +}) +;({ + '.group:disabled &': { + display: 'block', + }, +}) // Twin custom + +;({ + '.group * &': { + display: 'block', + }, +}) +;({ + '.group > * &': { + display: 'block', + }, +}) +;({ + '.group ~ * &': { + display: 'block', + }, +}) +;({ + '.group:hover &, .group:focus &': { + display: 'block', + }, +}) +;({ + '.group:link &': { + display: 'block', + }, +}) +;({ + '.group:read-write &': { + display: 'block', + }, +}) +;({ + '.group svg &': { + display: 'block', + }, +}) +;({ + '.group:nth-of-type(even) &': { + display: 'block', + }, +}) +;({ + '.group:nth-of-type(odd) &': { + display: 'block', + }, +}) // Not versions of the above +// Positional + +;({ + '.group:not(:first-child) &': { + display: 'block', + }, +}) +;({ + '.group:not(:last-child) &': { + display: 'block', + }, +}) +;({ + '.group:not(:only-child) &': { + display: 'block', + }, +}) +;({ + '.group:not(:nth-child(odd)) &': { + display: 'block', + }, +}) +;({ + '.group:not(:nth-child(even)) &': { + display: 'block', + }, +}) +;({ + '.group:not(:first-of-type) &': { + display: 'block', + }, +}) +;({ + '.group:not(:last-of-type) &': { + display: 'block', + }, +}) +;({ + '.group:not(:only-of-type) &': { + display: 'block', + }, +}) // State + +;({ + '.group:not(:visited) &': { + display: 'block', + }, +}) +;({ + '.group:not(:target) &': { + display: 'block', + }, +}) +;({ + '.group:not([open]) &': { + display: 'block', + }, +}) // Forms + +;({ + '.group:not(:default) &': { + display: 'block', + }, +}) +;({ + '.group:not(:checked) &': { + display: 'block', + }, +}) +;({ + '.group:not(:indeterminate) &': { + display: 'block', + }, +}) +;({ + '.group:not(:placeholder-shown) &': { + display: 'block', + }, +}) +;({ + '.group:not(:autofill) &': { + display: 'block', + }, +}) +;({ + '.group:not(:optional) &': { + display: 'block', + }, +}) +;({ + '.group:not(:required) &': { + display: 'block', + }, +}) +;({ + '.group:not(:valid) &': { + display: 'block', + }, +}) +;({ + '.group:not(:invalid) &': { + display: 'block', + }, +}) +;({ + '.group:not(:in-range) &': { + display: 'block', + }, +}) +;({ + '.group:not(:out-of-range) &': { + display: 'block', + }, +}) +;({ + '.group:not(:read-only) &': { + display: 'block', + }, +}) // Content + +;({ + '.group:not(:empty) &': { + display: 'block', + }, +}) // Interactive + +;({ + '.group:not(:focus-within) &': { + display: 'block', + }, +}) +;({ + '.group:not(:hover) &': { + display: 'block', + }, +}) +;({ + '.group:not(:focus) &': { + display: 'block', + }, +}) +;({ + '.group:not(:focus-visible) &': { + display: 'block', + }, +}) +;({ + '.group:not(:active) &': { + display: 'block', + }, +}) +;({ + '.group:not(:enabled) &': { + display: 'block', + }, +}) +;({ + '.group:not(:disabled) &': { + display: 'block', + }, +}) // Twin custom + +;({ + '.group:not(*) &': { + display: 'block', + }, +}) +;({ + '.group:not(> *) &': { + display: 'block', + }, +}) +;({ + '.group:not(~ *) &': { + display: 'block', + }, +}) +;({ + '.group:not(:hover) &, .group:not(:focus) &': { + display: 'block', + }, +}) +;({ + '.group:not(:link) &': { + display: 'block', + }, +}) +;({ + '.group:not(:read-write) &': { + display: 'block', + }, +}) +;({ + '.group:not(svg) &': { + display: 'block', + }, +}) +;({ + '.group:not(:nth-of-type(even)) &': { + display: 'block', + }, +}) +;({ + '.group:not(:nth-of-type(odd)) &': { + display: 'block', + }, +}) + + `; exports[`twin.macro height.js: height.js 1`] = ` @@ -38694,44 +39043,115 @@ exports[`twin.macro peers.js: peers.js 1`] = ` import tw from './macro' +// Positional tw\`peer-first:block\` tw\`peer-last:block\` tw\`peer-only:block\` tw\`peer-odd:block\` +tw\`peer-even:block\` tw\`peer-first-of-type:block\` tw\`peer-last-of-type:block\` tw\`peer-only-of-type:block\` + +// State tw\`peer-visited:block\` tw\`peer-target:block\` +tw\`peer-open:block\` + +// Forms tw\`peer-default:block\` tw\`peer-checked:block\` tw\`peer-indeterminate:block\` tw\`peer-placeholder-shown:block\` -tw\`peer-not-placeholder-shown:block\` tw\`peer-autofill:block\` +tw\`peer-optional:block\` tw\`peer-required:block\` tw\`peer-valid:block\` tw\`peer-invalid:block\` tw\`peer-in-range:block\` tw\`peer-out-of-range:block\` tw\`peer-read-only:block\` + +// Content tw\`peer-empty:block\` + +// Interactive tw\`peer-focus-within:block\` tw\`peer-hover:block\` tw\`peer-focus:block\` tw\`peer-focus-visible:block\` tw\`peer-active:block\` +tw\`peer-enabled:block\` tw\`peer-disabled:block\` -tw\`peer-open:block\` + +// Twin custom +tw\`peer-all:block\` +tw\`peer-all-child:block\` +tw\`peer-sibling:block\` +tw\`peer-hocus:block\` +tw\`peer-link:block\` +tw\`peer-read-write:block\` +tw\`peer-svg:block\` +tw\`peer-even-of-type:block\` +tw\`peer-odd-of-type:block\` + +// Not versions of the above + +// Positional +tw\`peer-not-first:block\` +tw\`peer-not-last:block\` +tw\`peer-not-only:block\` +tw\`peer-not-odd:block\` +tw\`peer-not-even:block\` +tw\`peer-not-first-of-type:block\` +tw\`peer-not-last-of-type:block\` +tw\`peer-not-only-of-type:block\` + +// State +tw\`peer-not-visited:block\` +tw\`peer-not-target:block\` tw\`peer-not-open:block\` -tw\`peer-focus:peer-hover:block\` -tw\`peer-disabled:peer-focus:peer-hover:first:block\` -tw\`first:peer-focus:peer-hover:block\` -tw\`peer-focus:first:peer-hover:peer-active:block\` +// Forms +tw\`peer-not-default:block\` +tw\`peer-not-checked:block\` +tw\`peer-not-indeterminate:block\` +tw\`peer-not-placeholder-shown:block\` +tw\`peer-not-autofill:block\` +tw\`peer-not-optional:block\` +tw\`peer-not-required:block\` +tw\`peer-not-valid:block\` +tw\`peer-not-invalid:block\` +tw\`peer-not-in-range:block\` +tw\`peer-not-out-of-range:block\` +tw\`peer-not-read-only:block\` + +// Content +tw\`peer-not-empty:block\` + +// Interactive +tw\`peer-not-focus-within:block\` +tw\`peer-not-hover:block\` +tw\`peer-not-focus:block\` +tw\`peer-not-focus-visible:block\` +tw\`peer-not-active:block\` +tw\`peer-not-enabled:block\` +tw\`peer-not-disabled:block\` + +// Twin custom +tw\`peer-not-all:block\` +tw\`peer-not-all-child:block\` +tw\`peer-not-sibling:block\` +tw\`peer-not-hocus:block\` +tw\`peer-not-link:block\` +tw\`peer-not-read-write:block\` +tw\`peer-not-svg:block\` +tw\`peer-not-even-of-type:block\` +tw\`peer-not-odd-of-type:block\` ↓ ↓ ↓ ↓ ↓ ↓ +// Positional ;({ '.peer:first-child ~ &': { display: 'block', @@ -38752,6 +39172,11 @@ tw\`peer-focus:first:peer-hover:peer-active:block\` display: 'block', }, }) +;({ + '.peer:nth-child(even) ~ &': { + display: 'block', + }, +}) ;({ '.peer:first-of-type ~ &': { display: 'block', @@ -38766,7 +39191,8 @@ tw\`peer-focus:first:peer-hover:peer-active:block\` '.peer:only-of-type ~ &': { display: 'block', }, -}) +}) // State + ;({ '.peer:visited ~ &': { display: 'block', @@ -38777,6 +39203,12 @@ tw\`peer-focus:first:peer-hover:peer-active:block\` display: 'block', }, }) +;({ + '.peer[open] ~ &': { + display: 'block', + }, +}) // Forms + ;({ '.peer:default ~ &': { display: 'block', @@ -38798,12 +39230,12 @@ tw\`peer-focus:first:peer-hover:peer-active:block\` }, }) ;({ - '.peer:not(:placeholder-shown) ~ &': { + '.peer:autofill ~ &': { display: 'block', }, }) ;({ - '.peer:autofill ~ &': { + '.peer:optional ~ &': { display: 'block', }, }) @@ -38836,12 +39268,14 @@ tw\`peer-focus:first:peer-hover:peer-active:block\` '.peer:read-only ~ &': { display: 'block', }, -}) +}) // Content + ;({ '.peer:empty ~ &': { display: 'block', }, -}) +}) // Interactive + ;({ '.peer:focus-within ~ &': { display: 'block', @@ -38867,47 +39301,267 @@ tw\`peer-focus:first:peer-hover:peer-active:block\` display: 'block', }, }) +;({ + '.peer:enabled ~ &': { + display: 'block', + }, +}) ;({ '.peer:disabled ~ &': { display: 'block', }, +}) // Twin custom + +;({ + '.peer * ~ &': { + display: 'block', + }, }) ;({ - '.peer:open ~ &': { + '.peer > * ~ &': { display: 'block', }, }) ;({ - '.peer:not(:open) ~ &': { + '.peer ~ * ~ &': { display: 'block', }, }) ;({ - '.peer:focus:hover ~ &': { + '.peer:hover ~ &, .peer:focus ~ &': { display: 'block', }, }) ;({ - '.peer:disabled:focus:hover ~ &': { - ':first-child': { - display: 'block', - }, + '.peer:link ~ &': { + display: 'block', }, }) ;({ - ':first-child': { - '.peer:focus:hover ~ &': { - display: 'block', - }, + '.peer:read-write ~ &': { + display: 'block', }, }) ;({ - '.peer:focus ~ &': { - ':first-child': { - '.peer:hover:active ~ &': { - display: 'block', - }, - }, + '.peer svg ~ &': { + display: 'block', + }, +}) +;({ + '.peer:nth-of-type(even) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:nth-of-type(odd) ~ &': { + display: 'block', + }, +}) // Not versions of the above +// Positional + +;({ + '.peer:not(:first-child) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:last-child) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:only-child) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:nth-child(odd)) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:nth-child(even)) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:first-of-type) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:last-of-type) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:only-of-type) ~ &': { + display: 'block', + }, +}) // State + +;({ + '.peer:not(:visited) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:target) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not([open]) ~ &': { + display: 'block', + }, +}) // Forms + +;({ + '.peer:not(:default) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:checked) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:indeterminate) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:placeholder-shown) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:autofill) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:optional) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:required) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:valid) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:invalid) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:in-range) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:out-of-range) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:read-only) ~ &': { + display: 'block', + }, +}) // Content + +;({ + '.peer:not(:empty) ~ &': { + display: 'block', + }, +}) // Interactive + +;({ + '.peer:not(:focus-within) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:hover) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:focus) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:focus-visible) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:active) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:enabled) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:disabled) ~ &': { + display: 'block', + }, +}) // Twin custom + +;({ + '.peer:not(*) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(> *) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(~ *) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:hover) ~ &, .peer:not(:focus) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:link) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:read-write) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(svg) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:nth-of-type(even)) ~ &': { + display: 'block', + }, +}) +;({ + '.peer:not(:nth-of-type(odd)) ~ &': { + display: 'block', }, }) @@ -53815,6 +54469,188 @@ globalStyles ) +`; + +exports[`twin.macro pluginExamples.js: pluginExamples.js 1`] = ` + +import tw, { globalStyles } from './macro' + +tw\`content-auto\` +tw\`content-hidden\` +tw\`content-visible\` + +tw\`tab-1\` +tw\`tab-2\` +tw\`tab-4\` +tw\`tab-8\` + +tw\`btn\` +tw\`btn-blue\` +tw\`btn-red\` +tw\`btn btn-blue btn-red\` + +globalStyles + +tw\`test-1:block\` +tw\`test-2:block\` +tw\`test-3:block\` +tw\`test-4:block\` + + ↓ ↓ ↓ ↓ ↓ ↓ + +;({ + contentVisibility: 'auto', +}) +;({ + contentVisibility: 'hidden', +}) +;({ + contentVisibility: 'visible', +}) +;({ + tabSizeTest: '1', +}) +;({ + tabSizeTest: '2', +}) +;({ + tabSizeTest: '4', +}) +;({ + tabSizeTest: '8', +}) +;({ + padding: '.5rem 1rem', + borderRadius: '.25rem', + fontWeight: '600', +}) +;({ + backgroundColor: '#3490dc', + color: '#fff', + ':hover': { + backgroundColor: '#2779bd', + }, +}) +;({ + backgroundColor: '#e3342f', + color: '#fff', + ':hover': { + backgroundColor: '#cc1f1a', + }, +}) +;({ + padding: '.5rem 1rem', + borderRadius: '.25rem', + fontWeight: '600', + backgroundColor: '#e3342f', + color: '#fff', + ':hover': { + backgroundColor: '#cc1f1a', + }, +}) +;({ + '@keyframes spin': { + to: { + transform: 'rotate(360deg)', + }, + }, + '@keyframes ping': { + '75%, 100%': { + transform: 'scale(2)', + opacity: '0', + }, + }, + '@keyframes pulse': { + '50%': { + opacity: '.5', + }, + }, + '@keyframes bounce': { + '0%, 100%': { + transform: 'translateY(-25%)', + animationTimingFunction: 'cubic-bezier(0.8,0,1,1)', + }, + '50%': { + transform: 'none', + animationTimingFunction: 'cubic-bezier(0,0,0.2,1)', + }, + }, + '*, ::before, ::after': { + '--tw-translate-x': '0', + '--tw-translate-y': '0', + '--tw-rotate': '0', + '--tw-skew-x': '0', + '--tw-skew-y': '0', + '--tw-scale-x': '1', + '--tw-scale-y': '1', + '--tw-pan-x': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-pan-y': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-pinch-zoom': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-scroll-snap-strictness': 'proximity', + '--tw-ordinal': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-slashed-zero': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-numeric-figure': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-numeric-spacing': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-numeric-fraction': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-ring-offset-width': '0px', + '--tw-ring-offset-color': '#fff', + '--tw-ring-color': 'rgb(59 130 246 / 0.5)', + '--tw-ring-offset-shadow': '0 0 #0000', + '--tw-ring-shadow': '0 0 #0000', + '--tw-shadow': '0 0 #0000', + '--tw-shadow-colored': '0 0 #0000', + '--tw-blur': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-brightness': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-contrast': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-grayscale': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-invert': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-saturate': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-sepia': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-drop-shadow': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-grayscale': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-hue-rotate': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-invert': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-opacity': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-saturate': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-backdrop-sepia': 'var(--tw-empty,/*!*/ /*!*/)', + }, + h1: { + fontSize: '1.5rem', + }, + h2: { + fontSize: '1.25rem', + }, + h3: { + fontSize: '1.125rem', + }, +}) +;({ + ':test1': { + display: 'block', + }, +}) +;({ + ':hover, :focus': { + display: 'block', + }, +}) +;({ + '@supports (display: grid)': { + display: 'block', + }, +}) +;({ + 'html.dark &.something': { + display: 'block', + }, +}) + + `; exports[`twin.macro pluginForms.js: pluginForms.js 1`] = ` @@ -55986,8 +56822,8 @@ import tw from './macro' /> // group ;
;
;
;
;
;
=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, "node_modules/postcss-js": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", @@ -10692,6 +10708,22 @@ "node": ">=0.10.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -11945,14 +11977,14 @@ "dev": true }, "node_modules/tailwindcss": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.24.tgz", - "integrity": "sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.4.tgz", + "integrity": "sha512-NrxbFV4tYsga/hpWbRyUfIaBrNMXDxx5BsHgBS4v5tlyjf+sDsgBg5m9OxjrXIqAS/uR9kicxLKP+bEHI7BSeQ==", "dependencies": { - "arg": "^5.0.1", + "arg": "^5.0.2", "chokidar": "^3.5.3", "color-name": "^1.1.4", - "detective": "^5.2.0", + "detective": "^5.2.1", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.11", @@ -11962,7 +11994,8 @@ "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.12", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", "postcss-js": "^4.0.0", "postcss-load-config": "^3.1.4", "postcss-nested": "5.0.6", @@ -15329,9 +15362,9 @@ } }, "arg": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", - "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "argparse": { "version": "2.0.1", @@ -16594,7 +16627,7 @@ "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" }, "delayed-stream": { "version": "1.0.0", @@ -16609,13 +16642,13 @@ "dev": true }, "detective": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", - "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", "requires": { - "acorn-node": "^1.6.1", + "acorn-node": "^1.8.2", "defined": "^1.0.0", - "minimist": "^1.1.1" + "minimist": "^1.2.6" } }, "didyoumean": { @@ -19696,9 +19729,9 @@ "dev": true }, "nanoid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", - "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==" + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "natural-compare": { "version": "1.4.0", @@ -20181,11 +20214,11 @@ "dev": true }, "postcss": { - "version": "8.4.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", - "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", "requires": { - "nanoid": "^3.3.1", + "nanoid": "^3.3.4", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -20249,6 +20282,16 @@ "dev": true, "requires": {} }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, "postcss-js": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", @@ -20715,6 +20758,21 @@ "loose-envify": "^1.1.0" } }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + } + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -21683,14 +21741,14 @@ "dev": true }, "tailwindcss": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.24.tgz", - "integrity": "sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.4.tgz", + "integrity": "sha512-NrxbFV4tYsga/hpWbRyUfIaBrNMXDxx5BsHgBS4v5tlyjf+sDsgBg5m9OxjrXIqAS/uR9kicxLKP+bEHI7BSeQ==", "requires": { - "arg": "^5.0.1", + "arg": "^5.0.2", "chokidar": "^3.5.3", "color-name": "^1.1.4", - "detective": "^5.2.0", + "detective": "^5.2.1", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.11", @@ -21700,7 +21758,8 @@ "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.12", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", "postcss-js": "^4.0.0", "postcss-load-config": "^3.1.4", "postcss-nested": "5.0.6", diff --git a/package.json b/package.json index 24698dc7..80d5c972 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "lodash.merge": "^4.6.2", "postcss": "^8.4.12", "string-similarity": "^4.0.4", - "tailwindcss": "^3.0.24", + "tailwindcss": "^3.1.4", "timsort": "^0.3.0" }, "devDependencies": { diff --git a/src/config/corePlugins.js b/src/config/corePlugins.js index e367058b..4240dd15 100644 --- a/src/config/corePlugins.js +++ b/src/config/corePlugins.js @@ -1,3 +1,7 @@ +import { toArray } from '../utils' +import { normalizeScreens } from 'tailwindcss/lib/util/normalizeScreens' +import buildMediaQuery from 'tailwindcss/lib/util/buildMediaQuery' + // https://tailwindcss.com/docs/font-variant-numeric // This feature uses var+comment hacks to get around property stripping: // https://github.com/tailwindlabs/tailwindcss.com/issues/522#issuecomment-687667238 @@ -40,7 +44,196 @@ const cssBackdropFilterValue = [ const cssTouchActionValue = 'var(--tw-pan-x) var(--tw-pan-y) var(--tw-pinch-zoom)' -export default { +const variantPlugins = { + pseudoElementVariants({ addVariant }) { + addVariant('first-letter', '&::first-letter') + addVariant('first-line', '&::first-line') + + addVariant('marker', ['& *::marker', '&::marker']) + addVariant('selection', ['& *::selection', '&::selection']) + + addVariant('file', '&::file-selector-button') + + addVariant('placeholder', '&::placeholder') + + addVariant('backdrop', '&::backdrop') + + addVariant('before', '&:before') // content is auto added in getStyleData@addContentClass + addVariant('after', '&:after') // content is auto added in getStyleData@addContentClass + }, + + pseudoClassVariants({ addVariant }) { + const pseudoVariants = [ + // Positional + ['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', + + // State + 'visited', // uses avoidAlpha feature + 'target', + ['open', '&[open]'], + + // Forms + 'default', + 'checked', + 'indeterminate', + 'placeholder-shown', + 'autofill', + 'optional', + 'required', + 'valid', + 'invalid', + 'in-range', + 'out-of-range', + 'read-only', + + // Content + 'empty', + + // Interactive + 'focus-within', + 'hover', + 'focus', + 'focus-visible', + 'active', + 'enabled', + 'disabled', + + // Twin custom + ['all', '& *'], + ['all-child', '& > *'], + ['sibling', '& ~ *'], + ['hocus', ['&:hover', '&:focus']], + 'link', + 'read-write', + ['svg', '& svg'], + ['even-of-type', '&:nth-of-type(even)'], + ['odd-of-type', '&:nth-of-type(odd)'], + ].flatMap(v => { + let [name, selector] = toArray(v) + selector = selector || `&:${name}` + const config = [name, selector] + + // Create a :not() version of the selectors above + const notConfig = [ + `not-${name}`, + toArray(selector).map(s => `&:not(${stripAmpersands(s)})`), + ] + + return [config, notConfig] + }) + + for (const [name, selector] of pseudoVariants) { + addVariant(name, toArray(selector).join(',')) + } + + for (const [name, selector] of pseudoVariants) { + const groupSelector = toArray(selector).map(s => + s.replace(/&(.+)/g, ':merge(.group){{$1}} &') + ) + addVariant(`group-${name}`, groupSelector) + } + + for (const [name, selector] of pseudoVariants) { + const peerSelector = toArray(selector).map(s => + s.replace(/&(.+)/g, ':merge(.peer){{$1}} ~ &') + ) + addVariant(`peer-${name}`, peerSelector) + } + }, + + directionVariants({ addVariant }) { + addVariant('ltr', '[dir="ltr"] &') + addVariant('rtl', '[dir="rtl"] &') + }, + + reducedMotionVariants({ addVariant }) { + addVariant('motion-safe', '@media (prefers-reduced-motion: no-preference)') + addVariant('motion-reduce', '@media (prefers-reduced-motion: reduce)') + }, + + darkVariants({ config, addVariant }) { + // eslint-disable-next-line unicorn/prefer-spread + let [mode, className = '.dark'] = [].concat(config('darkMode', 'media')) + + if (mode === false) { + mode = 'media' + console.warn('darkmode-false', [ + 'The `darkMode` option in your tailwind config is set to `false`, which now behaves the same as `media`.', + 'Change `darkMode` to `media` or remove it entirely.', + 'https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration', + ]) + } + + if (mode === 'class') { + addVariant('dark', `${className} &`) + } else if (mode === 'media') { + addVariant('dark', '@media (prefers-color-scheme: dark)') + } + }, + + // Twin feature + lightVariants({ config, addVariant }) { + // eslint-disable-next-line unicorn/prefer-spread + const [mode, className = '.light'] = [].concat(config('lightMode', 'media')) + + if (mode === 'class') { + addVariant('light', `${className} &`) + } else if (mode === 'media') { + addVariant('light', '@media (prefers-color-scheme: light)') + } + }, + + printVariants({ addVariant }) { + addVariant('print', '@media print') + addVariant('screen', '@media screen') // Twin only: Extra media type + }, + + screenVariants({ theme, addVariant }) { + for (const screen of normalizeScreens(theme('screens'))) { + const query = buildMediaQuery(screen) + addVariant(screen.name, `@media ${query}`) + } + }, + + orientationVariants({ addVariant }) { + addVariant('portrait', '@media (orientation: portrait)') + addVariant('landscape', '@media (orientation: landscape)') + }, + + prefersContrastVariants({ addVariant }) { + addVariant('contrast-more', '@media (prefers-contrast: more)') + addVariant('contrast-less', '@media (prefers-contrast: less)') + }, + + extraCursorVariants({ addVariant }) { + // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer + addVariant('any-pointer-none', '@media (any-pointer: none)') + addVariant('any-pointer-fine', '@media (any-pointer: fine)') + addVariant('any-pointer-coarse', '@media (any-pointer: coarse)') + + // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer + addVariant('pointer-none', '@media (pointer: none)') + addVariant('pointer-fine', '@media (pointer: fine)') + addVariant('pointer-coarse', '@media (pointer: coarse)') + + // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-hover + addVariant('any-hover-none', '@media (any-hover: none)') + addVariant('any-hover', '@media (any-hover: hover)') + + // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover + addVariant('can-hover', '@media (hover: hover)') + addVariant('cant-hover', '@media (hover: none)') + }, +} + +const corePlugins = { // https://tailwindcss.com/docs/container container: { output({ theme, pieces }) { @@ -325,8 +518,6 @@ export default { 'border-collapse': { output: { borderCollapse: 'collapse' } }, 'border-separate': { output: { borderCollapse: 'separate' } }, - // TODO: Border spacing - // https://tailwindcss.com/docs/transform-origin origin: { property: 'transformOrigin', config: 'transformOrigin' }, @@ -1848,3 +2039,9 @@ export default { { output: { content: '""' } }, // Deprecated (keep last in array here) ], } + +function stripAmpersands(string) { + return typeof string === 'string' ? string.replace(/&/g, '').trim() : string +} + +export { corePlugins, variantPlugins } diff --git a/src/config/index.js b/src/config/index.js deleted file mode 100644 index a59dc9b3..00000000 --- a/src/config/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as corePlugins } from './corePlugins' -export { default as variantConfig } from './variantConfig' diff --git a/src/config/variantConfig.js b/src/config/variantConfig.js deleted file mode 100644 index e91e6865..00000000 --- a/src/config/variantConfig.js +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Pseudo-classes (Variants) - * In Twin, these are always available on just about any class - * - * See MDN web docs for more information - * https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes - */ - -const variantConfig = ({ - variantDarkMode, - variantLightMode, - prefixDarkLightModeClass, - createPeer, -}) => ({ - // Before/after pseudo elements - // Usage: tw`before:(block w-10 h-10 bg-black)` - before: ':before', - after: ':after', - - // Interactive links/buttons - hover: ':hover', - focus: ':focus', - active: ':active', - visited: ':visited', - hocus: ':hover, :focus', - link: ':link', - target: ':target', - 'focus-visible': ':focus-visible', - 'focus-within': ':focus-within', - - // Form elements - file: '::file-selector-button', - - // Form element states - autofill: ':autofill', - disabled: ':disabled', - checked: ':checked', - 'not-checked': ':not(:checked)', - default: ':default', - enabled: ':enabled', - indeterminate: ':indeterminate', - 'in-range': ':in-range', - invalid: ':invalid', - valid: ':valid', - optional: ':optional', - 'out-of-range': ':out-of-range', - required: ':required', - 'placeholder-shown': ':placeholder-shown', - 'not-placeholder-shown': ':not(:placeholder-shown)', - placeholder: '::placeholder', - 'read-only': ':read-only', - 'read-write': ':read-write', - open: ':open', - 'not-open': ':not(:open)', - - // Child selectors - 'not-disabled': ':not(:disabled)', - 'first-of-type': ':first-of-type', - 'not-first-of-type': ':not(:first-of-type)', - 'last-of-type': ':last-of-type', - 'not-last-of-type': ':not(:last-of-type)', - 'first-letter': '::first-letter', - 'first-line': '::first-line', - first: ':first-child', - 'not-first': ':not(:first-child)', - last: ':last-child', - 'not-last': ':not(:last-child)', - only: ':only-child', - 'not-only': ':not(:only-child)', - 'only-of-type': ':only-of-type', - 'not-only-of-type': ':not(:only-of-type)', - even: ':nth-child(even)', - odd: ':nth-child(odd)', - 'even-of-type': ':nth-of-type(even)', - 'odd-of-type': ':nth-of-type(odd)', - svg: 'svg', - all: '*', - 'all-child': '> *', - sibling: '~ *', - - // Content - empty: ':empty', - - // Group states - // You'll need to add className="group" to an ancestor to make these work - // https://github.com/ben-rogerson/twin.macro/blob/master/docs/group.md - 'group-hocus': variantData => - prefixDarkLightModeClass('.group:hover &, .group:focus &', variantData), - 'group-first': variantData => - prefixDarkLightModeClass('.group:first-child &', variantData), - 'group-last': variantData => - prefixDarkLightModeClass('.group:last-child &', variantData), - 'group-only': variantData => - prefixDarkLightModeClass('.group:only-child &', variantData), - 'group-even': variantData => - prefixDarkLightModeClass('.group:nth-child(even) &', variantData), - 'group-odd': variantData => - prefixDarkLightModeClass('.group:nth-child(odd) &', variantData), - 'group-first-of-type': variantData => - prefixDarkLightModeClass('.group:first-of-type &', variantData), - 'group-last-of-type': variantData => - prefixDarkLightModeClass('.group:last-of-type &', variantData), - 'group-only-of-type': variantData => - prefixDarkLightModeClass('.group:not(:first-of-type) &', variantData), - 'group-hover': variantData => - prefixDarkLightModeClass('.group:hover &', variantData), - 'group-focus': variantData => - prefixDarkLightModeClass('.group:focus &', variantData), - 'group-disabled': variantData => - prefixDarkLightModeClass('.group:disabled &', variantData), - 'group-active': variantData => - prefixDarkLightModeClass('.group:active &', variantData), - 'group-target': variantData => - prefixDarkLightModeClass('.group:target &', variantData), - 'group-visited': variantData => - prefixDarkLightModeClass('.group:visited &', variantData), - 'group-default': variantData => - prefixDarkLightModeClass('.group:default &', variantData), - 'group-checked': variantData => - prefixDarkLightModeClass('.group:checked &', variantData), - 'group-indeterminate': variantData => - prefixDarkLightModeClass('.group:indeterminate &', variantData), - 'group-placeholder-shown': variantData => - prefixDarkLightModeClass('.group:placeholder-shown &', variantData), - 'group-autofill': variantData => - prefixDarkLightModeClass('.group:autofill &', variantData), - 'group-focus-within': variantData => - prefixDarkLightModeClass('.group:focus-within &', variantData), - 'group-focus-visible': variantData => - prefixDarkLightModeClass('.group:focus-visible &', variantData), - 'group-required': variantData => - prefixDarkLightModeClass('.group:required &', variantData), - 'group-valid': variantData => - prefixDarkLightModeClass('.group:valid &', variantData), - 'group-invalid': variantData => - prefixDarkLightModeClass('.group:invalid &', variantData), - 'group-in-range': variantData => - prefixDarkLightModeClass('.group:in-range &', variantData), - 'group-out-of-range': variantData => - prefixDarkLightModeClass('.group:out-of-range &', variantData), - 'group-read-only': variantData => - prefixDarkLightModeClass('.group:read-only &', variantData), - 'group-empty': variantData => - prefixDarkLightModeClass('.group:empty &', variantData), - 'group-open': variantData => - prefixDarkLightModeClass('.group:open &', variantData), - 'group-not-open': variantData => - prefixDarkLightModeClass('.group:not(:open) &', variantData), - - // Media types - print: '@media print', - screen: '@media screen', - - // Direction variants - rtl: '[dir="rtl"] &', - ltr: '[dir="ltr"] &', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion - 'motion-safe': '@media (prefers-reduced-motion: no-preference)', - 'motion-reduce': '@media (prefers-reduced-motion: reduce)', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer - 'any-pointer-none': '@media (any-pointer: none)', - 'any-pointer-fine': '@media (any-pointer: fine)', - 'any-pointer-coarse': '@media (any-pointer: coarse)', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer - 'pointer-none': '@media (pointer: none)', - 'pointer-fine': '@media (pointer: fine)', - 'pointer-coarse': '@media (pointer: coarse)', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-hover - 'any-hover-none': '@media (any-hover: none)', - 'any-hover': '@media (any-hover: hover)', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover - 'can-hover': '@media (hover: hover)', - 'cant-hover': '@media (hover: none)', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation - landscape: '@media (orientation: landscape)', - portrait: '@media (orientation: portrait)', - - // Dark mode / Light mode - dark: variantDarkMode, - light: variantLightMode, - - // Peer variants - 'peer-first': createPeer('first-child'), - 'peer-last': createPeer('last-child'), - 'peer-only': createPeer('only-child'), - 'peer-even': createPeer('nth-child(even)'), - 'peer-odd': createPeer('nth-child(odd)'), - 'peer-first-of-type': createPeer('first-of-type'), - 'peer-last-of-type': createPeer('last-of-type'), - 'peer-only-of-type': createPeer('only-of-type'), - 'peer-hover': createPeer('hover'), - 'peer-focus': createPeer('focus'), - 'peer-disabled': createPeer('disabled'), - 'peer-active': createPeer('active'), - 'peer-target': createPeer('target'), - 'peer-visited': createPeer('visited'), - 'peer-default': createPeer('default'), - 'peer-checked': createPeer('checked'), - 'peer-indeterminate': createPeer('indeterminate'), - 'peer-placeholder-shown': createPeer('placeholder-shown'), - 'peer-not-placeholder-shown': createPeer('not(:placeholder-shown)'), - 'peer-autofill': createPeer('autofill'), - 'peer-focus-within': createPeer('focus-within'), - 'peer-focus-visible': createPeer('focus-visible'), - 'peer-required': createPeer('required'), - 'peer-valid': createPeer('valid'), - 'peer-invalid': createPeer('invalid'), - 'peer-in-range': createPeer('in-range'), - 'peer-out-of-range': createPeer('out-of-range'), - 'peer-read-only': createPeer('read-only'), - 'peer-empty': createPeer('empty'), - 'peer-open': createPeer('open'), - 'peer-not-open': createPeer('not(:open)'), - - // Selection - selection: '::selection', - - // Lists - marker: '::marker, *::marker', -}) - -export default variantConfig diff --git a/src/configHelpers.js b/src/configHelpers.js index 9cccbcb8..32122e0d 100644 --- a/src/configHelpers.js +++ b/src/configHelpers.js @@ -3,7 +3,7 @@ import { existsSync } from 'fs' import resolveTailwindConfig from 'tailwindcss/lib/util/resolveConfig' import defaultTailwindConfig from 'tailwindcss/stubs/defaultConfig.stub' import { configTwinValidators, configDefaultsTwin } from './config/twinConfig' -import corePlugins from './config/corePlugins' +import { corePlugins } from './config/corePlugins' import flatMap from 'lodash.flatmap' import { logGeneralError } from './logging' import { throwIf, get, toArray, getFirstValue } from './utils' @@ -28,7 +28,7 @@ const silenceContentWarning = config => ({ ...config, }) -const getConfigTailwindProperties = (state, config) => { +const getTailwindConfigProperties = (state, config) => { const sourceRoot = state.file.opts.sourceRoot || '.' const configFile = config && config.config @@ -53,13 +53,13 @@ const getConfigTailwindProperties = (state, config) => { : defaultTailwindConfig const configUser = silenceContentWarning(configSelected) - const configTailwind = resolveTailwindConfig([...getAllConfigs(configUser)]) + const tailwindConfig = resolveTailwindConfig([...getAllConfigs(configUser)]) - throwIf(!configTailwind, () => + throwIf(!tailwindConfig, () => logGeneralError(`Couldn’t find the Tailwind config.\nLooked in ${config}`) ) - return { configExists, configTailwind, configPath } + return { configExists, tailwindConfig, configPath } } const checkExists = (fileName, sourceRoot) => { @@ -151,7 +151,7 @@ const supportsArbitraryValues = coreConfigValue => ) export { - getConfigTailwindProperties, + getTailwindConfigProperties, getStitchesPath, getConfigTwinValidated, getCoercedTypesByProperty, diff --git a/src/darkLightMode.js b/src/darkLightMode.js deleted file mode 100644 index 62f91af2..00000000 --- a/src/darkLightMode.js +++ /dev/null @@ -1,68 +0,0 @@ -const variantDarkMode = ({ hasGroupVariant, config, errorCustom }) => { - const styles = - { - // Media strategy: The default when you prepend with dark, tw`dark:block` - media: '@media (prefers-color-scheme: dark)', - // Class strategy: In your tailwind.config.js, add `{ dark: 'class' } - // then add a `className="dark"` on a parent element. - class: !hasGroupVariant && '.dark &', - }[config('darkMode') || 'media'] || null - - if (!styles && !hasGroupVariant) { - errorCustom( - "The `darkMode` config option must be either `{ darkMode: 'media' }` (default) or `{ darkMode: 'class' }`" - ) - } - - return styles -} - -const variantLightMode = ({ hasGroupVariant, config, errorCustom }) => { - const styles = - { - // Media strategy: The default when you prepend with light, tw`light:block` - media: '@media (prefers-color-scheme: light)', - // Class strategy: In your tailwind.config.js, add `{ light: 'class' } - // then add a `className="light"` on a parent element. - class: !hasGroupVariant && '.light &', - }[config('lightMode') || config('darkMode') || 'media'] || null - - if (!styles && !hasGroupVariant) { - if (config('lightMode')) { - errorCustom( - "The `lightMode` config option must be either `{ lightMode: 'media' }` (default) or `{ lightMode: 'class' }`" - ) - } - - errorCustom( - "The `darkMode` config option must be either `{ darkMode: 'media' }` (default) or `{ darkMode: 'class' }`" - ) - } - - return styles -} - -const prefixDarkLightModeClass = ( - className, - { hasDarkVariant, hasLightVariant, config } -) => { - const themeVariant = - (hasDarkVariant && config('darkMode') === 'class' && ['dark ', 'dark']) || - (hasLightVariant && - (config('lightMode') === 'class' || config('darkMode') === 'class') && [ - 'light ', - 'light', - ]) - if (!themeVariant) return className - - return themeVariant - .map(v => - className - .split(', ') - .map(cn => `.${v}${cn}`) - .join(', ') - ) - .join(', ') -} - -export { variantDarkMode, variantLightMode, prefixDarkLightModeClass } diff --git a/src/getProperties.js b/src/getProperties.js index 0c86f478..8cd94492 100644 --- a/src/getProperties.js +++ b/src/getProperties.js @@ -1,4 +1,4 @@ -import { corePlugins } from './config' +import { corePlugins } from './config/corePlugins' import { isShortCss as getIsShortCss, toArray, diff --git a/src/getStyleData.js b/src/getStyleData.js index 45e2a537..b64882d0 100644 --- a/src/getStyleData.js +++ b/src/getStyleData.js @@ -59,7 +59,7 @@ const formatTasks = [ handleVariantGroups, // Move some properties to the front of the list so they work as expected ...Object.values(ordering), - // Add a missing content class for after:/before: variants + // Add a missing content class for before:/after: variants addContentClass, ] @@ -144,7 +144,6 @@ export default (classes, args) => { if (hasUserPlugins) { style = applyTransforms({ - type, pieces, style: styleHandler.userPlugin(styleContext), }) @@ -164,15 +163,13 @@ export default (classes, args) => { style || applyTransforms({ pieces, style: styleHandler[type](styleContext) }) - const result = deepMerge( - results, - addVariants({ results, style, pieces, state }) - ) + deepMerge(results, addVariants({ results, style, pieces, state })) state.debug(debugSuccess(formatProp(classNameRaw), style)) classesMatched.push(classNameRaw) - return result + + return results }, {}) return { diff --git a/src/handlers/arbitraryCss.js b/src/handlers/arbitraryCss.js index c6fb8318..8f18c028 100644 --- a/src/handlers/arbitraryCss.js +++ b/src/handlers/arbitraryCss.js @@ -1,5 +1,5 @@ import stringSimilarity from 'string-similarity' -import { corePlugins } from './../config' +import { corePlugins } from './../config/corePlugins' import { getTypeCoerced, getCoercedValueFromTypeMap } from './../coerced' import { throwIf, diff --git a/src/logging.js b/src/logging.js index 0ecfb9d8..ab216c60 100644 --- a/src/logging.js +++ b/src/logging.js @@ -1,13 +1,15 @@ import stringSimilarity from 'string-similarity' import chalk from 'chalk' -import { corePlugins } from './config' +import { corePlugins } from './config/corePlugins' import { + get, getTheme, isEmpty, throwIf, toArray, isObject, splitOnFirst, + stripMergePlaceholders, } from './utils' const color = { @@ -23,32 +25,47 @@ const color = { const spaced = string => `\n\n${string}\n` const warning = string => color.error(`✕ ${string}`) -const inOutPlugins = (input, output, layer) => - `${layer} ${color.highlight2('→')} ${input} ${color.highlight2( - JSON.stringify(output) - )}` - const inOut = (input, output) => `${color.success('✓')} ${input} ${color.success(JSON.stringify(output))}` -const logNoVariant = (variant, validVariants) => - spaced( - `${warning( - `The variant ${color.errorLight(`${variant}:`)} was not found` - )}\n\n${Object.entries(validVariants) - .map( - ([k, v]) => - `${k}\n${v - .map( - (item, index) => - `${ - v.length > 6 && index % 6 === 0 && index > 0 ? '\n' : '' - }${color.highlight(item)}:` - ) - .join(color.subdued(' / '))}` +const logNoVariant = (variant, state) => { + const screensList = Object.entries( + get(state.config, ['theme', 'screens']) || {} + ).map(([k, v]) => [k, [v]]) + const variantList = [...screensList, ...state.userPluginData.variants] + + const textNotFound = `The variant ${color.errorLight( + `${variant}:` + )} was not found` + + const suggestions = variantList + .map(([variantName, styleList]) => { + const rating = Number( + stringSimilarity.compareTwoStrings(variant, variantName) ) - .join('\n\n')}\n\nRead more at https://twinredirect.page.link/variantList` - ) + if (rating < 0.15) return + return [rating, variantName, styleList] + }) + .filter(Boolean) + .sort(([a] = [], [b] = []) => b - a) + .slice(0, 5) + .map(([, variantName, styleList] = []) => + [ + color.subdued('-'), + `${color.highlight(variantName)}:`, + color.subdued('>'), + styleList.map(s => stripMergePlaceholders(s)).join(', '), + ].join(' ') + ) + .join('\n') + + const suggestionText = + suggestions.length > 0 + ? `\n\nTry one of these variants:\n\n${suggestions}` + : '' + + return spaced(`${warning(textNotFound)}${suggestionText}`) +} const logNotAllowed = (className, error, fix) => spaced( @@ -80,12 +97,20 @@ const debugSuccess = (className, log) => inOut(className, log) const formatPluginKey = key => key.replace(/(\\|(}}))/g, '').replace(/{{/g, '.') +const inOutPlugins = (input, output, layer) => + `${layer} ${color.highlight2('→ ')} ${input} ${color.highlight2( + JSON.stringify(output) + )}` + const debugPlugins = processedPlugins => { console.log( Object.entries(processedPlugins) .map(([layer, group]) => (layer === 'variants' - ? [...group].map(([k, v]) => [k, v.join(', ')]) + ? [...group].map(([k, v]) => [ + `${k}:`, + v.map(a => stripMergePlaceholders(a)).join(', '), + ]) : Object.entries(group) ) .map(([className, styles]) => diff --git a/src/macro.js b/src/macro.js index d6ab148a..d1166da6 100644 --- a/src/macro.js +++ b/src/macro.js @@ -7,7 +7,7 @@ import { getCssAttributeData, } from './macroHelpers' import { - getConfigTailwindProperties, + getTailwindConfigProperties, getConfigTwinValidated, } from './configHelpers' import { @@ -74,7 +74,7 @@ const twinMacro = args => { state.isDev = isDev state.isProd = !isDev - const { configExists, configTailwind } = getConfigTailwindProperties( + const { configExists, tailwindConfig } = getTailwindConfigProperties( state, config ) @@ -91,14 +91,14 @@ const twinMacro = args => { const stateContext = { configExists: configExists, - config: configTailwind, + config: tailwindConfig, configTwin: configTwin, debug: debug(isDev, configTwin), globalStyles: new Map(), tailwindConfigIdentifier: generateUid('tailwindConfig', program), tailwindUtilsIdentifier: generateUid('tailwindUtils', program), userPluginData: - getUserPluginData({ config: configTailwind, configTwin }) || {}, + getUserPluginData({ tailwindConfig, configTwin, state }) || {}, styledImport: styledImport, cssImport: cssImport, styledIdentifier: null, @@ -107,11 +107,6 @@ const twinMacro = args => { state = { ...state, ...stateContext } - isDev && - Boolean(config.debugPlugins) && - state.userPluginData && - debugPlugins(state.userPluginData) - // Group traversals together for better performance program.traverse({ ImportDeclaration(path) { diff --git a/src/macro/cs.js b/src/macro/cs.js index 37f2e2ea..c6d5110e 100644 --- a/src/macro/cs.js +++ b/src/macro/cs.js @@ -48,7 +48,6 @@ const handleCsProperty = ({ path, t, state }) => { t.jsxExpressionContainer(astStyles) ) ) - // TODO: Update the naming of this function addDataTwPropToPath({ t, attributes, diff --git a/src/pieces.js b/src/pieces.js index 2019429d..263bddf6 100644 --- a/src/pieces.js +++ b/src/pieces.js @@ -1,96 +1,57 @@ import { MacroError } from 'babel-plugin-macros' -import { stringifyScreen } from './screens' import { logNoVariant, logGeneralError } from './logging' -import { variantConfig } from './config' import { get, throwIf, formatProp, isShortCss, replaceSpaceId, - toArray, + getFirstValue, } from './utils' -import { - variantDarkMode, - variantLightMode, - prefixDarkLightModeClass, -} from './darkLightMode' - -const createPeer = selector => { - const selectors = toArray(selector) - return selectors.map(s => `.peer:${s} ~ &`).join(', ') -} -const fullVariantConfig = variantConfig({ - variantDarkMode, - variantLightMode, - prefixDarkLightModeClass, - createPeer, -}) +const logVariantError = (variant, state) => { + if (variant === 'only-child') { + throw new MacroError( + logGeneralError( + 'The "only-child:" variant was deprecated in favor of "only:"' + ) + ) + } -const getVariants = ({ variants, state, ...rest }) => { - if (!variants) return [] + if (variant === 'not-only-child') { + throw new MacroError( + logGeneralError( + 'The "not-only-child:" variant was deprecated in favor of "not-only:"' + ) + ) + } - const screens = get(state.config, ['theme', 'screens']) - const screenNames = Object.keys(screens) - const { variants: variantMap } = state.userPluginData + throw new MacroError(logNoVariant(variant, state)) +} + +const getVariants = ({ variants, state, className }) => { + if (!variants) return [] const list = variants .map(variant => { - const isResponsive = screenNames && screenNames.includes(variant) - if (isResponsive) return stringifyScreen(state.config, variant) - - let foundVariant = { - ...fullVariantConfig, - ...(variantMap && - Object.fromEntries( - [...variantMap].map(([c, v]) => [c, v.join(', ')]) - )), - }[variant] - - if (!foundVariant) { - const arbitraryVariant = variant.match(/^\[(.+)]/) - if (arbitraryVariant) foundVariant = arbitraryVariant[1] - } + const arbitraryVariant = variant.match(/^\[(.+)]/) + if (arbitraryVariant) return arbitraryVariant[1] - if (!foundVariant) { - if (variant === 'only-child') { - throw new MacroError( - logGeneralError( - 'The "only-child:" variant was deprecated in favor of "only:"' - ) - ) - } - - if (variant === 'not-only-child') { - throw new MacroError( - logGeneralError( - 'The "not-only-child:" variant was deprecated in favor of "not-only:"' - ) - ) - } - - const validVariants = { - ...(variantMap && { - 'Plugin variants': [...variantMap].map(([c]) => c), - }), - ...(screenNames.length > 0 && { 'Screen breakpoints': screenNames }), - 'Built-in variants': Object.keys(fullVariantConfig), - } - throw new MacroError(logNoVariant(variant, validVariants)) - } + let [foundVariant] = getFirstValue( + [...state.userPluginData.variants], + ([name, value]) => (name === variant ? value.join(', ') : false) + ) if (typeof foundVariant === 'function') { const context = { - ...rest, + className, config: item => state.config[item] || null, - errorCustom(message) { - throw new MacroError(logGeneralError(message)) - }, } foundVariant = foundVariant(context) } + if (!foundVariant) logVariantError(variant, state) + return foundVariant }) .filter(Boolean) @@ -115,41 +76,19 @@ const splitVariants = ({ classNameRaw, state }) => { } } - // dark: and light: variants - // FIXME: Remove comment and fix next line - // eslint-disable-next-line unicorn/prefer-includes - const hasDarkVariant = variantsList.some(v => v === 'dark') - // FIXME: Remove comment and fix next line - // eslint-disable-next-line unicorn/prefer-includes - const hasLightVariant = variantsList.some(v => v === 'light') - if (hasDarkVariant && hasLightVariant) { - throw new MacroError( - logGeneralError( - 'The light: and dark: variants can’t be used on the same element' - ) - ) - } - - const hasGroupVariant = variantsList.some(v => v.startsWith('group-')) + className = replaceSpaceId(className) // Match the filtered variants - const variants = getVariants({ - variants: variantsList, - state, - hasDarkVariant, - hasLightVariant, - hasGroupVariant, - }) + const variants = getVariants({ variants: variantsList, state, className }) const hasVariants = variants.length > 0 - className = replaceSpaceId(className) return { classNameRawNoVariants: className, className, variants, hasVariants, - hasVariantVisited: variants.includes(':visited'), + avoidAlpha: variants.includes(':visited'), } } diff --git a/src/screens.js b/src/screens.js deleted file mode 100644 index 1def4b16..00000000 --- a/src/screens.js +++ /dev/null @@ -1,29 +0,0 @@ -import { get, toArray } from './utils' - -const stringifyScreen = (config, screenName) => { - const screen = get(config, ['theme', 'screens', screenName]) - if (typeof screen === 'undefined') { - throw new Error( - `Couldn’t find Tailwind the screen "${screenName}" in the Tailwind config` - ) - } - - if (typeof screen === 'string') return `@media (min-width: ${screen})` - if (typeof screen.raw === 'string') { - return `@media ${screen.raw}` - } - - const string = toArray(screen) - .map(range => - [ - typeof range.min === 'string' ? `(min-width: ${range.min})` : null, - typeof range.max === 'string' ? `(max-width: ${range.max})` : null, - ] - .filter(Boolean) - .join(' and ') - ) - .join(', ') - return string ? `@media ${string}` : '' -} - -export { stringifyScreen } diff --git a/src/utils/alpha.js b/src/utils/alpha.js index 5bc06ab0..d83712b8 100644 --- a/src/utils/alpha.js +++ b/src/utils/alpha.js @@ -30,14 +30,14 @@ const toAlpha = ({ pieces, property, variable }) => (color, alpha, fallBackColor) => { const newPieces = - (pieces.hasVariantVisited && { ...pieces, alpha: '', hasAlpha: false }) || + (pieces.avoidAlpha && { ...pieces, alpha: '', hasAlpha: false }) || (alpha && { ...pieces, alpha, hasAlpha: true }) || pieces return withAlpha({ color, property, - ...(!pieces.hasVariantVisited && { variable }), + ...(!pieces.avoidAlpha && { variable }), pieces: newPieces, fallBackColor, }) diff --git a/src/utils/getUserPluginData.js b/src/utils/getUserPluginData.js index 18907fab..cc6e682c 100644 --- a/src/utils/getUserPluginData.js +++ b/src/utils/getUserPluginData.js @@ -7,6 +7,8 @@ import { LAYER_UTILITIES, SELECTOR_ALL, } from '../constants' +import { variantPlugins } from '../config/corePlugins' +import { debugPlugins } from '../logging' const stripLeadingDot = string => string.startsWith('.') ? string.slice(1) : string @@ -151,19 +153,8 @@ const getUserPluginRules = (rules, screens, isBase) => { return deepMerge(...ruleList) } -const getUserPluginData = ({ config, configTwin }) => { - if (!config.plugins || config.plugins.length === 0) return - - const context = { - candidateRuleMap: new Map(), - variants: new Map(), - tailwindConfig: config, - configTwin, - } - - const pluginApi = buildPluginApi(config, context) - - const userPlugins = config.plugins.map(plugin => { +const resolvePlugins = context => { + const userPlugins = context.tailwindConfig.plugins.map(plugin => { if (plugin.__isOptionsFunction) { plugin = plugin() } @@ -171,8 +162,32 @@ const getUserPluginData = ({ config, configTwin }) => { return typeof plugin === 'function' ? plugin : plugin.handler }) + // This is a workaround for backwards compatibility, since custom variants + // were historically sorted before screen/stackable variants. + const beforeVariants = [ + variantPlugins.pseudoElementVariants, + variantPlugins.pseudoClassVariants, + ] + const afterVariants = [ + variantPlugins.directionVariants, + variantPlugins.reducedMotionVariants, + variantPlugins.prefersContrastVariants, + variantPlugins.darkVariants, + variantPlugins.lightVariants, + variantPlugins.printVariants, + variantPlugins.screenVariants, + variantPlugins.orientationVariants, + variantPlugins.extraCursorVariants, + ] + + return [...beforeVariants, ...userPlugins, ...afterVariants] +} + +const registerPlugins = (plugins, context) => { + const pluginApi = buildPluginApi(context.tailwindConfig, context) + // Call each of the plugins with the pluginApi - for (const plugin of userPlugins) { + for (const plugin of plugins) { if (Array.isArray(plugin)) { for (const pluginItem of plugin) { pluginItem(pluginApi) @@ -181,7 +196,23 @@ const getUserPluginData = ({ config, configTwin }) => { plugin(pluginApi) } } +} +const createContext = (tailwindConfig, configTwin) => { + const context = { + candidateRuleMap: new Map(), + variants: new Map(), + tailwindConfig, + configTwin, + } + + const resolvedPlugins = resolvePlugins(context) + registerPlugins(resolvedPlugins, context) + + return context +} + +const getLayeredPluginData = context => { const baseRaw = [] const componentsRaw = [] const utilitiesRaw = [] @@ -202,12 +233,25 @@ const getUserPluginData = ({ config, configTwin }) => { } } + const { screens } = context.tailwindConfig.theme return { - base: getUserPluginRules(baseRaw, config.theme.screens, true), - components: getUserPluginRules(componentsRaw, config.theme.screens), - utilities: getUserPluginRules(utilitiesRaw, config.theme.screens), + base: getUserPluginRules(baseRaw, screens, true), + components: getUserPluginRules(componentsRaw, screens), + utilities: getUserPluginRules(utilitiesRaw, screens), variants: context.variants, } } +const getUserPluginData = ({ tailwindConfig, configTwin, state }) => { + const context = createContext(tailwindConfig, configTwin) + + const layeredPluginData = getLayeredPluginData(context) + + if (state.isDev && configTwin.debugPlugins) { + debugPlugins(layeredPluginData) + } + + return layeredPluginData +} + export default getUserPluginData diff --git a/src/utils/index.js b/src/utils/index.js index cfca6063..56ec3c23 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -15,5 +15,6 @@ export { replaceSpaceId, toArray, formatCssProperty, + stripMergePlaceholders, } from './misc' export { withAlpha, toAlpha, maybeAddAlpha } from './alpha' diff --git a/src/utils/misc.js b/src/utils/misc.js index 7c5b9d11..cfee77d8 100644 --- a/src/utils/misc.js +++ b/src/utils/misc.js @@ -138,6 +138,12 @@ const formatCssProperty = string => { return camelize(string) } +const stripMergePlaceholders = str => + str + .replace(/:merge\((\S*?)\)/, '$1') + .replace(/({{)|(}})/g, '') + .trim() + export { throwIf, isEmpty, @@ -155,4 +161,5 @@ export { replaceSpaceId, toArray, formatCssProperty, + stripMergePlaceholders, } diff --git a/src/utils/pluginApi.js b/src/utils/pluginApi.js index ed0353c0..cc6e5ef1 100644 --- a/src/utils/pluginApi.js +++ b/src/utils/pluginApi.js @@ -43,27 +43,43 @@ export default function buildPluginApi(tailwindConfig, context) { wrap, format, }) => { - const result = variantFunction({ + const extraParams = variantFunction[MATCH_VARIANT] && { + args, + wrap, + format, + } + let result = variantFunction({ modifySelectors, container, separator, - ...(variantFunction[MATCH_VARIANT] && { args, wrap, format }), + ...extraParams, }) + throwIf( typeof result === 'string' && !isValidVariantFormatString(result), () => logGeneralError( - `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.` + `\`${variantName}\` isn’t a valid at-rule or doesn’t contain a \`&\` placeholder` ) ) + + if (!context.configTwin.sassyPseudo) { + result = stripSassyPseudo(result) + } + return result } throwIf(!isValidVariantFormatString(variantFunction), () => logGeneralError( - `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.` + `\`${variantName}\` isn’t a valid at-rule or doesn’t contain a \`&\` placeholder` ) ) + + if (!context.configTwin.sassyPseudo) { + variantFunction = stripSassyPseudo(variantFunction) + } + return variantFunction }) @@ -205,6 +221,14 @@ const matchPlugin = (rules, options, { layer, context, prefixIdentifier }) => { } } +function stripSassyPseudo(string) { + if (typeof string !== 'string') return + return string + .split(',') + .map(v => v.replace(/(?<=^ *)&/, '').trim()) + .join(', ') +} + const asPlugin = (rules, options, { layer, context, prefixIdentifier }) => { const defaultOptions = { respectPrefix: true, diff --git a/src/variants.js b/src/variants.js index acea6be9..4da2dd02 100644 --- a/src/variants.js +++ b/src/variants.js @@ -1,56 +1,66 @@ /* eslint-disable @typescript-eslint/restrict-plus-operands */ import cleanSet from 'clean-set' import { logGeneralError } from './logging' -import { get, throwIf } from './utils' +import { get, throwIf, stripMergePlaceholders } from './utils' import { SPACE_ID } from './constants' -const getPeerValueFromVariant = variant => - get(/\.peer:(.+) ~ &/.exec(variant), '1') - -/** - * Combine peers when they are used in succession - */ -const combinePeers = ({ variants }) => - variants - .map((_, i) => { - let isPeer = false - let index = i - let returnVariant - const peerList = [] - - do { - const peer = getPeerValueFromVariant(variants[index]) - - isPeer = Boolean(peer) - if (isPeer) { - peerList.push(peer) - variants[index] = null - index = index + 1 - } else { - returnVariant = - peerList.length === 0 - ? variants[index] - : `.peer:${peerList.join(':')} ~ &` - } - } while (isPeer) - - return returnVariant - }) - .filter(Boolean) - -const addSassyPseudo = ({ variants, state }) => { - if (!state.configTwin.sassyPseudo) return variants - return variants.map(v => v.replace(/(?<= ):|^:/g, '&:')) +const MERGE_MAP_DIVIDER = '///' + +const handleMergeAndOrder = ({ variants }) => { + const variantMap = new Map() + // at-rules + .set('@', []) + // plain variants + .set('', []) + + for (const variant of variants) { + const mergeVariantValues = /:merge\((\S+?)\){{2}(.+?)}{2}( .+)?/.exec( + variant + ) + + if (!mergeVariantValues) { + variantMap.get(variant.startsWith('@') ? '@' : '').push(variant) + continue + } + + const [, className, selector, after] = mergeVariantValues + + // Remove any extra placeholders (for variant arrays) + const afterSelector = stripMergePlaceholders(after) + + const key = [className, afterSelector].join(MERGE_MAP_DIVIDER) + + if (!variantMap.has(key)) { + variantMap.set(key, []) + } + + variantMap.get(key).push(selector) + } + + const [[, atRuleVariants], [, plainVariants], ...mergeVariantsRaw] = + variantMap + + const mergeVariants = [...mergeVariantsRaw].flatMap( + ([beforeAfter, selectors]) => { + const [before = '', after = ''] = beforeAfter.split(MERGE_MAP_DIVIDER) + return [before, [selectors, after].join(' ')].join('') + } + ) + + return [...atRuleVariants, ...mergeVariants, ...plainVariants] } -const formatTasks = [combinePeers, addSassyPseudo] +const formatTasks = [ + handleMergeAndOrder, + // ... +] -const addVariants = ({ results, style, pieces, state }) => { +const addVariants = ({ results, style, pieces }) => { let { variants, hasVariants } = pieces if (!hasVariants) return style for (const task of formatTasks) { - variants = task({ variants, state }) + variants = task({ variants }) } let styleWithVariants @@ -84,7 +94,6 @@ function findRightBracket( const sliceToSpace = str => { const spaceIndex = str.indexOf(' ') if (spaceIndex === -1) return str - return str.slice(0, spaceIndex) }