Skip to content

Commit

Permalink
Add peer variant
Browse files Browse the repository at this point in the history
  • Loading branch information
adamwathan committed Jun 3, 2021
1 parent 05d26a5 commit 146e9ea
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 19 deletions.
50 changes: 31 additions & 19 deletions src/jit/corePlugins.js
Expand Up @@ -3,6 +3,7 @@ import * as corePlugins from '../plugins'
import buildMediaQuery from '../util/buildMediaQuery'
import prefixSelector from '../util/prefixSelector'
import {
applyPseudoToMarker,
updateLastClasses,
updateAllClasses,
transformAllSelectors,
Expand Down Expand Up @@ -156,7 +157,7 @@ export default {
)
}

let baseGroupSelector = prefixSelector(config('prefix'), '.group')
let groupMarker = prefixSelector(config('prefix'), '.group')
for (let variant of pseudoVariants) {
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
let groupVariantName = `group-${variantName}`
Expand All @@ -165,36 +166,47 @@ export default {
groupVariantName,
transformAllSelectors((selector) => {
let variantSelector = updateAllClasses(selector, (className) => {
if (`.${className}` === baseGroupSelector) return className
if (`.${className}` === groupMarker) return className
return `${groupVariantName}${config('separator')}${className}`
})

if (variantSelector === selector) {
return null
}

let states = [state]
return applyPseudoToMarker(
variantSelector,
groupMarker,
state,
(marker, selector) => `${marker} ${selector}`
)
})
)
}

