Last active
July 1, 2021 15:12
-
-
Save mpalpha/01b337a59b67f885a5e02d0f38a756f1 to your computer and use it in GitHub Desktop.
tailwind peer pseudo variants extracted for use in tailwindcss v2.2 outside of jit mode. (warning: use a parser, this can inflate your css)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// from https://blog.tailwindcss.com/tailwindcss-2-2#sibling-selector-variants | |
// add your desired variant 'peer-checked' | |
// | |
// Just like group can be combined with any other variant, peer can as well, | |
// so you have variants like peer-hover, peer-focus, peer-disabled, and loads more at your fingertips. | |
const plugin = require('tailwindcss/plugin'); | |
const peer = plugin.withOptions(function (options) { | |
return function ({ addVariant, config }) { | |
const prefixSelector = function (prefix, selector) { | |
const parser = require('postcss-selector-parser'); | |
const tap = require('lodash/tap'); | |
const getPrefix = typeof prefix === 'function' ? prefix : () => (prefix === undefined ? '' : prefix); | |
return parser(selectors => { | |
selectors.walkClasses(classSelector => { | |
tap(classSelector.value, baseClass => { | |
classSelector.value = `${getPrefix('.' + baseClass)}${baseClass}`; | |
}); | |
}); | |
}).processSync(selector); | |
}; | |
const updateAllClasses = function (selectors, updateClass) { | |
const selectorParser = require('postcss-selector-parser'); | |
const escapeCommas = function (className) { | |
return className.replace(/\\,/g, '\\2c '); | |
}; | |
let parser = selectorParser(selectors => { | |
selectors.walkClasses(sel => { | |
let updatedClass = updateClass(sel.value, { | |
withPseudo(className, pseudo) { | |
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `${pseudo}` })); | |
return className; | |
}, | |
}); | |
sel.value = updatedClass; | |
if (sel.raws && sel.raws.value) { | |
sel.raws.value = escapeCommas(sel.raws.value); | |
} | |
}); | |
}); | |
let result = parser.processSync(selectors); | |
return result; | |
}; | |
const applyPseudoToMarker = function (selector, marker, state, join) { | |
let states = [state]; | |
let markerIdx = selector.indexOf(marker + ':'); | |
if (markerIdx !== -1) { | |
let existingMarker = selector.slice(markerIdx, selector.indexOf(' ', markerIdx)); | |
states = states.concat(selector.slice(markerIdx + marker.length + 1, existingMarker.length).split(':')); | |
selector = selector.replace(existingMarker, ''); | |
} | |
return join(`${[marker, ...states].join(':')}`, selector); | |
}; | |
const transformAllSelectors = function (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; | |
}); | |
if (wrap) { | |
let wrapper = wrap(); | |
wrapper.append(container.nodes); | |
container.append(wrapper); | |
} | |
}; | |
}; | |
let 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', | |
'target', | |
// Forms | |
'default', | |
'checked', | |
'indeterminate', | |
'placeholder-shown', | |
'autofill', | |
'required', | |
'valid', | |
'invalid', | |
'in-range', | |
'out-of-range', | |
'read-only', | |
// Content | |
'empty', | |
// Interactive | |
'focus-within', | |
'hover', | |
'focus', | |
'focus-visible', | |
'active', | |
'disabled', | |
]; | |
let peerMarker = prefixSelector(config('prefix'), '.peer'); | |
for (let variant of pseudoVariants) { | |
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]; | |
let peerVariantName = `peer-${variantName}`; | |
addVariant( | |
peerVariantName, | |
transformAllSelectors(selector => { | |
let variantSelector = updateAllClasses(selector, className => { | |
if (`.${className}` === peerMarker) return className; | |
return `${peerVariantName}${config('separator')}${className}`; | |
}); | |
if (variantSelector === selector) { | |
return null; | |
} | |
return applyPseudoToMarker(variantSelector, peerMarker, state, (marker, selector) => | |
selector.trim().startsWith('~') ? `${marker}${selector}` : `${marker} ~ ${selector}`, | |
); | |
}), | |
); | |
} | |
}; | |
}); | |
module.exports = peer; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment