Skip to content

Commit 8004917

Browse files
reininkadamwathanseanpdoyleRobinMalfait
authored
Add [open] variant (#5627)
* Add `[open]` variant Co-Authored-By: Adam Wathan <[email protected]> Co-Authored-By: Sean Doyle <[email protected]> * Add new `applyStateToMarker()` function This function replaces the existing `applyPseudoToMarker()` and `applyAttributeToMarker()` functions. Co-Authored-By: Robin Malfait <[email protected]> Co-authored-by: Adam Wathan <[email protected]> Co-authored-by: Sean Doyle <[email protected]> Co-authored-by: Robin Malfait <[email protected]>
1 parent c30a32e commit 8004917

File tree

4 files changed

+59
-27
lines changed

4 files changed

+59
-27
lines changed

src/corePlugins.js

+18-13
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import toColorValue from './util/toColorValue'
1010
import isPlainObject from './util/isPlainObject'
1111
import transformThemeValue from './util/transformThemeValue'
1212
import {
13-
applyPseudoToMarker,
13+
applyStateToMarker,
1414
updateLastClasses,
1515
updateAllClasses,
1616
transformAllSelectors,
@@ -128,18 +128,19 @@ export default {
128128
pseudoClassVariants: ({ config, addVariant }) => {
129129
let pseudoVariants = [
130130
// Positional
131-
['first', 'first-child'],
132-
['last', 'last-child'],
133-
['only', 'only-child'],
134-
['odd', 'nth-child(odd)'],
135-
['even', 'nth-child(even)'],
131+
['first', ':first-child'],
132+
['last', ':last-child'],
133+
['only', ':only-child'],
134+
['odd', ':nth-child(odd)'],
135+
['even', ':nth-child(even)'],
136136
'first-of-type',
137137
'last-of-type',
138138
'only-of-type',
139139

140140
// State
141141
'visited',
142142
'target',
143+
['open', '[open]'],
143144

144145
// Forms
145146
'default',
@@ -167,19 +168,23 @@ export default {
167168
]
168169

169170
for (let variant of pseudoVariants) {
170-
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
171+
let [variantName, state] = Array.isArray(variant) ? variant : [variant, `:${variant}`]
171172

172173
addVariant(
173174
variantName,
174-
transformAllClasses((className, { withPseudo }) => {
175-
return withPseudo(`${variantName}${config('separator')}${className}`, `:${state}`)
175+
transformAllClasses((className, { withAttr, withPseudo }) => {
176+
if (state.startsWith(':')) {
177+
return withPseudo(`${variantName}${config('separator')}${className}`, state)
178+
} else if (state.startsWith('[')) {
179+
return withAttr(`${variantName}${config('separator')}${className}`, state)
180+
}
176181
})
177182
)
178183
}
179184

180185
let groupMarker = prefixSelector(config('prefix'), '.group')
181186
for (let variant of pseudoVariants) {
182-
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
187+
let [variantName, state] = Array.isArray(variant) ? variant : [variant, `:${variant}`]
183188
let groupVariantName = `group-${variantName}`
184189

185190
addVariant(
@@ -194,7 +199,7 @@ export default {
194199
return null
195200
}
196201

197-
return applyPseudoToMarker(
202+
return applyStateToMarker(
198203
variantSelector,
199204
groupMarker,
200205
state,
@@ -206,7 +211,7 @@ export default {
206211

207212
let peerMarker = prefixSelector(config('prefix'), '.peer')
208213
for (let variant of pseudoVariants) {
209-
let [variantName, state] = Array.isArray(variant) ? variant : [variant, variant]
214+
let [variantName, state] = Array.isArray(variant) ? variant : [variant, `:${variant}`]
210215
let peerVariantName = `peer-${variantName}`
211216

212217
addVariant(
@@ -221,7 +226,7 @@ export default {
221226
return null
222227
}
223228

224-
return applyPseudoToMarker(variantSelector, peerMarker, state, (marker, selector) =>
229+
return applyStateToMarker(variantSelector, peerMarker, state, (marker, selector) =>
225230
selector.trim().startsWith('~') ? `${marker}${selector}` : `${marker} ~ ${selector}`
226231
)
227232
})

src/util/pluginUtils.js

+15-14
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,31 @@ import {
1818
lineWidth,
1919
} from './dataTypes'
2020

21-
export function applyPseudoToMarker(selector, marker, state, join) {
22-
let states = [state]
21+
export function applyStateToMarker(selector, marker, state, join) {
22+
let markerIdx = selector.search(new RegExp(`${marker}[:[]`))
2323

24-
let markerIdx = selector.indexOf(marker + ':')
25-
26-
if (markerIdx !== -1) {
27-
let existingMarker = selector.slice(markerIdx, selector.indexOf(' ', markerIdx))
28-
29-
states = states.concat(
30-
selector.slice(markerIdx + marker.length + 1, existingMarker.length).split(':')
31-
)
32-
33-
selector = selector.replace(existingMarker, '')
24+
if (markerIdx === -1) {
25+
return join(marker + state, selector)
3426
}
3527

36-
return join(`${[marker, ...states].join(':')}`, selector)
28+
let markerSelector = selector.slice(markerIdx, selector.indexOf(' ', markerIdx))
29+
30+
return join(
31+
marker + state + markerSelector.slice(markerIdx + marker.length),
32+
selector.replace(markerSelector, '')
33+
)
3734
}
3835

3936
export function updateAllClasses(selectors, updateClass) {
4037
let parser = selectorParser((selectors) => {
4138
selectors.walkClasses((sel) => {
4239
let updatedClass = updateClass(sel.value, {
40+
withAttr(className, attr) {
41+
sel.parent.insertAfter(sel, selectorParser.attribute({ attribute: attr.slice(1, -1) }))
42+
return className
43+
},
4344
withPseudo(className, pseudo) {
44-
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `${pseudo}` }))
45+
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: pseudo }))
4546
return className
4647
},
4748
})

tests/variants.test.css

+21
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@
127127
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
128128
var(--tw-shadow);
129129
}
130+
.open\:bg-red-200[open] {
131+
--tw-bg-opacity: 1;
132+
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
133+
}
130134
.default\:shadow-md:default {
131135
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -1px rgb(0 0 0 / 0.06);
132136
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
@@ -201,6 +205,10 @@
201205
--tw-bg-opacity: 1;
202206
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
203207
}
208+
.open\:hover\:bg-red-200[open]:hover {
209+
--tw-bg-opacity: 1;
210+
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
211+
}
204212
.focus\:shadow-md:focus {
205213
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -1px rgb(0 0 0 / 0.06);
206214
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
@@ -276,6 +284,10 @@
276284
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
277285
var(--tw-shadow);
278286
}
287+
.group[open] .group-open\:bg-red-200 {
288+
--tw-bg-opacity: 1;
289+
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
290+
}
279291
.group:default .group-default\:shadow-md {
280292
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -1px rgb(0 0 0 / 0.06);
281293
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
@@ -346,11 +358,20 @@
346358
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
347359
var(--tw-shadow);
348360
}
361+
.group[open]:hover .group-open\:group-hover\:space-x-2 > :not([hidden]) ~ :not([hidden]) {
362+
--tw-space-x-reverse: 0;
363+
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
364+
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
365+
}
349366
.group:focus .group-focus\:shadow-md {
350367
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -1px rgb(0 0 0 / 0.06);
351368
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
352369
var(--tw-shadow);
353370
}
371+
.group[open]:focus .group-open\:group-focus\:bg-red-200 {
372+
--tw-bg-opacity: 1;
373+
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
374+
}
354375
.group:focus:hover .group-focus\:group-hover\:shadow-md {
355376
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -1px rgb(0 0 0 / 0.06);
356377
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),

tests/variants.test.html

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<div class="disabled:shadow-md"></div>
2323
<div class="active:shadow-md"></div>
2424
<div class="target:shadow-md"></div>
25+
<div class="open:bg-red-200"></div>
2526
<div class="visited:shadow-md"></div>
2627
<div class="default:shadow-md"></div>
2728
<div class="checked:shadow-md"></div>
@@ -50,6 +51,7 @@
5051
<div class="after:flex after:uppercase"></div>
5152

5253
<!-- Group variants -->
54+
<div class="group-open:bg-red-200"></div>
5355
<div class="group-first:shadow-md"></div>
5456
<div class="group-last:shadow-md"></div>
5557
<div class="group-only:shadow-md"></div>
@@ -128,6 +130,7 @@
128130
<div class="2xl:shadow-md"></div>
129131

130132
<!-- Stacked variants -->
133+
<div class="open:hover:bg-red-200"></div>
131134
<div class="file:hover:bg-blue-600"></div>
132135
<div class="focus:hover:shadow-md"></div>
133136
<div class="sm:active:shadow-md"></div>
@@ -137,6 +140,8 @@
137140
<div class="2xl:dark:motion-safe:focus-within:shadow-md"></div>
138141

139142
<!-- Stacked group variants -->
143+
<div class="group-open:group-focus:bg-red-200"></div>
144+
<div class="group-open:group-hover:space-x-2"></div>
140145
<div class="group-focus:group-hover:shadow-md"></div>
141146
<div class="group-disabled:group-focus:group-hover:shadow-md"></div>
142147
<div class="dark:group-disabled:group-focus:group-hover:shadow-md"></div>

0 commit comments

Comments
 (0)