// Stack group variants
let baseGroupIdx = variantSelector.indexOf(baseGroupSelector + ':')
if (baseGroupIdx !== -1) {
let groupClassName = variantSelector.slice(
baseGroupIdx,
variantSelector.indexOf(' ', baseGroupIdx)
)
let peerMarker = prefixSelector(config('prefix'), '.peer')
for (let variant of pseudoVariants) {
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
let peerVariantName = `peer-${variantName}`

// Pick all the states
states = states.concat(
variantSelector
.slice(baseGroupIdx + baseGroupSelector.length + 1, groupClassName.length)
.split(':')
)
addVariant(
peerVariantName,
transformAllSelectors((selector) => {
let variantSelector = updateAllClasses(selector, (className) => {
if (`.${className}` === peerMarker) return className
return `${peerVariantName}${config('separator')}${className}`
})

// Remove the base `.group:...`
variantSelector = variantSelector.replace(groupClassName, '')
if (variantSelector === selector) {
return null
}

return `${[baseGroupSelector, ...states].join(':')} ${variantSelector}`
return applyPseudoToMarker(
variantSelector,
peerMarker,
state,
(marker, selector) => `${marker} ~ ${selector}`
)
})
)
}
Expand Down
18 changes: 18 additions & 0 deletions src/util/pluginUtils.js
Expand Up @@ -4,6 +4,24 @@ import createColor from 'color'
import escapeCommas from './escapeCommas'
import { withAlphaValue } from './withAlphaVariable'

export function applyPseudoToMarker(selector, marker, state, join) {
let states = [state]

let targetIdx = selector.indexOf(marker + ':')

if (targetIdx !== -1) {
let existingTarget = selector.slice(targetIdx, selector.indexOf(' ', targetIdx))

states = states.concat(
selector.slice(targetIdx + marker.length + 1, existingTarget.length).split(':')
)

selector = selector.replace(existingTarget, '')
}

return join(`${[marker, ...states].join(':')}`, selector)
}

export function updateAllClasses(selectors, updateClass) {
let parser = selectorParser((selectors) => {
selectors.walkClasses((sel) => {
Expand Down
160 changes: 160 additions & 0 deletions tests/jit/variants.test.css
Expand Up @@ -378,6 +378,161 @@
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:first-child ~ .peer-first\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:last-child ~ .peer-last\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:only-child ~ .peer-only\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:nth-child(odd) ~ .peer-odd\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:nth-child(even) ~ .peer-even\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:first-of-type ~ .peer-first-of-type\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:last-of-type ~ .peer-last-of-type\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:only-of-type ~ .peer-only-of-type\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:visited ~ .peer-visited\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:target ~ .peer-target\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:default ~ .peer-default\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:checked ~ .peer-checked\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:indeterminate ~ .peer-indeterminate\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:placeholder-shown ~ .peer-placeholder-shown\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:autofill ~ .peer-autofill\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:required ~ .peer-required\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:valid ~ .peer-valid\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:invalid ~ .peer-invalid\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:in-range ~ .peer-in-range\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:out-of-range ~ .peer-out-of-range\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:read-only ~ .peer-read-only\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:empty ~ .peer-empty\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:focus-within ~ .peer-focus-within\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:hover ~ .peer-hover\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:focus ~ .peer-focus\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:focus:hover ~ .peer-focus\:peer-hover\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:focus-visible ~ .peer-focus-visible\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:active ~ .peer-active\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:disabled ~ .peer-disabled\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.peer:disabled:focus:hover ~ .peer-disabled\:peer-focus\:peer-hover\:first\:shadow-md:first-child {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
[dir='ltr'] .ltr\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
Expand Down Expand Up @@ -412,6 +567,11 @@
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.dark .peer:disabled:focus:hover ~ .dark\:peer-disabled\:peer-focus\:peer-hover\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
@media (min-width: 640px) {
.sm\:shadow-md {
--tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
Expand Down
36 changes: 36 additions & 0 deletions tests/jit/variants.test.html
Expand Up @@ -78,6 +78,36 @@
<div class="group-read-only:shadow-md"></div>
<div class="group-empty:shadow-md"></div>

<!-- Peer variants -->
<div class="peer-first:shadow-md"></div>
<div class="peer-last:shadow-md"></div>
<div class="peer-only:shadow-md"></div>
<div class="peer-even:shadow-md"></div>
<div class="peer-odd:shadow-md"></div>
<div class="peer-first-of-type:shadow-md"></div>
<div class="peer-last-of-type:shadow-md"></div>
<div class="peer-only-of-type:shadow-md"></div>
<div class="peer-hover:shadow-md"></div>
<div class="peer-focus:shadow-md"></div>
<div class="peer-disabled:shadow-md"></div>
<div class="peer-active:shadow-md"></div>
<div class="peer-target:shadow-md"></div>
<div class="peer-visited:shadow-md"></div>
<div class="peer-default:shadow-md"></div>
<div class="peer-checked:shadow-md"></div>
<div class="peer-indeterminate:shadow-md"></div>
<div class="peer-placeholder-shown:shadow-md"></div>
<div class="peer-autofill:shadow-md"></div>
<div class="peer-focus-within:shadow-md"></div>
<div class="peer-focus-visible:shadow-md"></div>
<div class="peer-required:shadow-md"></div>
<div class="peer-valid:shadow-md"></div>
<div class="peer-invalid:shadow-md"></div>
<div class="peer-in-range:shadow-md"></div>
<div class="peer-out-of-range:shadow-md"></div>
<div class="peer-read-only:shadow-md"></div>
<div class="peer-empty:shadow-md"></div>

<!-- Reduced motion variants -->
<div class="motion-safe:shadow-md"></div>
<div class="motion-reduce:shadow-md"></div>
Expand Down Expand Up @@ -109,5 +139,11 @@
<div class="group-disabled:group-focus:group-hover:shadow-md"></div>
<div class="dark:group-disabled:group-focus:group-hover:shadow-md"></div>
<div class="group-disabled:group-focus:group-hover:first:shadow-md"></div>

<!-- Stacked peer variants -->
<div class="peer-focus:peer-hover:shadow-md"></div>
<div class="peer-disabled:peer-focus:peer-hover:shadow-md"></div>
<div class="dark:peer-disabled:peer-focus:peer-hover:shadow-md"></div>
<div class="peer-disabled:peer-focus:peer-hover:first:shadow-md"></div>
</body>
</html>

0 comments on commit 146e9ea

Please sign in to comment.