diff --git a/src/corePlugins.js b/src/corePlugins.js index 10ad57102..1bafdd3be 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -1,8 +1,11 @@ import * as plugins from './plugins/index.js' - import configurePlugins from './util/configurePlugins' +const jitOnlyPlugins = ['content'] + export default function ({ corePlugins: corePluginConfig }) { + corePluginConfig = corePluginConfig.filter((pluginName) => !jitOnlyPlugins.includes(pluginName)) + return configurePlugins(corePluginConfig, Object.keys(plugins)).map((pluginName) => { return plugins[pluginName]() }) diff --git a/src/jit/corePlugins.js b/src/jit/corePlugins.js index 5c4b87051..f5fd23215 100644 --- a/src/jit/corePlugins.js +++ b/src/jit/corePlugins.js @@ -12,6 +12,50 @@ import { export default { pseudoClassVariants: function ({ config, addVariant }) { + addVariant( + 'before', + transformAllSelectors( + (selector) => { + return updateAllClasses(selector, (className, { withPseudo }) => { + return withPseudo(`before${config('separator')}${className}`, '::before') + }) + }, + { + withRule: (rule) => { + let foundContent = false + rule.walkDecls('content', () => { + foundContent = true + }) + if (!foundContent) { + rule.prepend(postcss.decl({ prop: 'content', value: '""' })) + } + }, + } + ) + ) + + addVariant( + 'after', + transformAllSelectors( + (selector) => { + return updateAllClasses(selector, (className, { withPseudo }) => { + return withPseudo(`after${config('separator')}${className}`, '::after') + }) + }, + { + withRule: (rule) => { + let foundContent = false + rule.walkDecls('content', () => { + foundContent = true + }) + if (!foundContent) { + rule.prepend(postcss.decl({ prop: 'content', value: '""' })) + } + }, + } + ) + ) + let pseudoVariants = [ ['first', 'first-child'], ['last', 'last-child'], @@ -35,7 +79,7 @@ export default { addVariant( variantName, transformAllClasses((className, { withPseudo }) => { - return withPseudo(`${variantName}${config('separator')}${className}`, state) + return withPseudo(`${variantName}${config('separator')}${className}`, `:${state}`) }) ) } @@ -95,11 +139,13 @@ export default { (className) => { return `motion-safe${config('separator')}${className}` }, - () => - postcss.atRule({ - name: 'media', - params: '(prefers-reduced-motion: no-preference)', - }) + { + wrap: () => + postcss.atRule({ + name: 'media', + params: '(prefers-reduced-motion: no-preference)', + }), + } ) ) @@ -109,11 +155,13 @@ export default { (className) => { return `motion-reduce${config('separator')}${className}` }, - () => - postcss.atRule({ - name: 'media', - params: '(prefers-reduced-motion: reduce)', - }) + { + wrap: () => + postcss.atRule({ + name: 'media', + params: '(prefers-reduced-motion: reduce)', + }), + } ) ) }, @@ -142,11 +190,13 @@ export default { (className) => { return `dark${config('separator')}${className}` }, - () => - postcss.atRule({ - name: 'media', - params: '(prefers-color-scheme: dark)', - }) + { + wrap: () => + postcss.atRule({ + name: 'media', + params: '(prefers-color-scheme: dark)', + }), + } ) ) } @@ -162,7 +212,7 @@ export default { (className) => { return `${screen}${config('separator')}${className}` }, - () => postcss.atRule({ name: 'media', params: query }) + { wrap: () => postcss.atRule({ name: 'media', params: query }) } ) ) } diff --git a/src/plugins/content.js b/src/plugins/content.js new file mode 100644 index 000000000..cc55ee6ff --- /dev/null +++ b/src/plugins/content.js @@ -0,0 +1,5 @@ +import createUtilityPlugin from '../util/createUtilityPlugin' + +export default function () { + return createUtilityPlugin('content') +} diff --git a/src/plugins/index.js b/src/plugins/index.js index bea0b2a08..8107a2eaf 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -161,3 +161,5 @@ export { default as transitionProperty } from './transitionProperty' export { default as transitionDelay } from './transitionDelay' export { default as transitionDuration } from './transitionDuration' export { default as transitionTimingFunction } from './transitionTimingFunction' + +export { default as content } from './content' diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index a37c6e98d..33abffc20 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -9,7 +9,7 @@ export function updateAllClasses(selectors, updateClass) { selectors.walkClasses((sel) => { let updatedClass = updateClass(sel.value, { withPseudo(className, pseudo) { - sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `:${pseudo}` })) + sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `${pseudo}` })) return className }, }) @@ -36,7 +36,7 @@ export function updateLastClasses(selectors, updateClass) { let updatedClass = updateClass(lastClass.value, { withPseudo(className, pseudo) { - lastClass.parent.insertAfter(lastClass, selectorParser.pseudo({ value: `:${pseudo}` })) + lastClass.parent.insertAfter(lastClass, selectorParser.pseudo({ value: `${pseudo}` })) return className }, }) @@ -51,11 +51,14 @@ export function updateLastClasses(selectors, updateClass) { return result } -export function transformAllSelectors(transformSelector, wrap = null) { +export function transformAllSelectors(transformSelector, { wrap, withRule } = {}) { return ({ container }) => { container.walkRules((rule) => { let transformed = rule.selector.split(',').map(transformSelector).join(',') rule.selector = transformed + if (withRule) { + withRule(rule) + } return rule }) @@ -67,23 +70,35 @@ export function transformAllSelectors(transformSelector, wrap = null) { } } -export function transformAllClasses(transformClass) { +export function transformAllClasses(transformClass, { wrap, withRule } = {}) { return ({ container }) => { container.walkRules((rule) => { let selector = rule.selector let variantSelector = updateAllClasses(selector, transformClass) rule.selector = variantSelector + if (withRule) { + withRule(rule) + } return rule }) + + if (wrap) { + let wrapper = wrap() + wrapper.append(container.nodes) + container.append(wrapper) + } } } -export function transformLastClasses(transformClass, wrap = null) { +export function transformLastClasses(transformClass, { wrap, withRule } = {}) { return ({ container }) => { container.walkRules((rule) => { let selector = rule.selector let variantSelector = updateLastClasses(selector, transformClass) rule.selector = variantSelector + if (withRule) { + withRule(rule) + } return rule }) diff --git a/stubs/defaultConfig.stub.js b/stubs/defaultConfig.stub.js index 29cc4dcb8..dcc51b6fb 100644 --- a/stubs/defaultConfig.stub.js +++ b/stubs/defaultConfig.stub.js @@ -175,6 +175,9 @@ module.exports = { 200: '2', }, container: {}, + content: { + none: 'none', + }, cursor: { auto: 'auto', default: 'default', diff --git a/tests/jit/arbitrary-values.test.css b/tests/jit/arbitrary-values.test.css index e648584c2..de5344278 100644 --- a/tests/jit/arbitrary-values.test.css +++ b/tests/jit/arbitrary-values.test.css @@ -383,6 +383,9 @@ .duration-\[var\(--app-duration\)\] { transition-duration: var(--app-duration); } +.content-\[attr\(content-before\)\] { + content: attr(content-before); +} @media (min-width: 1024px) { .lg\:grid-cols-\[200px\2c repeat\(auto-fill\2c minmax\(15\%\2c 100px\)\)\2c 300px\] { grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px; diff --git a/tests/jit/arbitrary-values.test.html b/tests/jit/arbitrary-values.test.html index 13b2b98b4..1811d2bed 100644 --- a/tests/jit/arbitrary-values.test.html +++ b/tests/jit/arbitrary-values.test.html @@ -111,5 +111,6 @@
+