arbitrary-variants.test.js 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. import { run, html, css, defaults } from './util/run'
  2. import { flagEnabled } from '../src/featureFlags'
  3. test('basic arbitrary variants', () => {
  4. let config = {
  5. content: [{ raw: html`<div class="[&>*]:underline"></div>` }],
  6. corePlugins: { preflight: false },
  7. }
  8. let input = css`
  9. @tailwind base;
  10. @tailwind components;
  11. @tailwind utilities;
  12. `
  13. return run(input, config).then((result) => {
  14. expect(result.css).toMatchFormattedCss(css`
  15. ${defaults}
  16. .\[\&\>\*\]\:underline > * {
  17. text-decoration-line: underline;
  18. }
  19. `)
  20. })
  21. })
  22. test('spaces in selector (using _)', () => {
  23. let config = {
  24. content: [
  25. {
  26. raw: html`<div class="[.a.b_&]:underline"></div>`,
  27. },
  28. ],
  29. corePlugins: { preflight: false },
  30. }
  31. let input = css`
  32. @tailwind base;
  33. @tailwind components;
  34. @tailwind utilities;
  35. `
  36. return run(input, config).then((result) => {
  37. expect(result.css).toMatchFormattedCss(css`
  38. ${defaults}
  39. .a.b .\[\.a\.b_\&\]\:underline {
  40. text-decoration-line: underline;
  41. }
  42. `)
  43. })
  44. })
  45. test('arbitrary variants with modifiers', () => {
  46. let config = {
  47. content: [{ raw: html`<div class="dark:lg:hover:[&>*]:underline"></div>` }],
  48. corePlugins: { preflight: false },
  49. }
  50. let input = css`
  51. @tailwind base;
  52. @tailwind components;
  53. @tailwind utilities;
  54. `
  55. return run(input, config).then((result) => {
  56. expect(result.css).toMatchFormattedCss(css`
  57. ${defaults}
  58. @media (prefers-color-scheme: dark) {
  59. @media (min-width: 1024px) {
  60. .dark\:lg\:hover\:\[\&\>\*\]\:underline > :hover {
  61. text-decoration-line: underline;
  62. }
  63. }
  64. }
  65. `)
  66. })
  67. })
  68. test('variants without & or an at-rule are ignored', () => {
  69. let config = {
  70. content: [
  71. {
  72. raw: html`
  73. <div class="[div]:underline"></div>
  74. <div class="[:hover]:underline"></div>
  75. <div class="[wtf-bbq]:underline"></div>
  76. <div class="[lol]:hover:underline"></div>
  77. `,
  78. },
  79. ],
  80. corePlugins: { preflight: false },
  81. }
  82. let input = css`
  83. @tailwind base;
  84. @tailwind components;
  85. @tailwind utilities;
  86. `
  87. return run(input, config).then((result) => {
  88. expect(result.css).toMatchFormattedCss(css`
  89. ${defaults}
  90. `)
  91. })
  92. })
  93. test('arbitrary variants are sorted after other variants', () => {
  94. let config = {
  95. content: [{ raw: html`<div class="underline lg:underline [&>*]:underline"></div>` }],
  96. corePlugins: { preflight: false },
  97. }
  98. let input = css`
  99. @tailwind base;
  100. @tailwind components;
  101. @tailwind utilities;
  102. `
  103. return run(input, config).then((result) => {
  104. expect(result.css).toMatchFormattedCss(css`
  105. ${defaults}
  106. .underline {
  107. text-decoration-line: underline;
  108. }
  109. @media (min-width: 1024px) {
  110. .lg\:underline {
  111. text-decoration-line: underline;
  112. }
  113. }
  114. .\[\&\>\*\]\:underline > * {
  115. text-decoration-line: underline;
  116. }
  117. `)
  118. })
  119. })
  120. test('using the important modifier', () => {
  121. let config = {
  122. content: [{ raw: html`<div class="[&>*]:!underline"></div>` }],
  123. corePlugins: { preflight: false },
  124. }
  125. let input = css`
  126. @tailwind base;
  127. @tailwind components;
  128. @tailwind utilities;
  129. `
  130. return run(input, config).then((result) => {
  131. expect(result.css).toMatchFormattedCss(css`
  132. ${defaults}
  133. .\[\&\>\*\]\:\!underline > * {
  134. text-decoration-line: underline !important;
  135. }
  136. `)
  137. })
  138. })
  139. test('at-rules', () => {
  140. let config = {
  141. content: [{ raw: html`<div class="[@supports(what:ever)]:underline"></div>` }],
  142. corePlugins: { preflight: false },
  143. }
  144. let input = css`
  145. @tailwind base;
  146. @tailwind components;
  147. @tailwind utilities;
  148. `
  149. return run(input, config).then((result) => {
  150. expect(result.css).toMatchFormattedCss(css`
  151. ${defaults}
  152. @supports (what: ever) {
  153. .\[\@supports\(what\:ever\)\]\:underline {
  154. text-decoration-line: underline;
  155. }
  156. }
  157. `)
  158. })
  159. })
  160. test('nested at-rules', () => {
  161. let config = {
  162. content: [
  163. {
  164. raw: html`<div class="[@media_screen{@media(hover:hover)}]:underline"></div>`,
  165. },
  166. ],
  167. corePlugins: { preflight: false },
  168. }
  169. let input = css`
  170. @tailwind base;
  171. @tailwind components;
  172. @tailwind utilities;
  173. `
  174. return run(input, config).then((result) => {
  175. expect(result.css).toMatchFormattedCss(css`
  176. ${defaults}
  177. @media screen {
  178. @media (hover: hover) {
  179. .\[\@media_screen\{\@media\(hover\:hover\)\}\]\:underline {
  180. text-decoration-line: underline;
  181. }
  182. }
  183. }
  184. `)
  185. })
  186. })
  187. test('at-rules with selector modifications', () => {
  188. let config = {
  189. content: [{ raw: html`<div class="[@media(hover:hover){&:hover}]:underline"></div>` }],
  190. corePlugins: { preflight: false },
  191. }
  192. let input = css`
  193. @tailwind base;
  194. @tailwind components;
  195. @tailwind utilities;
  196. `
  197. return run(input, config).then((result) => {
  198. expect(result.css).toMatchFormattedCss(css`
  199. ${defaults}
  200. @media (hover: hover) {
  201. .\[\@media\(hover\:hover\)\{\&\:hover\}\]\:underline:hover {
  202. text-decoration-line: underline;
  203. }
  204. }
  205. `)
  206. })
  207. })
  208. test('nested at-rules with selector modifications', () => {
  209. let config = {
  210. content: [
  211. {
  212. raw: html`<div class="[@media_screen{@media(hover:hover){&:hover}}]:underline"></div>`,
  213. },
  214. ],
  215. corePlugins: { preflight: false },
  216. }
  217. let input = css`
  218. @tailwind base;
  219. @tailwind components;
  220. @tailwind utilities;
  221. `
  222. return run(input, config).then((result) => {
  223. expect(result.css).toMatchFormattedCss(css`
  224. ${defaults}
  225. @media screen {
  226. @media (hover: hover) {
  227. .\[\@media_screen\{\@media\(hover\:hover\)\{\&\:hover\}\}\]\:underline:hover {
  228. text-decoration-line: underline;
  229. }
  230. }
  231. }
  232. `)
  233. })
  234. })
  235. test('attribute selectors', () => {
  236. let config = {
  237. content: [{ raw: html`<div class="[&[data-open]]:underline"></div>` }],
  238. corePlugins: { preflight: false },
  239. }
  240. let input = css`
  241. @tailwind base;
  242. @tailwind components;
  243. @tailwind utilities;
  244. `
  245. return run(input, config).then((result) => {
  246. expect(result.css).toMatchFormattedCss(css`
  247. ${defaults}
  248. .\[\&\[data-open\]\]\:underline[data-open] {
  249. text-decoration-line: underline;
  250. }
  251. `)
  252. })
  253. })
  254. test('multiple attribute selectors', () => {
  255. let config = {
  256. content: [{ raw: html`<div class="[&[data-foo][data-bar]:not([data-baz])]:underline"></div>` }],
  257. corePlugins: { preflight: false },
  258. }
  259. let input = css`
  260. @tailwind base;
  261. @tailwind components;
  262. @tailwind utilities;
  263. `
  264. return run(input, config).then((result) => {
  265. expect(result.css).toMatchFormattedCss(css`
  266. ${defaults}
  267. .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]\:underline[data-foo][data-bar]:not([data-baz]) {
  268. text-decoration-line: underline;
  269. }
  270. `)
  271. })
  272. })
  273. test('multiple attribute selectors with custom separator (1)', () => {
  274. let config = {
  275. separator: '__',
  276. content: [
  277. { raw: html`<div class="[&[data-foo][data-bar]:not([data-baz])]__underline"></div>` },
  278. ],
  279. corePlugins: { preflight: false },
  280. }
  281. let input = css`
  282. @tailwind base;
  283. @tailwind components;
  284. @tailwind utilities;
  285. `
  286. return run(input, config).then((result) => {
  287. expect(result.css).toMatchFormattedCss(css`
  288. ${defaults}
  289. .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]__underline[data-foo][data-bar]:not([data-baz]) {
  290. text-decoration-line: underline;
  291. }
  292. `)
  293. })
  294. })
  295. test('multiple attribute selectors with custom separator (2)', () => {
  296. let config = {
  297. separator: '_@',
  298. content: [
  299. { raw: html`<div class="[&[data-foo][data-bar]:not([data-baz])]_@underline"></div>` },
  300. ],
  301. corePlugins: { preflight: false },
  302. }
  303. let input = css`
  304. @tailwind base;
  305. @tailwind components;
  306. @tailwind utilities;
  307. `
  308. return run(input, config).then((result) => {
  309. expect(result.css).toMatchFormattedCss(css`
  310. ${defaults}
  311. .\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]_\@underline[data-foo][data-bar]:not([data-baz]) {
  312. text-decoration-line: underline;
  313. }
  314. `)
  315. })
  316. })
  317. test('with @apply', () => {
  318. let config = {
  319. content: [
  320. {
  321. raw: html`<div class="foo"></div>`,
  322. },
  323. ],
  324. corePlugins: { preflight: false },
  325. }
  326. let input = `
  327. @tailwind base;
  328. @tailwind components;
  329. @tailwind utilities;
  330. .foo {
  331. @apply [@media_screen{@media(hover:hover){&:hover}}]:underline;
  332. }
  333. `
  334. return run(input, config).then((result) => {
  335. expect(result.css).toMatchFormattedCss(css`
  336. ${defaults}
  337. @media screen {
  338. @media (hover: hover) {
  339. .foo:hover {
  340. text-decoration-line: underline;
  341. }
  342. }
  343. }
  344. `)
  345. })
  346. })
  347. test('keeps escaped underscores', () => {
  348. let config = {
  349. content: [
  350. {
  351. raw: '<div class="[&_.foo\\_\\_bar]:underline"></div>',
  352. },
  353. ],
  354. corePlugins: { preflight: false },
  355. }
  356. let input = css`
  357. @tailwind base;
  358. @tailwind components;
  359. @tailwind utilities;
  360. `
  361. return run(input, config).then((result) => {
  362. expect(result.css).toMatchFormattedCss(css`
  363. ${defaults}
  364. .\[\&_\.foo\\_\\_bar\]\:underline .foo__bar {
  365. text-decoration-line: underline;
  366. }
  367. `)
  368. })
  369. })
  370. test('keeps escaped underscores with multiple arbitrary variants', () => {
  371. let config = {
  372. content: [
  373. {
  374. raw: '<div class="[&_.foo\\_\\_bar]:[&_.bar\\_\\_baz]:underline"></div>',
  375. },
  376. ],
  377. corePlugins: { preflight: false },
  378. }
  379. let input = css`
  380. @tailwind base;
  381. @tailwind components;
  382. @tailwind utilities;
  383. `
  384. return run(input, config).then((result) => {
  385. expect(result.css).toMatchFormattedCss(css`
  386. ${defaults}
  387. .\[\&_\.foo\\_\\_bar\]\:\[\&_\.bar\\_\\_baz\]\:underline .bar__baz .foo__bar {
  388. text-decoration-line: underline;
  389. }
  390. `)
  391. })
  392. })
  393. test('keeps escaped underscores in arbitrary variants mixed with normal variants', () => {
  394. let config = {
  395. content: [
  396. {
  397. raw: `
  398. <div class="[&_.foo\\_\\_bar]:hover:underline"></div>
  399. <div class="hover:[&_.foo\\_\\_bar]:underline"></div>
  400. `,
  401. },
  402. ],
  403. corePlugins: { preflight: false },
  404. }
  405. let input = css`
  406. @tailwind base;
  407. @tailwind components;
  408. @tailwind utilities;
  409. `
  410. return run(input, config).then((result) => {
  411. expect(result.css).toMatchFormattedCss(css`
  412. ${defaults}
  413. .\[\&_\.foo\\_\\_bar\]\:hover\:underline:hover .foo__bar,
  414. .hover\:\[\&_\.foo\\_\\_bar\]\:underline .foo__bar:hover {
  415. text-decoration-line: underline;
  416. }
  417. `)
  418. })
  419. })
  420. test('allows attribute variants with quotes', () => {
  421. let config = {
  422. content: [
  423. {
  424. raw: `
  425. <div class="[&[data-test='2']]:underline"></div>
  426. <div class='[&[data-test="2"]]:underline'></div>
  427. `,
  428. },
  429. ],
  430. corePlugins: { preflight: false },
  431. }
  432. let input = css`
  433. @tailwind base;
  434. @tailwind components;
  435. @tailwind utilities;
  436. `
  437. return run(input, config).then((result) => {
  438. expect(result.css).toMatchFormattedCss(css`
  439. ${defaults}
  440. .\[\&\[data-test\=\"2\"\]\]\:underline[data-test='2'],
  441. .\[\&\[data-test\=\'2\'\]\]\:underline[data-test='2'] {
  442. text-decoration-line: underline;
  443. }
  444. `)
  445. })
  446. })
  447. test('classes in arbitrary variants should not be prefixed', () => {
  448. let config = {
  449. prefix: 'tw-',
  450. content: [
  451. {
  452. raw: `
  453. <div class="[.foo_&]:tw-text-red-400">should not be red</div>
  454. <div class="foo">
  455. <div class="[.foo_&]:tw-text-red-400">should be red</div>
  456. </div>
  457. <div class="[&_.foo]:tw-text-red-400">
  458. <div>should not be red</div>
  459. <div class="foo">should be red</div>
  460. </div>
  461. <div class="hover:[&_.foo]:tw-text-red-400">
  462. <div>should not be red</div>
  463. <div class="foo">should be red</div>
  464. </div>
  465. <div class="[&_.foo]:hover:tw-text-red-400">
  466. <div>should not be red</div>
  467. <div class="foo">should be red</div>
  468. </div>
  469. `,
  470. },
  471. ],
  472. corePlugins: { preflight: false },
  473. }
  474. let input = css`
  475. @tailwind utilities;
  476. `
  477. return run(input, config).then((result) => {
  478. expect(result.css).toMatchFormattedCss(css`
  479. .\[\&_\.foo\]\:tw-text-red-400 .foo,
  480. .\[\&_\.foo\]\:hover\:tw-text-red-400:hover .foo,
  481. .hover\:\[\&_\.foo\]\:tw-text-red-400 .foo:hover,
  482. .foo .\[\.foo_\&\]\:tw-text-red-400 {
  483. --tw-text-opacity: 1;
  484. color: rgb(248 113 113 / var(--tw-text-opacity));
  485. }
  486. `)
  487. })
  488. })
  489. test('classes in the same arbitrary variant should not be prefixed', () => {
  490. let config = {
  491. prefix: 'tw-',
  492. content: [
  493. {
  494. raw: `
  495. <div class="[.foo_&]:tw-font-bold">should not be red</div>
  496. <div class="foo">
  497. <div class="[.foo_&]:tw-font-bold">should be red</div>
  498. </div>
  499. <div class="[&_.foo]:tw-font-bold [&_.foo]:tw-font-bold">
  500. <div>should not be red</div>
  501. <div class="foo">should be red</div>
  502. </div>
  503. `,
  504. },
  505. ],
  506. corePlugins: { preflight: false },
  507. }
  508. let input = css`
  509. @tailwind utilities;
  510. `
  511. return run(input, config).then((result) => {
  512. expect(result.css).toMatchFormattedCss(css`
  513. .\[\&_\.foo\]\:tw-font-bold .foo,
  514. .foo .\[\.foo_\&\]\:tw-font-bold {
  515. font-weight: 700;
  516. }
  517. `)
  518. })
  519. })
  520. it('should support aria variants', () => {
  521. let config = {
  522. content: [
  523. {
  524. raw: html`
  525. <div>
  526. <div class="aria-checked:underline"></div>
  527. <div class="aria-[sort=ascending]:underline"></div>
  528. <div class="aria-[labelledby='a_b']:underline"></div>
  529. <div class="group-aria-checked:underline"></div>
  530. <div class="peer-aria-checked:underline"></div>
  531. <div class="group-aria-checked/foo:underline"></div>
  532. <div class="peer-aria-checked/foo:underline"></div>
  533. <div class="group-aria-[sort=ascending]:underline"></div>
  534. <div class="peer-aria-[sort=ascending]:underline"></div>
  535. <div class="group-aria-[labelledby='a_b']:underline"></div>
  536. <div class="peer-aria-[labelledby='a_b']:underline"></div>
  537. <div class="group-aria-[sort=ascending]/foo:underline"></div>
  538. <div class="peer-aria-[sort=ascending]/foo:underline"></div>
  539. </div>
  540. `,
  541. },
  542. ],
  543. corePlugins: { preflight: false },
  544. }
  545. let input = css`
  546. @tailwind utilities;
  547. `
  548. return run(input, config).then((result) => {
  549. expect(result.css).toMatchFormattedCss(
  550. flagEnabled(config, 'oxideParser')
  551. ? css`
  552. .aria-checked\:underline[aria-checked='true'],
  553. .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'],
  554. .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'],
  555. .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline,
  556. .group[aria-checked='true'] .group-aria-checked\:underline,
  557. .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline,
  558. .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline,
  559. .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline,
  560. .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline,
  561. .peer[aria-checked='true'] ~ .peer-aria-checked\:underline,
  562. .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline,
  563. .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline,
  564. .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline {
  565. text-decoration-line: underline;
  566. }
  567. `
  568. : css`
  569. .underline,
  570. .aria-checked\:underline[aria-checked='true'],
  571. .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'],
  572. .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'],
  573. .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline,
  574. .group[aria-checked='true'] .group-aria-checked\:underline,
  575. .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline,
  576. .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline,
  577. .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline,
  578. .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline,
  579. .peer[aria-checked='true'] ~ .peer-aria-checked\:underline,
  580. .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline,
  581. .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline,
  582. .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline {
  583. text-decoration-line: underline;
  584. }
  585. `
  586. )
  587. })
  588. })
  589. it('should support data variants', () => {
  590. let config = {
  591. theme: {
  592. data: {
  593. checked: 'ui~="checked"',
  594. },
  595. },
  596. content: [
  597. {
  598. raw: html`
  599. <div>
  600. <div class="data-checked:underline"></div>
  601. <div class="data-[position=top]:underline"></div>
  602. <div class="data-[foo='bar_baz']:underline"></div>
  603. <div class="group-data-checked:underline"></div>
  604. <div class="peer-data-checked:underline"></div>
  605. <div class="group-data-checked/foo:underline"></div>
  606. <div class="peer-data-checked/foo:underline"></div>
  607. <div class="group-data-[position=top]:underline"></div>
  608. <div class="peer-data-[position=top]:underline"></div>
  609. <div class="group-data-[foo='bar_baz']:underline"></div>
  610. <div class="peer-data-[foo='bar_baz']:underline"></div>
  611. <div class="group-data-[position=top]/foo:underline"></div>
  612. <div class="peer-data-[position=top]/foo:underline"></div>
  613. </div>
  614. `,
  615. },
  616. ],
  617. corePlugins: { preflight: false },
  618. }
  619. let input = css`
  620. @tailwind utilities;
  621. `
  622. return run(input, config).then((result) => {
  623. expect(result.css).toMatchFormattedCss(
  624. flagEnabled(config, 'oxideParser')
  625. ? css`
  626. .data-checked\:underline[data-ui~='checked'],
  627. .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'],
  628. .data-\[position\=top\]\:underline[data-position='top'],
  629. .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline,
  630. .group[data-ui~='checked'] .group-data-checked\:underline,
  631. .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline,
  632. .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline,
  633. .group[data-position='top'] .group-data-\[position\=top\]\:underline,
  634. .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline,
  635. .peer[data-ui~='checked'] ~ .peer-data-checked\:underline,
  636. .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline,
  637. .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline,
  638. .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline {
  639. text-decoration-line: underline;
  640. }
  641. `
  642. : css`
  643. .underline,
  644. .data-checked\:underline[data-ui~='checked'],
  645. .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'],
  646. .data-\[position\=top\]\:underline[data-position='top'],
  647. .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline,
  648. .group[data-ui~='checked'] .group-data-checked\:underline,
  649. .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline,
  650. .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline,
  651. .group[data-position='top'] .group-data-\[position\=top\]\:underline,
  652. .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline,
  653. .peer[data-ui~='checked'] ~ .peer-data-checked\:underline,
  654. .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline,
  655. .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline,
  656. .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline {
  657. text-decoration-line: underline;
  658. }
  659. `
  660. )
  661. })
  662. })
  663. it('should support supports', () => {
  664. let config = {
  665. theme: {
  666. supports: {
  667. grid: 'display: grid',
  668. },
  669. },
  670. content: [
  671. {
  672. raw: html`
  673. <div>
  674. <!-- Property check -->
  675. <div class="supports-[display:grid]:grid"></div>
  676. <!-- Value with spaces, needs to be normalized -->
  677. <div class="supports-[transform-origin:5%_5%]:underline"></div>
  678. <!-- Selectors (raw) -->
  679. <div class="supports-[selector(A_>_B)]:underline"></div>
  680. <!-- 'not' check (raw) -->
  681. <div class="supports-[not(foo:bar)]:underline"></div>
  682. <!-- 'or' check (raw) -->
  683. <div class="supports-[(foo:bar)or(bar:baz)]:underline"></div>
  684. <!-- 'and' check (raw) -->
  685. <div class="supports-[(foo:bar)and(bar:baz)]:underline"></div>
  686. <!-- No value give for the property, defaulting to prop: var(--tw) -->
  687. <div class="supports-[container-type]:underline"></div>
  688. <!-- Named supports usage -->
  689. <div class="supports-grid:underline"></div>
  690. </div>
  691. `,
  692. },
  693. ],
  694. corePlugins: { preflight: false },
  695. }
  696. let input = css`
  697. @tailwind utilities;
  698. `
  699. return run(input, config).then((result) => {
  700. expect(result.css).toMatchFormattedCss(css`
  701. @supports (display: grid) {
  702. .supports-grid\:underline {
  703. text-decoration-line: underline;
  704. }
  705. .supports-\[display\:grid\]\:grid {
  706. display: grid;
  707. }
  708. }
  709. @supports (foo: bar) and (bar: baz) {
  710. .supports-\[\(foo\:bar\)and\(bar\:baz\)\]\:underline {
  711. text-decoration-line: underline;
  712. }
  713. }
  714. @supports (foo: bar) or (bar: baz) {
  715. .supports-\[\(foo\:bar\)or\(bar\:baz\)\]\:underline {
  716. text-decoration-line: underline;
  717. }
  718. }
  719. @supports (container-type: var(--tw)) {
  720. .supports-\[container-type\]\:underline {
  721. text-decoration-line: underline;
  722. }
  723. }
  724. @supports not (foo: bar) {
  725. .supports-\[not\(foo\:bar\)\]\:underline {
  726. text-decoration-line: underline;
  727. }
  728. }
  729. @supports selector(A > B) {
  730. .supports-\[selector\(A_\>_B\)\]\:underline {
  731. text-decoration-line: underline;
  732. }
  733. }
  734. @supports (transform-origin: 5% 5%) {
  735. .supports-\[transform-origin\:5\%_5\%\]\:underline {
  736. text-decoration-line: underline;
  737. }
  738. }
  739. `)
  740. })
  741. })
  742. test('has-* variants with arbitrary values', () => {
  743. let config = {
  744. theme: {},
  745. content: [
  746. {
  747. raw: html`
  748. <div>
  749. <figure class="has-[figcaption]:inline-block"></figure>
  750. <div class="has-[.foo]:flex"></div>
  751. <div class="has-[.foo:hover]:block"></div>
  752. <div class="has-[[data-active]]:inline"></div>
  753. <div class="has-[>_.potato]:table"></div>
  754. <div class="has-[+_h2]:grid"></div>
  755. <div class="has-[>_h1_+_h2]:contents"></div>
  756. <div class="has-[h2]:has-[.banana]:hidden"></div>
  757. </div>
  758. `,
  759. },
  760. ],
  761. corePlugins: { preflight: false },
  762. }
  763. let input = css`
  764. @tailwind utilities;
  765. `
  766. return run(input, config).then((result) => {
  767. expect(result.css).toMatchFormattedCss(css`
  768. .has-\[\.foo\:hover\]\:block:has(.foo:hover) {
  769. display: block;
  770. }
  771. .has-\[figcaption\]\:inline-block:has(figcaption) {
  772. display: inline-block;
  773. }
  774. .has-\[\[data-active\]\]\:inline:has([data-active]) {
  775. display: inline;
  776. }
  777. .has-\[\.foo\]\:flex:has(.foo) {
  778. display: flex;
  779. }
  780. .has-\[\>_\.potato\]\:table:has(> .potato) {
  781. display: table;
  782. }
  783. .has-\[\+_h2\]\:grid:has(+ h2) {
  784. display: grid;
  785. }
  786. .has-\[\>_h1_\+_h2\]\:contents:has(> h1 + h2) {
  787. display: contents;
  788. }
  789. .has-\[h2\]\:has-\[\.banana\]\:hidden:has(.banana):has(h2) {
  790. display: none;
  791. }
  792. `)
  793. })
  794. })
  795. test('group-has-* variants with arbitrary values', () => {
  796. let config = {
  797. theme: {},
  798. content: [
  799. {
  800. raw: html`
  801. <div class="group">
  802. <div class="group-has-[>_h1_+_.foo]:block"></div>
  803. </div>
  804. <div class="group/two">
  805. <div class="group-has-[>_h1_+_.foo]/two:flex"></div>
  806. </div>
  807. `,
  808. },
  809. ],
  810. corePlugins: { preflight: false },
  811. }
  812. let input = css`
  813. @tailwind utilities;
  814. `
  815. return run(input, config).then((result) => {
  816. expect(result.css).toMatchFormattedCss(css`
  817. .group:has(> h1 + .foo) .group-has-\[\>_h1_\+_\.foo\]\:block {
  818. display: block;
  819. }
  820. .group\/two:has(> h1 + .foo) .group-has-\[\>_h1_\+_\.foo\]\/two\:flex {
  821. display: flex;
  822. }
  823. `)
  824. })
  825. })
  826. test('peer-has-* variants with arbitrary values', () => {
  827. let config = {
  828. theme: {},
  829. content: [
  830. {
  831. raw: html`
  832. <div>
  833. <div className="peer"></div>
  834. <div class="peer-has-[>_h1_+_.foo]:block"></div>
  835. </div>
  836. <div>
  837. <div className="peer"></div>
  838. <div class="peer-has-[>_h1_+_.foo]/two:flex"></div>
  839. </div>
  840. `,
  841. },
  842. ],
  843. corePlugins: { preflight: false },
  844. }
  845. let input = css`
  846. @tailwind utilities;
  847. `
  848. return run(input, config).then((result) => {
  849. expect(result.css).toMatchFormattedCss(css`
  850. .peer:has(> h1 + .foo) ~ .peer-has-\[\>_h1_\+_\.foo\]\:block {
  851. display: block;
  852. }
  853. .peer\/two:has(> h1 + .foo) ~ .peer-has-\[\>_h1_\+_\.foo\]\/two\:flex {
  854. display: flex;
  855. }
  856. `)
  857. })
  858. })
  859. it('should be possible to use modifiers and arbitrary groups', () => {
  860. let config = {
  861. content: [
  862. {
  863. raw: html`
  864. <div>
  865. <div class="group">
  866. <!-- Default group usage -->
  867. <div class="group-hover:underline"></div>
  868. <!-- Arbitrary variants with pseudo class for group -->
  869. <!-- With & -->
  870. <div class="group-[&:focus]:underline"></div>
  871. <!-- Without & -->
  872. <div class="group-[:hover]:underline"></div>
  873. <!-- Arbitrary variants with attributes selectors for group -->
  874. <!-- With & -->
  875. <div class="group-[&[data-open]]:underline"></div>
  876. <!-- Without & -->
  877. <div class="group-[[data-open]]:underline"></div>
  878. <!-- Arbitrary variants with other selectors -->
  879. <!-- With & -->
  880. <div class="group-[.in-foo_&]:underline"></div>
  881. <!-- Without & -->
  882. <div class="group-[.in-foo]:underline"></div>
  883. </div>
  884. <!-- The same as above, but with modifiers -->
  885. <div class="group/foo">
  886. <div class="group-hover/foo:underline"></div>
  887. <div class="group-[&:focus]/foo:underline"></div>
  888. <div class="group-[:hover]/foo:underline"></div>
  889. <div class="group-[&[data-open]]/foo:underline"></div>
  890. <div class="group-[[data-open]]/foo:underline"></div>
  891. <div class="group-[.in-foo_&]/foo:underline"></div>
  892. <div class="group-[.in-foo]/foo:underline"></div>
  893. </div>
  894. </div>
  895. `,
  896. },
  897. ],
  898. corePlugins: { preflight: false },
  899. }
  900. let input = css`
  901. @tailwind utilities;
  902. `
  903. return run(input, config).then((result) => {
  904. expect(result.css).toMatchFormattedCss(css`
  905. .group\/foo:hover .group-hover\/foo\:underline,
  906. .group:hover .group-hover\:underline,
  907. .group\/foo:focus .group-\[\&\:focus\]\/foo\:underline,
  908. .group:focus .group-\[\&\:focus\]\:underline,
  909. .group\/foo[data-open] .group-\[\&\[data-open\]\]\/foo\:underline,
  910. .group[data-open] .group-\[\&\[data-open\]\]\:underline,
  911. .group\/foo.in-foo .group-\[\.in-foo\]\/foo\:underline,
  912. .group.in-foo .group-\[\.in-foo\]\:underline,
  913. .in-foo .group\/foo .group-\[\.in-foo_\&\]\/foo\:underline,
  914. .in-foo .group .group-\[\.in-foo_\&\]\:underline,
  915. .group\/foo:hover .group-\[\:hover\]\/foo\:underline,
  916. .group:hover .group-\[\:hover\]\:underline,
  917. .group\/foo[data-open] .group-\[\[data-open\]\]\/foo\:underline,
  918. .group[data-open] .group-\[\[data-open\]\]\:underline {
  919. text-decoration-line: underline;
  920. }
  921. `)
  922. })
  923. })
  924. it('should be possible to use modifiers and arbitrary peers', () => {
  925. let config = {
  926. content: [
  927. {
  928. raw: html`
  929. <div>
  930. <div class="peer"></div>
  931. <!-- Default peer usage -->
  932. <div class="peer-hover:underline"></div>
  933. <!-- Arbitrary variants with pseudo class for peer -->
  934. <!-- With & -->
  935. <div class="peer-[&:focus]:underline"></div>
  936. <!-- Without & -->
  937. <div class="peer-[:hover]:underline"></div>
  938. <!-- Arbitrary variants with attributes selectors for peer -->
  939. <!-- With & -->
  940. <div class="peer-[&[data-open]]:underline"></div>
  941. <!-- Without & -->
  942. <div class="peer-[[data-open]]:underline"></div>
  943. <!-- Arbitrary variants with other selectors -->
  944. <!-- With & -->
  945. <div class="peer-[.in-foo_&]:underline"></div>
  946. <!-- Without & -->
  947. <div class="peer-[.in-foo]:underline"></div>
  948. <!-- The same as above, but with modifiers -->
  949. <div class="peer/foo"></div>
  950. <div class="peer-hover/foo:underline"></div>
  951. <div class="peer-[&:focus]/foo:underline"></div>
  952. <div class="peer-[:hover]/foo:underline"></div>
  953. <div class="peer-[&[data-open]]/foo:underline"></div>
  954. <div class="peer-[[data-open]]/foo:underline"></div>
  955. <div class="peer-[.in-foo_&]/foo:underline"></div>
  956. <div class="peer-[.in-foo]/foo:underline"></div>
  957. </div>
  958. `,
  959. },
  960. ],
  961. corePlugins: { preflight: false },
  962. }
  963. let input = css`
  964. @tailwind utilities;
  965. `
  966. return run(input, config).then((result) => {
  967. expect(result.css).toMatchFormattedCss(css`
  968. .peer\/foo:hover ~ .peer-hover\/foo\:underline,
  969. .peer:hover ~ .peer-hover\:underline,
  970. .peer\/foo:focus ~ .peer-\[\&\:focus\]\/foo\:underline,
  971. .peer:focus ~ .peer-\[\&\:focus\]\:underline,
  972. .peer\/foo[data-open] ~ .peer-\[\&\[data-open\]\]\/foo\:underline,
  973. .peer[data-open] ~ .peer-\[\&\[data-open\]\]\:underline,
  974. .peer\/foo.in-foo ~ .peer-\[\.in-foo\]\/foo\:underline,
  975. .peer.in-foo ~ .peer-\[\.in-foo\]\:underline,
  976. .in-foo .peer\/foo ~ .peer-\[\.in-foo_\&\]\/foo\:underline,
  977. .in-foo .peer ~ .peer-\[\.in-foo_\&\]\:underline,
  978. .peer\/foo:hover ~ .peer-\[\:hover\]\/foo\:underline,
  979. .peer:hover ~ .peer-\[\:hover\]\:underline,
  980. .peer\/foo[data-open] ~ .peer-\[\[data-open\]\]\/foo\:underline,
  981. .peer[data-open] ~ .peer-\[\[data-open\]\]\:underline {
  982. text-decoration-line: underline;
  983. }
  984. `)
  985. })
  986. })
  987. it('Arbitrary variants are ordered alphabetically', () => {
  988. let config = {
  989. content: [
  990. {
  991. raw: html`
  992. <div>
  993. <div class="[&::b]:underline"></div>
  994. <div class="[&::a]:underline"></div>
  995. <div class="[&::c]:underline"></div>
  996. <div class="[&::b]:underline"></div>
  997. </div>
  998. `,
  999. },
  1000. ],
  1001. corePlugins: { preflight: false },
  1002. }
  1003. let input = css`
  1004. @tailwind utilities;
  1005. `
  1006. return run(input, config).then((result) => {
  1007. expect(result.css).toMatchFormattedCss(css`
  1008. .\[\&\:\:a\]\:underline::a {
  1009. text-decoration-line: underline;
  1010. }
  1011. .\[\&\:\:b\]\:underline::b {
  1012. text-decoration-line: underline;
  1013. }
  1014. .\[\&\:\:c\]\:underline::c {
  1015. text-decoration-line: underline;
  1016. }
  1017. `)
  1018. })
  1019. })
  1020. it('Arbitrary variants support multiple attribute selectors', () => {
  1021. let config = {
  1022. content: [
  1023. {
  1024. raw: html` <div class="[[data-foo='bar'][data-baz]_&]:underline"></div> `,
  1025. },
  1026. ],
  1027. corePlugins: { preflight: false },
  1028. }
  1029. let input = css`
  1030. @tailwind utilities;
  1031. `
  1032. return run(input, config).then((result) => {
  1033. expect(result.css).toMatchFormattedCss(css`
  1034. [data-foo='bar'][data-baz] .\[\[data-foo\=\'bar\'\]\[data-baz\]_\&\]\:underline {
  1035. text-decoration-line: underline;
  1036. }
  1037. `)
  1038. })
  1039. })
  1040. it('Invalid arbitrary variants selectors should produce nothing instead of failing', () => {
  1041. let config = {
  1042. content: [
  1043. {
  1044. raw: html`
  1045. <div class="[&;foo]:underline"></div>
  1046. `,
  1047. },
  1048. ],
  1049. corePlugins: { preflight: false },
  1050. }
  1051. let input = css`
  1052. @tailwind utilities;
  1053. `
  1054. return run(input, config).then((result) => {
  1055. expect(result.css).toMatchFormattedCss(css``)
  1056. })
  1057. })
  1058. it('should output responsive variants + stacked variants in the right order', () => {
  1059. let config = {
  1060. content: [
  1061. {
  1062. raw: html`
  1063. <div class="xl:p-1"></div>
  1064. <div class="md:[&_ul]:flex-row"></div>
  1065. <div class="[&_ul]:flex"></div>
  1066. <div class="[&_ul]:flex-col"></div>
  1067. `,
  1068. },
  1069. ],
  1070. corePlugins: { preflight: false },
  1071. }
  1072. let input = css`
  1073. @tailwind utilities;
  1074. `
  1075. return run(input, config).then((result) => {
  1076. expect(result.css).toMatchFormattedCss(css`
  1077. @media (min-width: 1280px) {
  1078. .xl\:p-1 {
  1079. padding: 0.25rem;
  1080. }
  1081. }
  1082. .\[\&_ul\]\:flex ul {
  1083. display: flex;
  1084. }
  1085. .\[\&_ul\]\:flex-col ul {
  1086. flex-direction: column;
  1087. }
  1088. @media (min-width: 768px) {
  1089. .md\:\[\&_ul\]\:flex-row ul {
  1090. flex-direction: row;
  1091. }
  1092. }
  1093. `)
  1094. })
  1095. })
  1096. it('it should discard arbitrary variants with multiple selectors', () => {
  1097. let config = {
  1098. content: [
  1099. {
  1100. raw: html`
  1101. <div class="p-1"></div>
  1102. <div class="[div]:p-1"></div>
  1103. <div class="[div_&]:p-1"></div>
  1104. <div class="[div,span]:p-1"></div>
  1105. <div class="[div_&,span]:p-1"></div>
  1106. <div class="[div,span_&]:p-1"></div>
  1107. <div class="[div_&,span_&]:p-1"></div>
  1108. <div class="hover:[div]:p-1"></div>
  1109. <div class="hover:[div_&]:p-1"></div>
  1110. <div class="hover:[div,span]:p-1"></div>
  1111. <div class="hover:[div_&,span]:p-1"></div>
  1112. <div class="hover:[div,span_&]:p-1"></div>
  1113. <div class="hover:[div_&,span_&]:p-1"></div>
  1114. <div class="hover:[:is(span,div)_&]:p-1"></div>
  1115. `,
  1116. },
  1117. {
  1118. // escaped commas are a-ok
  1119. // This is separate because prettier complains about `\,` in the template string
  1120. raw: '<div class="hover:[.span\\,div_&]:p-1"></div>',
  1121. },
  1122. ],
  1123. corePlugins: { preflight: false },
  1124. }
  1125. let input = css`
  1126. @tailwind utilities;
  1127. `
  1128. return run(input, config).then((result) => {
  1129. expect(result.css).toMatchFormattedCss(css`
  1130. .p-1,
  1131. .span\,div .hover\:\[\.span\\\,div_\&\]\:p-1:hover,
  1132. :is(span, div) .hover\:\[\:is\(span\,div\)_\&\]\:p-1:hover,
  1133. div .\[div_\&\]\:p-1,
  1134. div .hover\:\[div_\&\]\:p-1:hover {
  1135. padding: 0.25rem;
  1136. }
  1137. `)
  1138. })
  1139. })
  1140. it('should sort multiple variant fns with normal variants between them', () => {
  1141. /** @type {string[]} */
  1142. let lines = []
  1143. for (let a of [1, 2]) {
  1144. for (let b of [2, 1]) {
  1145. for (let c of [1, 2]) {
  1146. for (let d of [2, 1]) {
  1147. for (let e of [1, 2]) {
  1148. lines.push(`<div class="fred${a}:qux-[${b}]:baz${c}:bar-[${d}]:foo${e}:p-1"></div>`)
  1149. }
  1150. }
  1151. }
  1152. }
  1153. }
  1154. // Fisher-Yates shuffle
  1155. for (let i = lines.length - 1; i > 0; i--) {
  1156. let j = Math.floor(Math.random() * i)
  1157. ;[lines[i], lines[j]] = [lines[j], lines[i]]
  1158. }
  1159. let config = {
  1160. content: [
  1161. {
  1162. raw: lines.join('\n'),
  1163. },
  1164. ],
  1165. corePlugins: { preflight: false },
  1166. plugins: [
  1167. function ({ addVariant, matchVariant }) {
  1168. addVariant('foo1', "&[data-foo='1']")
  1169. addVariant('foo2', "&[data-foo='2']")
  1170. matchVariant('bar', (value) => `&[data-bar='${value}']`, {
  1171. sort: (a, b) => b.value - a.value,
  1172. })
  1173. addVariant('baz1', "&[data-baz='1']")
  1174. addVariant('baz2', "&[data-baz='2']")
  1175. matchVariant('qux', (value) => `&[data-qux='${value}']`, {
  1176. sort: (a, b) => b.value - a.value,
  1177. })
  1178. addVariant('fred1', "&[data-fred='1']")
  1179. addVariant('fred2', "&[data-fred='2']")
  1180. },
  1181. ],
  1182. }
  1183. let input = css`
  1184. @tailwind utilities;
  1185. `
  1186. return run(input, config).then((result) => {
  1187. expect(result.css).toMatchFormattedCss(css`
  1188. .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'],
  1189. .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'],
  1190. .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'],
  1191. .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'],
  1192. .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'],
  1193. .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'],
  1194. .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'],
  1195. .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'],
  1196. .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'],
  1197. .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'],
  1198. .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'],
  1199. .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'],
  1200. .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'],
  1201. .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'],
  1202. .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'],
  1203. .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'],
  1204. .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'],
  1205. .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'],
  1206. .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'],
  1207. .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'],
  1208. .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'],
  1209. .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'],
  1210. .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'],
  1211. .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'],
  1212. .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'],
  1213. .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'],
  1214. .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'],
  1215. .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'],
  1216. .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'],
  1217. .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'],
  1218. .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'],
  1219. .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] {
  1220. padding: 0.25rem;
  1221. }
  1222. `)
  1223. })
  1224. })