Skip to content

Instantly share code, notes, and snippets.

@mpalpha
Last active July 1, 2021 15:12
Show Gist options
  • Save mpalpha/01b337a59b67f885a5e02d0f38a756f1 to your computer and use it in GitHub Desktop.
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)
// 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