arbitrary-values.test.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. import fs from 'fs'
  2. import path from 'path'
  3. import { run, html, css } from './util/run'
  4. test('arbitrary values', () => {
  5. let config = {
  6. content: [path.resolve(__dirname, './arbitrary-values.test.html')],
  7. }
  8. return run('@tailwind utilities', config).then((result) => {
  9. expect(result.css).toMatchFormattedCss(
  10. fs.readFileSync(path.resolve(__dirname, './arbitrary-values.test.css'), 'utf8')
  11. )
  12. })
  13. })
  14. // TODO: Currently Lightning CSS will throw an error when trying to print the invalid CSS, whereas
  15. // before it generated invalid CSS without throwing an error.
  16. //
  17. // In perfect world, we would not generate anything, and potentially show a warning.
  18. test('arbitrary values that result in invalid CSS should not be generated', () => {
  19. let config = {
  20. content: [
  21. {
  22. raw: html`<div class="w-full w-[{}] w-[{{}}] [--custom:{}]"></div>`,
  23. },
  24. ],
  25. }
  26. // NOTE: The version with braces are invalid and therefore produce nothing
  27. return run('@tailwind utilities', config).then((result) => {
  28. // Required because it tries to reformat the {}
  29. // prettier-ignore
  30. return expect(result.css).toMatchFormattedCss(css`
  31. .w-full {
  32. width: 100%;
  33. }
  34. .\[--custom\:\{\}\] {
  35. --custom: {}
  36. }
  37. `)
  38. })
  39. })
  40. test('should only detect classes with arbitrary values that are properly terminated after the arbitrary value', () => {
  41. let config = {
  42. content: [
  43. {
  44. raw: html`<div class="w-[do-not-generate-this]w-[it-is-invalid-syntax]"></div>`,
  45. },
  46. ],
  47. }
  48. return run('@tailwind utilities', config).then((result) => {
  49. return expect(result.css).toMatchFormattedCss(css``)
  50. })
  51. })
  52. it('should be possible to differentiate between decoration utilities', () => {
  53. let config = {
  54. content: [
  55. {
  56. raw: html`<div class="decoration-[#ccc] decoration-[3px]"></div>`,
  57. },
  58. ],
  59. }
  60. return run('@tailwind utilities', config).then((result) => {
  61. return expect(result.css).toMatchFormattedCss(css`
  62. .decoration-\[\#ccc\] {
  63. text-decoration-color: #ccc;
  64. }
  65. .decoration-\[3px\] {
  66. text-decoration-thickness: 3px;
  67. }
  68. `)
  69. })
  70. })
  71. it('should support modifiers for arbitrary values that contain the separator', () => {
  72. let config = {
  73. content: [
  74. {
  75. raw: html`<div class="hover:bg-[url('https://github.com/tailwindlabs.png')]"></div>`,
  76. },
  77. ],
  78. }
  79. return run('@tailwind utilities', config).then((result) => {
  80. return expect(result.css).toMatchFormattedCss(css`
  81. .hover\:bg-\[url\(\'https\:\/\/github\.com\/tailwindlabs\.png\'\)\]:hover {
  82. background-image: url('https://github.com/tailwindlabs.png');
  83. }
  84. `)
  85. })
  86. })
  87. it('should support arbitrary values for various background utilities', () => {
  88. let config = {
  89. content: [
  90. {
  91. raw: html`
  92. <!-- Lookup -->
  93. <div class="bg-gradient-to-r"></div>
  94. <div class="bg-red-500"></div>
  95. <!-- By implicit type -->
  96. <div class="bg-[url('/image-1-0.png')]"></div>
  97. <div class="bg-[#ff0000]"></div>
  98. <div class="bg-[rgb(var(--bg-color))]"></div>
  99. <div class="bg-[hsl(var(--bg-color))]"></div>
  100. <!-- By explicit type -->
  101. <div class="bg-[url:var(--image-url)]"></div>
  102. <div class="bg-[color:var(--bg-color)]"></div>
  103. `,
  104. },
  105. ],
  106. }
  107. return run('@tailwind utilities', config).then((result) => {
  108. expect(result.css).toMatchFormattedCss(css`
  109. .bg-\[\#ff0000\] {
  110. --tw-bg-opacity: 1;
  111. background-color: rgb(255 0 0 / var(--tw-bg-opacity));
  112. }
  113. .bg-\[color\:var\(--bg-color\)\] {
  114. background-color: var(--bg-color);
  115. }
  116. .bg-\[hsl\(var\(--bg-color\)\)\] {
  117. background-color: hsl(var(--bg-color));
  118. }
  119. .bg-\[rgb\(var\(--bg-color\)\)\] {
  120. background-color: rgb(var(--bg-color));
  121. }
  122. .bg-red-500 {
  123. --tw-bg-opacity: 1;
  124. background-color: rgb(239 68 68 / var(--tw-bg-opacity));
  125. }
  126. .bg-\[url\(\'\/image-1-0\.png\'\)\] {
  127. background-image: url('/image-1-0.png');
  128. }
  129. .bg-\[url\:var\(--image-url\)\] {
  130. background-image: var(--image-url);
  131. }
  132. .bg-gradient-to-r {
  133. background-image: linear-gradient(to right, var(--tw-gradient-stops));
  134. }
  135. `)
  136. })
  137. })
  138. it('should not generate any css if an unknown typehint is used', () => {
  139. let config = {
  140. content: [
  141. {
  142. raw: html`<div class="inset-[hmm:12px]"></div>`,
  143. },
  144. ],
  145. }
  146. return run('@tailwind utilities', config).then((result) => {
  147. return expect(result.css).toMatchFormattedCss(css``)
  148. })
  149. })
  150. it('should handle unknown typehints', () => {
  151. let config = { content: [{ raw: html`<div class="w-[length:12px]"></div>` }] }
  152. return run('@tailwind utilities', config).then((result) => {
  153. return expect(result.css).toMatchFormattedCss(css`
  154. .w-\[length\:12px\] {
  155. width: 12px;
  156. }
  157. `)
  158. })
  159. })
  160. it('should convert _ to spaces', () => {
  161. // Using custom css function here, because otherwise with String.raw, we run
  162. // into an issue with `\2c ` escapes. If we use `\2c ` then JS complains
  163. // about strict mode. But `\\2c ` is not what it expected.
  164. function css(templates) {
  165. return templates.join('')
  166. }
  167. let config = {
  168. content: [
  169. {
  170. raw: html`
  171. <div class="grid-cols-[200px_repeat(auto-fill,minmax(15%,100px))_300px]"></div>
  172. <div class="grid-rows-[200px_repeat(auto-fill,minmax(15%,100px))_300px]"></div>
  173. <div class="shadow-[0px_0px_4px_black]"></div>
  174. <div class="rounded-[0px_4px_4px_0px]"></div>
  175. <div class="m-[8px_4px]"></div>
  176. <div class="p-[8px_4px]"></div>
  177. <div class="flex-[1_1_100%]"></div>
  178. <div class="col-[span_3_/_span_8]"></div>
  179. <div class="row-[span_3_/_span_8]"></div>
  180. <div class="auto-cols-[minmax(0,_1fr)]"></div>
  181. <div class="drop-shadow-[0px_1px_3px_black]"></div>
  182. <div class="content-[_hello_world_]"></div>
  183. <div class="content-[___abc____]"></div>
  184. <div class="content-['__hello__world__']"></div>
  185. `,
  186. },
  187. ],
  188. corePlugins: { preflight: false },
  189. }
  190. return run('@tailwind utilities', config).then((result) => {
  191. return expect(result.css).toMatchFormattedCss(css`
  192. .col-\\[span_3_\\/_span_8\\] {
  193. grid-column: span 3 / span 8;
  194. }
  195. .row-\\[span_3_\\/_span_8\\] {
  196. grid-row: span 3 / span 8;
  197. }
  198. .m-\\[8px_4px\\] {
  199. margin: 8px 4px;
  200. }
  201. .flex-\\[1_1_100\\%\\] {
  202. flex: 100%;
  203. }
  204. .auto-cols-\\[minmax\\(0\\,_1fr\\)\\] {
  205. grid-auto-columns: minmax(0, 1fr);
  206. }
  207. .grid-cols-\\[200px_repeat\\(auto-fill\\,minmax\\(15\\%\\,100px\\)\\)_300px\\] {
  208. grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
  209. }
  210. .grid-rows-\\[200px_repeat\\(auto-fill\\,minmax\\(15\\%\\,100px\\)\\)_300px\\] {
  211. grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
  212. }
  213. .rounded-\\[0px_4px_4px_0px\\] {
  214. border-radius: 0 4px 4px 0;
  215. }
  216. .p-\\[8px_4px\\] {
  217. padding: 8px 4px;
  218. }
  219. .shadow-\\[0px_0px_4px_black\\] {
  220. --tw-shadow: 0px 0px 4px black;
  221. --tw-shadow-colored: 0px 0px 4px var(--tw-shadow-color);
  222. box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
  223. var(--tw-shadow);
  224. }
  225. .drop-shadow-\\[0px_1px_3px_black\\] {
  226. --tw-drop-shadow: drop-shadow(0px 1px 3px black);
  227. filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale)
  228. var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia)
  229. var(--tw-drop-shadow);
  230. }
  231. .content-\\[\\'__hello__world__\\'\\] {
  232. --tw-content: ' hello world ';
  233. content: var(--tw-content);
  234. }
  235. .content-\\[___abc____\\] {
  236. --tw-content: abc;
  237. content: var(--tw-content);
  238. }
  239. .content-\\[_hello_world_\\] {
  240. --tw-content: hello world;
  241. content: var(--tw-content);
  242. }
  243. `)
  244. })
  245. })
  246. it('should not convert escaped underscores with spaces', () => {
  247. let config = {
  248. content: [{ raw: `<div class="content-['snake\\_case']"></div>` }],
  249. corePlugins: { preflight: false },
  250. }
  251. return run('@tailwind utilities', config).then((result) => {
  252. return expect(result.css).toMatchFormattedCss(css`
  253. .content-\[\'snake\\_case\'\] {
  254. --tw-content: 'snake_case';
  255. content: var(--tw-content);
  256. }
  257. `)
  258. })
  259. })
  260. it('should pick the fallback plugin when arbitrary values collide', () => {
  261. let config = {
  262. content: [
  263. {
  264. raw: html`
  265. <div>
  266. <!-- Background color -->
  267. <div class="bg-[var(--unknown)]"></div>
  268. <!-- Background size -->
  269. <div class="bg-[200px_100px]"></div>
  270. </div>
  271. `,
  272. },
  273. ],
  274. }
  275. return run('@tailwind utilities', config).then((result) => {
  276. return expect(result.css).toMatchFormattedCss(css`
  277. .bg-\[var\(--unknown\)\] {
  278. background-color: var(--unknown);
  279. }
  280. .bg-\[200px_100px\] {
  281. background-position: 200px 100px;
  282. }
  283. `)
  284. })
  285. })
  286. it('should pick the fallback plugin when arbitrary values collide and can not be inferred', () => {
  287. let config = {
  288. content: [{ raw: html`<div class="bg-[var(--tw-unknown)]"></div>` }],
  289. }
  290. return run('@tailwind utilities', config).then((result) => {
  291. return expect(result.css).toMatchFormattedCss(css`
  292. .bg-\[var\(--tw-unknown\)\] {
  293. background-color: var(--tw-unknown);
  294. }
  295. `)
  296. })
  297. })
  298. it('should warn and not generate if arbitrary values are ambiguous (without fallback)', () => {
  299. let config = {
  300. content: [{ raw: html`<div class="foo-[200px_100px]"></div>` }],
  301. plugins: [
  302. function ({ matchUtilities }) {
  303. matchUtilities({ foo: (value) => ({ value }) }, { type: ['position'] })
  304. matchUtilities({ foo: (value) => ({ value }) }, { type: ['size'] })
  305. },
  306. ],
  307. }
  308. return run('@tailwind utilities', config).then((result) => {
  309. return expect(result.css).toMatchFormattedCss(css``)
  310. })
  311. })
  312. it('should support colons in URLs', () => {
  313. let config = {
  314. content: [
  315. {
  316. raw: html`<div class="bg-[url('https://www.spacejam.com/1996/img/bg_stars.gif')]"></div>`,
  317. },
  318. ],
  319. }
  320. return run('@tailwind utilities', config).then((result) => {
  321. return expect(result.css).toMatchFormattedCss(css`
  322. .bg-\[url\(\'https\:\/\/www\.spacejam\.com\/1996\/img\/bg_stars\.gif\'\)\] {
  323. background-image: url('https://www.spacejam.com/1996/img/bg_stars.gif');
  324. }
  325. `)
  326. })
  327. })
  328. it('should support unescaped underscores in URLs', () => {
  329. let config = {
  330. content: [
  331. { raw: html`<div class="bg-[url('brown_potato.jpg'),_url('red_tomato.png')]"></div>` },
  332. ],
  333. }
  334. return run('@tailwind utilities', config).then((result) => {
  335. return expect(result.css).toMatchFormattedCss(`
  336. .bg-\\[url\\(\\'brown_potato\\.jpg\\'\\)\\,_url\\(\\'red_tomato\\.png\\'\\)\\] {
  337. background-image: url('brown_potato.jpg'), url('red_tomato.png');
  338. }
  339. `)
  340. })
  341. })
  342. it('should be possible to read theme values in arbitrary values (without quotes)', () => {
  343. let config = {
  344. content: [{ raw: html`<div class="w-[theme(spacing.1)] w-[theme(spacing[0.5])]"></div>` }],
  345. theme: {
  346. spacing: {
  347. 0.5: 'calc(.5 * .25rem)',
  348. 1: 'calc(1 * .25rem)',
  349. },
  350. },
  351. }
  352. return run('@tailwind utilities', config).then((result) => {
  353. return expect(result.css).toMatchFormattedCss(css`
  354. .w-\[theme\(spacing\.1\)\] {
  355. width: 0.25rem;
  356. }
  357. .w-\[theme\(spacing\[0\.5\]\)\] {
  358. width: 0.125rem;
  359. }
  360. `)
  361. })
  362. })
  363. it('should be possible to read theme values in arbitrary values (with quotes)', () => {
  364. let config = {
  365. content: [{ raw: html`<div class="w-[theme('spacing.1')] w-[theme('spacing[0.5]')]"></div>` }],
  366. theme: {
  367. spacing: {
  368. 0.5: 'calc(.5 * .25rem)',
  369. 1: 'calc(1 * .25rem)',
  370. },
  371. },
  372. }
  373. return run('@tailwind utilities', config).then((result) => {
  374. return expect(result.css).toMatchFormattedCss(css`
  375. .w-\[theme\(\'spacing\.1\'\)\] {
  376. width: 0.25rem;
  377. }
  378. .w-\[theme\(\'spacing\[0\.5\]\'\)\] {
  379. width: 0.125rem;
  380. }
  381. `)
  382. })
  383. })
  384. it('should be possible to read theme values in arbitrary values (with quotes) when inside calc or similar functions', () => {
  385. let config = {
  386. content: [
  387. {
  388. raw: html`<div
  389. class="w-[calc(100%-theme('spacing.1'))] w-[calc(100%-theme('spacing[0.5]'))]"
  390. ></div>`,
  391. },
  392. ],
  393. theme: {
  394. spacing: {
  395. 0.5: 'calc(.5 * .25rem)',
  396. 1: 'calc(1 * .25rem)',
  397. },
  398. },
  399. }
  400. return run('@tailwind utilities', config).then((result) => {
  401. return expect(result.css).toMatchFormattedCss(css`
  402. .w-\[calc\(100\%-theme\(\'spacing\.1\'\)\)\] {
  403. width: calc(100% - 0.25rem);
  404. }
  405. .w-\[calc\(100\%-theme\(\'spacing\[0\.5\]\'\)\)\] {
  406. width: calc(100% - 0.125rem);
  407. }
  408. `)
  409. })
  410. })
  411. it('should not output unparsable arbitrary CSS values', () => {
  412. let config = {
  413. content: [
  414. {
  415. raw: 'let classes = `w-[${sizes.width}]`',
  416. },
  417. ],
  418. }
  419. return run('@tailwind utilities', config).then((result) => {
  420. return expect(result.css).toMatchFormattedCss(``)
  421. })
  422. })
  423. // Issue: https://github.com/tailwindlabs/tailwindcss/issues/7997
  424. // `top_right_50%` was a valid percentage before introducing this change
  425. it('should correctly validate each part when checking for `percentage` data types', () => {
  426. let config = {
  427. content: [{ raw: html`<div class="bg-[top_right_50%]"></div>` }],
  428. corePlugins: { preflight: false },
  429. plugins: [],
  430. }
  431. let input = css`
  432. @tailwind utilities;
  433. `
  434. return run(input, config).then((result) => {
  435. expect(result.css).toMatchFormattedCss(css`
  436. .bg-\[top_right_50\%\] {
  437. background-position: right 50% top;
  438. }
  439. `)
  440. })
  441. })
  442. it('should correctly validate background size', () => {
  443. let config = {
  444. content: [{ raw: html`<div class="bg-[auto_auto,cover,_contain,10px,10px_10%]"></div>` }],
  445. corePlugins: { preflight: false },
  446. plugins: [],
  447. }
  448. let input = css`
  449. @tailwind utilities;
  450. `
  451. return run(input, config).then((result) => {
  452. expect(result.css).toMatchFormattedCss(css`
  453. .bg-\[auto_auto\,cover\,_contain\,10px\,10px_10\%\] {
  454. background-size: auto, cover, contain, 10px, 10px 10%;
  455. }
  456. `)
  457. })
  458. })
  459. it('should correctly validate combination of percentage and length', () => {
  460. let config = {
  461. content: [{ raw: html`<div class="bg-[50px_10%] bg-[50%_10%] bg-[50px_10px]"></div>` }],
  462. corePlugins: { preflight: false },
  463. plugins: [],
  464. }
  465. let input = css`
  466. @tailwind utilities;
  467. `
  468. return run(input, config).then((result) => {
  469. expect(result.css).toMatchFormattedCss(css`
  470. .bg-\[50\%_10\%\] {
  471. background-position: 50% 10%;
  472. }
  473. .bg-\[50px_10\%\] {
  474. background-position: 50px 10%;
  475. }
  476. .bg-\[50px_10px\] {
  477. background-position: 50px 10px;
  478. }
  479. `)
  480. })
  481. })
  482. it('can explicitly specify type for percentage and length', () => {
  483. let config = {
  484. content: [
  485. { raw: html`<div class="bg-[size:50px_10%] bg-[50px_10px] bg-[position:50%_10%]"></div>` },
  486. ],
  487. corePlugins: { preflight: false },
  488. plugins: [],
  489. }
  490. let input = css`
  491. @tailwind utilities;
  492. `
  493. return run(input, config).then((result) => {
  494. expect(result.css).toMatchFormattedCss(css`
  495. .bg-\[size\:50px_10\%\] {
  496. background-size: 50px 10%;
  497. }
  498. .bg-\[50px_10px\] {
  499. background-position: 50px 10px;
  500. }
  501. .bg-\[position\:50\%_10\%\] {
  502. background-position: 50% 10%;
  503. }
  504. `)
  505. })
  506. })
  507. it('can use CSS variables as arbitrary values without `var()`', () => {
  508. let config = {
  509. content: [
  510. {
  511. raw: html`<div
  512. class="w-[--width-var] bg-[--color-var] bg-[--color-var,#000] bg-[length:--size-var] text-[length:--size-var,12px]"
  513. ></div>`,
  514. },
  515. ],
  516. corePlugins: { preflight: false },
  517. plugins: [],
  518. }
  519. let input = css`
  520. @tailwind utilities;
  521. `
  522. return run(input, config).then((result) => {
  523. expect(result.css).toMatchFormattedCss(css`
  524. .w-\[--width-var\] {
  525. width: var(--width-var);
  526. }
  527. .bg-\[--color-var\,\#000\] {
  528. background-color: var(--color-var, #000);
  529. }
  530. .bg-\[--color-var\] {
  531. background-color: var(--color-var);
  532. }
  533. .bg-\[length\:--size-var\] {
  534. background-size: var(--size-var);
  535. }
  536. .text-\[length\:--size-var\,12px\] {
  537. font-size: var(--size-var, 12px);
  538. }
  539. `)
  540. })
  541. })
  542. it('can use CSS variables as arbitrary modifiers without `var()`', () => {
  543. let config = {
  544. content: [
  545. {
  546. raw: html`<div class="text-sm/[--line-height] bg-red-500/[--opacity]"></div>`,
  547. },
  548. ],
  549. corePlugins: { preflight: false },
  550. plugins: [],
  551. }
  552. let input = css`
  553. @tailwind utilities;
  554. `
  555. return run(input, config).then((result) => {
  556. expect(result.css).toMatchFormattedCss(css`
  557. .bg-red-500\/\[--opacity\] {
  558. background-color: rgb(239 68 68 / var(--opacity));
  559. }
  560. .text-sm\/\[--line-height\] {
  561. font-size: 0.875rem;
  562. line-height: var(--line-height);
  563. }
  564. `)
  565. })
  566. })
  567. it('should support underscores in arbitrary modifiers', () => {
  568. let config = {
  569. content: [{ raw: html`<div class="text-lg/[calc(50px_*_2)]"></div>` }],
  570. }
  571. return run('@tailwind utilities', config).then((result) => {
  572. return expect(result.css).toMatchFormattedCss(css`
  573. .text-lg\/\[calc\(50px_\*_2\)\] {
  574. font-size: 1.125rem;
  575. line-height: 100px;
  576. }
  577. `)
  578. })
  579. })
  580. it('should support slashes in arbitrary modifiers', () => {
  581. let config = {
  582. content: [{ raw: html`<div class="text-lg/[calc(50px/1rem)]"></div>` }],
  583. }
  584. return run('@tailwind utilities', config).then((result) => {
  585. return expect(result.css).toMatchFormattedCss(css`
  586. .text-lg\/\[calc\(50px\/1rem\)\] {
  587. font-size: 1.125rem;
  588. line-height: calc(50px / 1rem);
  589. }
  590. `)
  591. })
  592. })
  593. it('should not insert spaces around operators inside `env()`', () => {
  594. let config = {
  595. content: [{ raw: html`<div class="grid-cols-[calc(env(safe-area-inset-bottom)+1px)]"></div>` }],
  596. }
  597. return run('@tailwind utilities', config).then((result) => {
  598. expect(result.css).toMatchFormattedCss(css`
  599. .grid-cols-\[calc\(env\(safe-area-inset-bottom\)\+1px\)\] {
  600. grid-template-columns: calc(env(safe-area-inset-bottom) + 1px);
  601. }
  602. `)
  603. })
  604. })
  605. it('should not insert spaces around `-` in arbitrary values that use `max-content`', () => {
  606. let config = {
  607. content: [{ raw: html`<div class="grid-cols-[repeat(3,_minmax(0,_max-content))]"></div>` }],
  608. }
  609. return run('@tailwind utilities', config).then((result) => {
  610. expect(result.css).toMatchFormattedCss(css`
  611. .grid-cols-\[repeat\(3\,_minmax\(0\,_max-content\)\)\] {
  612. grid-template-columns: repeat(3, minmax(0, max-content));
  613. }
  614. `)
  615. })
  616. })