From d1fd645bebb6086772584c326c8cfe793b4a7480 Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Thu, 11 Sep 2025 12:21:50 +0200 Subject: [PATCH] Proposal: Allow overwriting static utilities that have a namespace (#18056) This PR attempts to move static utilities that are overwriteable by a theme value to be a fallback rather than a conflicting implementation. The idea is to allow a theme value to take presedence over that static utility _and cause it not to generate_. For example, when overwriting the `--radius-full` variant, it should ensure that the default `rounded-full` no longer emits the `calc(infinity * 1px)` declaration: ```ts expect( await compileCss( css` @theme { --radius-full: 99999px; } @tailwind utilities; `, ['rounded-full'], ), ).toMatchInlineSnapshot(` ":root, :host { --radius-full: 99999px; } .rounded-full { border-radius: var(--radius-full); }" `) ``` This allows anyone who wants `--radius-full` to be a CSS variable to simply define it in their theme: ```css @theme { /* Make `--radius-full` a CSS variable without the utility generating two CSS classes */ --radius-full: calc(infinity * 1px); } ``` The idea is to extend this pattern across all functional utilities that also have static utilities that can collide with the namespace. This gives users more control over what they want as CSS variables when the defaults don't work for them, allowing them to resolve #16639 and #15115 in user space. You may now find yourself thinking "but Philipp, why would someone want to be able to overwrite `--animate-none`. `none` surely always will mean no animation" and I would agree [but it's already possible right now anyways so this is not a new behavior! This PR just cleans up the generated output.](https://play.tailwindcss.com/StnQqm4V2e) --------- Co-authored-by: Robin Malfait --- CHANGELOG.md | 1 + packages/tailwindcss/src/utilities.test.ts | 790 ++++++++++++++++++++- packages/tailwindcss/src/utilities.ts | 340 +++++---- 3 files changed, 970 insertions(+), 161 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f7d9be27..b8a11dcb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure that file system watchers created when using the CLI are always cleaned up ([#18905](https://github.com/tailwindlabs/tailwindcss/pull/18905)) - Do not generate `grid-column` utilities when configuring `grid-column-start` or `grid-column-end` ([#18907](https://github.com/tailwindlabs/tailwindcss/pull/18907)) - Do not generate `grid-row` utilities when configuring `grid-row-start` or `grid-row-end` ([#18907](https://github.com/tailwindlabs/tailwindcss/pull/18907)) +- Prevent duplicate CSS when overwriting a static utility with a theme key ([#18056](https://github.com/tailwindlabs/tailwindcss/pull/18056)) ## [4.1.13] - 2025-09-03 diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index 5a1fe7596..c7615708a 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -1054,6 +1054,26 @@ test('z-index', async () => { '-z-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --z-index-auto: 42; + } + @tailwind utilities; + `, + ['z-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --z-index-auto: 42; + } + + .z-auto { + z-index: var(--z-index-auto); + }" + `) }) test('order', async () => { @@ -1114,6 +1134,46 @@ test('order', async () => { 'order-none/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --order-first: 1; + } + @tailwind utilities; + `, + ['order-first'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --order-first: 1; + } + + .order-first { + order: var(--order-first); + }" + `) + + expect( + await compileCss( + css` + @theme { + --order-last: -1; + } + @tailwind utilities; + `, + ['order-last'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --order-last: -1; + } + + .order-last { + order: var(--order-last); + }" + `) }) test('col', async () => { @@ -1176,6 +1236,26 @@ test('col', async () => { 'col-span-[var(--my-variable)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-column-auto: 5; + } + @tailwind utilities; + `, + ['col-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-column-auto: 5; + } + + .col-auto { + grid-column: var(--grid-column-auto); + }" + `) }) test('col-start', async () => { @@ -1237,6 +1317,26 @@ test('col-start', async () => { '-col-start-4/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-column-start-auto: 7; + } + @tailwind utilities; + `, + ['col-start-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-column-start-auto: 7; + } + + .col-start-auto { + grid-column-start: var(--grid-column-start-auto); + }" + `) }) test('col-end', async () => { @@ -1291,6 +1391,26 @@ test('col-end', async () => { '-col-end-4/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-column-end-auto: 3; + } + @tailwind utilities; + `, + ['col-end-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-column-end-auto: 3; + } + + .col-end-auto { + grid-column-end: var(--grid-column-end-auto); + }" + `) }) test('row', async () => { @@ -1353,6 +1473,26 @@ test('row', async () => { 'row-span-[var(--my-variable)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-row-auto: 9; + } + @tailwind utilities; + `, + ['row-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-row-auto: 9; + } + + .row-auto { + grid-row: var(--grid-row-auto); + }" + `) }) test('row-start', async () => { @@ -1414,6 +1554,26 @@ test('row-start', async () => { '-row-start-4/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-row-start-auto: 11; + } + @tailwind utilities; + `, + ['row-start-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-row-start-auto: 11; + } + + .row-start-auto { + grid-row-start: var(--grid-row-start-auto); + }" + `) }) test('row-end', async () => { @@ -1468,6 +1628,26 @@ test('row-end', async () => { '-row-end-4/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-row-end-auto: 13; + } + @tailwind utilities; + `, + ['row-end-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-row-end-auto: 13; + } + + .row-end-auto { + grid-row-end: var(--grid-row-end-auto); + }" + `) }) test('float', async () => { @@ -2422,6 +2602,29 @@ test('line-clamp', async () => { 'line-clamp-none/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --line-clamp-none: 0; + } + @tailwind utilities; + `, + ['line-clamp-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --line-clamp-none: 0; + } + + .line-clamp-none { + -webkit-line-clamp: var(--line-clamp-none); + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + }" + `) }) test('display', async () => { @@ -3980,6 +4183,26 @@ test('origin', async () => { 'origin-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --transform-origin-top: 10px 20px; + } + @tailwind utilities; + `, + ['origin-top'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --transform-origin-top: 10px 20px; + } + + .origin-top { + transform-origin: var(--transform-origin-top); + }" + `) }) test('perspective-origin', async () => { @@ -4059,6 +4282,27 @@ test('perspective-origin', async () => { 'perspective-origin-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --perspective-origin-top: 10px 20px; + } + @tailwind utilities; + `, + ['perspective-origin-top'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --perspective-origin-top: 10px 20px; + } + + .perspective-origin-top { + perspective-origin: var(--perspective-origin-top); + perspective: var(--perspective-origin-top); + }" + `) }) test('translate', async () => { @@ -5560,6 +5804,26 @@ test('perspective', async () => { 'perspective-[456px]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --perspective-none: 400px; + } + @tailwind utilities; + `, + ['perspective-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --perspective-none: 400px; + } + + .perspective-none { + perspective: var(--perspective-none); + }" + `) }) test('cursor', async () => { @@ -6937,6 +7201,26 @@ test('list', async () => { 'list-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --list-style-type-none: disc; + } + @tailwind utilities; + `, + ['list-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --list-style-type-none: disc; + } + + .list-none { + list-style-type: var(--list-style-type-none); + }" + `) }) test('list-image', async () => { @@ -6958,6 +7242,26 @@ test('list-image', async () => { 'list-image-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --list-style-image-none: url(../foo.png); + } + @tailwind utilities; + `, + ['list-image-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --list-style-image-none: url("../foo.png"); + } + + .list-image-none { + list-style-image: var(--list-style-image-none); + }" + `) }) test('appearance', async () => { @@ -7099,6 +7403,26 @@ test('columns', async () => { 'columns-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --columns-auto: 3; + } + @tailwind utilities; + `, + ['columns-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --columns-auto: 3; + } + + .columns-auto { + columns: var(--columns-auto); + }" + `) }) test('break-before', async () => { @@ -7319,6 +7643,26 @@ test('auto-cols', async () => { 'auto-cols-[2fr]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-auto-columns-auto: 2fr; + } + @tailwind utilities; + `, + ['auto-cols-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-auto-columns-auto: 2fr; + } + + .auto-cols-auto { + grid-auto-columns: var(--grid-auto-columns-auto); + }" + `) }) test('grid-flow', async () => { @@ -7410,6 +7754,26 @@ test('auto-rows', async () => { 'auto-rows-[2fr]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-auto-rows-auto: 2fr; + } + @tailwind utilities; + `, + ['auto-rows-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-auto-rows-auto: 2fr; + } + + .auto-rows-auto { + grid-auto-rows: var(--grid-auto-rows-auto); + }" + `) }) test('grid-cols', async () => { @@ -7459,6 +7823,26 @@ test('grid-cols', async () => { 'grid-cols-[123]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-template-columns-none: 200px 1fr; + } + @tailwind utilities; + `, + ['grid-cols-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-template-columns-none: 200px 1fr; + } + + .grid-cols-none { + grid-template-columns: var(--grid-template-columns-none); + }" + `) }) test('grid-rows', async () => { @@ -7508,6 +7892,26 @@ test('grid-rows', async () => { 'grid-rows-[123]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --grid-template-rows-none: 200px 1fr; + } + @tailwind utilities; + `, + ['grid-rows-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --grid-template-rows-none: 200px 1fr; + } + + .grid-rows-none { + grid-template-rows: var(--grid-template-rows-none); + }" + `) }) test('flex-direction', async () => { @@ -9770,6 +10174,25 @@ test('rounded', async () => { border-radius: var(--radius-sm); }" `) + expect( + await compileCss( + css` + @theme { + --radius-full: 99999px; + } + @tailwind utilities; + `, + ['rounded-full'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --radius-full: 99999px; + } + + .rounded-full { + border-radius: var(--radius-full); + }" + `) expect( await run([ '-rounded', @@ -9945,15 +10368,11 @@ test('rounded-t', async () => { } .rounded-t-full { - border-top-left-radius: 3.40282e38px; - border-top-right-radius: 3.40282e38px; border-top-left-radius: var(--radius-full); border-top-right-radius: var(--radius-full); } .rounded-t-none { - border-top-left-radius: 0; - border-top-right-radius: 0; border-top-left-radius: var(--radius-none); border-top-right-radius: var(--radius-none); } @@ -10012,15 +10431,11 @@ test('rounded-r', async () => { } .rounded-r-full { - border-top-right-radius: 3.40282e38px; - border-bottom-right-radius: 3.40282e38px; border-top-right-radius: var(--radius-full); border-bottom-right-radius: var(--radius-full); } .rounded-r-none { - border-top-right-radius: 0; - border-bottom-right-radius: 0; border-top-right-radius: var(--radius-none); border-bottom-right-radius: var(--radius-none); } @@ -10079,15 +10494,11 @@ test('rounded-b', async () => { } .rounded-b-full { - border-bottom-right-radius: 3.40282e38px; - border-bottom-left-radius: 3.40282e38px; border-bottom-right-radius: var(--radius-full); border-bottom-left-radius: var(--radius-full); } .rounded-b-none { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; border-bottom-right-radius: var(--radius-none); border-bottom-left-radius: var(--radius-none); } @@ -10146,15 +10557,11 @@ test('rounded-l', async () => { } .rounded-l-full { - border-top-left-radius: 3.40282e38px; - border-bottom-left-radius: 3.40282e38px; border-top-left-radius: var(--radius-full); border-bottom-left-radius: var(--radius-full); } .rounded-l-none { - border-top-left-radius: 0; - border-bottom-left-radius: 0; border-top-left-radius: var(--radius-none); border-bottom-left-radius: var(--radius-none); } @@ -10443,12 +10850,10 @@ test('rounded-tl', async () => { } .rounded-tl-full { - border-top-left-radius: 3.40282e38px; border-top-left-radius: var(--radius-full); } .rounded-tl-none { - border-top-left-radius: 0; border-top-left-radius: var(--radius-none); } @@ -10503,12 +10908,10 @@ test('rounded-tr', async () => { } .rounded-tr-full { - border-top-right-radius: 3.40282e38px; border-top-right-radius: var(--radius-full); } .rounded-tr-none { - border-top-right-radius: 0; border-top-right-radius: var(--radius-none); } @@ -10563,12 +10966,10 @@ test('rounded-br', async () => { } .rounded-br-full { - border-bottom-right-radius: 3.40282e38px; border-bottom-right-radius: var(--radius-full); } .rounded-br-none { - border-bottom-right-radius: 0; border-bottom-right-radius: var(--radius-none); } @@ -10623,12 +11024,10 @@ test('rounded-bl', async () => { } .rounded-bl-full { - border-bottom-left-radius: 3.40282e38px; border-bottom-left-radius: var(--radius-full); } .rounded-bl-none { - border-bottom-left-radius: 0; border-bottom-left-radius: var(--radius-none); } @@ -20025,6 +20424,26 @@ test('object', async () => { 'object-top/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --object-position-center: top left; + } + @tailwind utilities; + `, + ['object-center'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --object-position-center: top left; + } + + .object-center { + object-position: var(--object-position-center); + }" + `) }) test('p', async () => { @@ -21327,6 +21746,26 @@ test('animate', async () => { 'animate-not-found/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --animate-none: bounce 1s infinite; + } + @tailwind utilities; + `, + ['animate-none'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --animate-none: bounce 1s infinite; + } + + .animate-none { + animation: var(--animate-none); + }" + `) }) test('filter', async () => { @@ -21756,6 +22195,113 @@ test('filter', async () => { 'sepia-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --blur-none: 2px; + } + @tailwind utilities; + `, + ['blur-none'], + ), + ).toMatchInlineSnapshot(` + "@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + } + } + } + + :root, :host { + --blur-none: 2px; + } + + .blur-none { + --tw-blur: blur(var(--blur-none)); + filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, ) var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, ) var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, ); + } + + @property --tw-blur { + syntax: "*"; + inherits: false + } + + @property --tw-brightness { + syntax: "*"; + inherits: false + } + + @property --tw-contrast { + syntax: "*"; + inherits: false + } + + @property --tw-grayscale { + syntax: "*"; + inherits: false + } + + @property --tw-hue-rotate { + syntax: "*"; + inherits: false + } + + @property --tw-invert { + syntax: "*"; + inherits: false + } + + @property --tw-opacity { + syntax: "*"; + inherits: false + } + + @property --tw-saturate { + syntax: "*"; + inherits: false + } + + @property --tw-sepia { + syntax: "*"; + inherits: false + } + + @property --tw-drop-shadow { + syntax: "*"; + inherits: false + } + + @property --tw-drop-shadow-color { + syntax: "*"; + inherits: false + } + + @property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; + } + + @property --tw-drop-shadow-size { + syntax: "*"; + inherits: false + }" + `) }) test('backdrop-filter', async () => { @@ -22136,6 +22682,89 @@ test('backdrop-filter', async () => { 'backdrop-sepia-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --backdrop-blur-none: 2px; + } + @tailwind utilities; + `, + ['backdrop-blur-none'], + ), + ).toMatchInlineSnapshot(` + "@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-backdrop-blur: initial; + --tw-backdrop-brightness: initial; + --tw-backdrop-contrast: initial; + --tw-backdrop-grayscale: initial; + --tw-backdrop-hue-rotate: initial; + --tw-backdrop-invert: initial; + --tw-backdrop-opacity: initial; + --tw-backdrop-saturate: initial; + --tw-backdrop-sepia: initial; + } + } + } + + :root, :host { + --backdrop-blur-none: 2px; + } + + .backdrop-blur-none { + --tw-backdrop-blur: blur(var(--backdrop-blur-none)); + -webkit-backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, ); + backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, ); + } + + @property --tw-backdrop-blur { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-brightness { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-contrast { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-grayscale { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-hue-rotate { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-invert { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-opacity { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-saturate { + syntax: "*"; + inherits: false + } + + @property --tw-backdrop-sepia { + syntax: "*"; + inherits: false + }" + `) }) test('transition', async () => { @@ -22195,9 +22824,6 @@ test('transition', async () => { } .transition-opacity { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); transition-property: var(--transition-property-opacity); transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); @@ -22283,6 +22909,28 @@ test('transition', async () => { 'transition-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --transition-property-colors: transform; + } + @tailwind utilities; + `, + ['transition-colors'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --transition-property-colors: transform; + } + + .transition-colors { + transition-property: var(--transition-property-colors); + transition-timing-function: var(--tw-ease, ease); + transition-duration: var(--tw-duration, 0s); + }" + `) }) test('transition-behavior', async () => { @@ -22426,6 +23074,40 @@ test('ease', async () => { 'ease-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --ease-linear: steps(4); + } + @tailwind utilities; + `, + ['ease-linear'], + ), + ).toMatchInlineSnapshot(` + "@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-ease: initial; + } + } + } + + :root, :host { + --ease-linear: steps(4); + } + + .ease-linear { + --tw-ease: var(--ease-linear); + transition-timing-function: var(--ease-linear); + } + + @property --tw-ease { + syntax: "*"; + inherits: false + }" + `) }) test('will-change', async () => { @@ -22683,6 +23365,40 @@ test('leading', async () => { 'leading-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --leading-none: 2; + } + @tailwind utilities; + `, + ['leading-none'], + ), + ).toMatchInlineSnapshot(` + "@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-leading: initial; + } + } + } + + :root, :host { + --leading-none: 2; + } + + .leading-none { + --tw-leading: var(--leading-none); + line-height: var(--leading-none); + } + + @property --tw-leading { + syntax: "*"; + inherits: false + }" + `) }) test('tracking', async () => { @@ -23410,6 +24126,26 @@ test('underline-offset', async () => { '-underline-offset-[var(--value)]/foo', ]), ).toEqual('') + + expect( + await compileCss( + css` + @theme { + --text-underline-offset-auto: 4px; + } + @tailwind utilities; + `, + ['underline-offset-auto'], + ), + ).toMatchInlineSnapshot(` + ":root, :host { + --text-underline-offset-auto: 4px; + } + + .underline-offset-auto { + text-underline-offset: var(--text-underline-offset-auto); + }" + `) }) test('text', async () => { diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index 2cc1fe37e..efe7b3dc6 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -375,6 +375,7 @@ export function createUtilities(theme: Theme) { supportsFractions?: boolean themeKeys?: ThemeKey[] defaultValue?: string | null + staticValues?: Record handleBareValue?: (value: NamedUtilityValue) => string | null handleNegativeBareValue?: (value: NamedUtilityValue) => string | null handle: (value: string, dataType: string | null) => AstNode[] | undefined @@ -431,6 +432,11 @@ export function createUtilities(theme: Theme) { value = desc.handleBareValue(candidate.value) if (!value?.includes('/') && candidate.modifier) return } + + if (value === null && !negative && desc.staticValues && !candidate.modifier) { + let fallback = desc.staticValues[candidate.value.value] + if (fallback) return fallback + } } // If there is no value, don't generate any rules. @@ -454,6 +460,13 @@ export function createUtilities(theme: Theme) { supportsFractions: desc.supportsFractions, }, ]) + + // Also suggest any staticValues automatically so callers don't need to + // manually add suggestion groups for e.g. `auto`, `none`, `full`, etc. + if (desc.staticValues && Object.keys(desc.staticValues).length > 0) { + let values = Object.keys(desc.staticValues) + suggest(classRoot, () => [{ values }]) + } } type ColorUtilityDescription = { @@ -506,9 +519,11 @@ export function createUtilities(theme: Theme) { { supportsNegative = false, supportsFractions = false, + staticValues, }: { supportsNegative?: boolean supportsFractions?: boolean + staticValues?: Record } = {}, ) { if (supportsNegative) { @@ -535,6 +550,7 @@ export function createUtilities(theme: Theme) { return `calc(${multiplier} * -${value})` }, handle, + staticValues, }) suggest(name, () => [ @@ -629,7 +645,6 @@ export function createUtilities(theme: Theme) { /** * @css `z-index` */ - staticUtility('z-auto', [['z-index', 'auto']]) functionalUtility('z', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -638,6 +653,9 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--z-index'], handle: (value) => [decl('z-index', value)], + staticValues: { + auto: [decl('z-index', 'auto')], + }, }) suggest('z', () => [ @@ -651,8 +669,6 @@ export function createUtilities(theme: Theme) { /** * @css `order` */ - staticUtility('order-first', [['order', '-9999']]) - staticUtility('order-last', [['order', '9999']]) functionalUtility('order', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -661,6 +677,10 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--order'], handle: (value) => [decl('order', value)], + staticValues: { + first: [decl('order', '-9999')], + last: [decl('order', '9999')], + }, }) suggest('order', () => [ @@ -674,7 +694,6 @@ export function createUtilities(theme: Theme) { /** * @css `grid-column` */ - staticUtility('col-auto', [['grid-column', 'auto']]) functionalUtility('col', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -683,20 +702,25 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--grid-column'], handle: (value) => [decl('grid-column', value)], + staticValues: { + auto: [decl('grid-column', 'auto')], + }, }) - staticUtility('col-span-full', [['grid-column', '1 / -1']]) + functionalUtility('col-span', { handleBareValue: ({ value }) => { if (!isPositiveInteger(value)) return null return value }, handle: (value) => [decl('grid-column', `span ${value} / span ${value}`)], + staticValues: { + full: [decl('grid-column', '1 / -1')], + }, }) /** * @css `grid-column-start` */ - staticUtility('col-start-auto', [['grid-column-start', 'auto']]) functionalUtility('col-start', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -705,12 +729,14 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--grid-column-start'], handle: (value) => [decl('grid-column-start', value)], + staticValues: { + auto: [decl('grid-column-start', 'auto')], + }, }) /** * @css `grid-column-end` */ - staticUtility('col-end-auto', [['grid-column-end', 'auto']]) functionalUtility('col-end', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -719,6 +745,9 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--grid-column-end'], handle: (value) => [decl('grid-column-end', value)], + staticValues: { + auto: [decl('grid-column-end', 'auto')], + }, }) suggest('col-span', () => [ @@ -747,7 +776,6 @@ export function createUtilities(theme: Theme) { /** * @css `grid-row` */ - staticUtility('row-auto', [['grid-row', 'auto']]) functionalUtility('row', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -756,8 +784,11 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--grid-row'], handle: (value) => [decl('grid-row', value)], + staticValues: { + auto: [decl('grid-row', 'auto')], + }, }) - staticUtility('row-span-full', [['grid-row', '1 / -1']]) + functionalUtility('row-span', { themeKeys: [], handleBareValue: ({ value }) => { @@ -765,12 +796,14 @@ export function createUtilities(theme: Theme) { return value }, handle: (value) => [decl('grid-row', `span ${value} / span ${value}`)], + staticValues: { + full: [decl('grid-row', '1 / -1')], + }, }) /** * @css `grid-row-start` */ - staticUtility('row-start-auto', [['grid-row-start', 'auto']]) functionalUtility('row-start', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -779,12 +812,14 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--grid-row-start'], handle: (value) => [decl('grid-row-start', value)], + staticValues: { + auto: [decl('grid-row-start', 'auto')], + }, }) /** * @css `grid-row-end` */ - staticUtility('row-end-auto', [['grid-row-end', 'auto']]) functionalUtility('row-end', { supportsNegative: true, handleBareValue: ({ value }) => { @@ -793,6 +828,9 @@ export function createUtilities(theme: Theme) { }, themeKeys: ['--grid-row-end'], handle: (value) => [decl('grid-row-end', value)], + staticValues: { + auto: [decl('grid-row-end', 'auto')], + }, }) suggest('row-span', () => [ @@ -866,12 +904,6 @@ export function createUtilities(theme: Theme) { /** * @css `line-clamp` */ - staticUtility('line-clamp-none', [ - ['overflow', 'visible'], - ['display', 'block'], - ['-webkit-box-orient', 'horizontal'], - ['-webkit-line-clamp', 'unset'], - ]) functionalUtility('line-clamp', { themeKeys: ['--line-clamp'], handleBareValue: ({ value }) => { @@ -884,6 +916,14 @@ export function createUtilities(theme: Theme) { decl('-webkit-box-orient', 'vertical'), decl('-webkit-line-clamp', value), ], + staticValues: { + none: [ + decl('overflow', 'visible'), + decl('display', 'block'), + decl('-webkit-box-orient', 'horizontal'), + decl('-webkit-line-clamp', 'unset'), + ], + }, }) suggest('line-clamp', () => [ @@ -928,8 +968,6 @@ export function createUtilities(theme: Theme) { /** * @css `aspect-ratio` */ - staticUtility('aspect-auto', [['aspect-ratio', 'auto']]) - staticUtility('aspect-square', [['aspect-ratio', '1 / 1']]) functionalUtility('aspect', { themeKeys: ['--aspect'], handleBareValue: ({ fraction }) => { @@ -939,6 +977,10 @@ export function createUtilities(theme: Theme) { return fraction }, handle: (value) => [decl('aspect-ratio', value)], + staticValues: { + auto: [decl('aspect-ratio', 'auto')], + square: [decl('aspect-ratio', '1 / 1')], + }, }) /** @@ -1171,41 +1213,47 @@ export function createUtilities(theme: Theme) { /** * @css `transform-origin` */ - staticUtility('origin-center', [['transform-origin', 'center']]) - staticUtility('origin-top', [['transform-origin', 'top']]) - staticUtility('origin-top-right', [['transform-origin', 'top right']]) - staticUtility('origin-right', [['transform-origin', 'right']]) - staticUtility('origin-bottom-right', [['transform-origin', 'bottom right']]) - staticUtility('origin-bottom', [['transform-origin', 'bottom']]) - staticUtility('origin-bottom-left', [['transform-origin', 'bottom left']]) - staticUtility('origin-left', [['transform-origin', 'left']]) - staticUtility('origin-top-left', [['transform-origin', 'top left']]) functionalUtility('origin', { themeKeys: ['--transform-origin'], handle: (value) => [decl('transform-origin', value)], + staticValues: { + center: [decl('transform-origin', 'center')], + top: [decl('transform-origin', 'top')], + 'top-right': [decl('transform-origin', '100% 0')], + right: [decl('transform-origin', '100%')], + 'bottom-right': [decl('transform-origin', '100% 100%')], + bottom: [decl('transform-origin', 'bottom')], + 'bottom-left': [decl('transform-origin', '0 100%')], + left: [decl('transform-origin', '0')], + 'top-left': [decl('transform-origin', '0 0')], + }, }) - staticUtility('perspective-origin-center', [['perspective-origin', 'center']]) - staticUtility('perspective-origin-top', [['perspective-origin', 'top']]) - staticUtility('perspective-origin-top-right', [['perspective-origin', 'top right']]) - staticUtility('perspective-origin-right', [['perspective-origin', 'right']]) - staticUtility('perspective-origin-bottom-right', [['perspective-origin', 'bottom right']]) - staticUtility('perspective-origin-bottom', [['perspective-origin', 'bottom']]) - staticUtility('perspective-origin-bottom-left', [['perspective-origin', 'bottom left']]) - staticUtility('perspective-origin-left', [['perspective-origin', 'left']]) - staticUtility('perspective-origin-top-left', [['perspective-origin', 'top left']]) functionalUtility('perspective-origin', { themeKeys: ['--perspective-origin'], handle: (value) => [decl('perspective-origin', value)], + staticValues: { + center: [decl('perspective-origin', 'center')], + top: [decl('perspective-origin', 'top')], + 'top-right': [decl('perspective-origin', '100% 0')], + right: [decl('perspective-origin', '100%')], + 'bottom-right': [decl('perspective-origin', '100% 100%')], + bottom: [decl('perspective-origin', 'bottom')], + 'bottom-left': [decl('perspective-origin', '0 100%')], + left: [decl('perspective-origin', '0')], + 'top-left': [decl('perspective-origin', '0 0')], + }, }) /** * @css `perspective` */ - staticUtility('perspective-none', [['perspective', 'none']]) functionalUtility('perspective', { themeKeys: ['--perspective'], handle: (value) => [decl('perspective', value)], + staticValues: { + none: [decl('perspective', 'none')], + }, }) let translateProperties = () => @@ -1757,23 +1805,24 @@ export function createUtilities(theme: Theme) { staticUtility('list-inside', [['list-style-position', 'inside']]) staticUtility('list-outside', [['list-style-position', 'outside']]) - /** - * @css `list-style-type` - */ - staticUtility('list-none', [['list-style-type', 'none']]) - staticUtility('list-disc', [['list-style-type', 'disc']]) - staticUtility('list-decimal', [['list-style-type', 'decimal']]) functionalUtility('list', { themeKeys: ['--list-style-type'], handle: (value) => [decl('list-style-type', value)], + staticValues: { + none: [decl('list-style-type', 'none')], + disc: [decl('list-style-type', 'disc')], + decimal: [decl('list-style-type', 'decimal')], + }, }) // list-image-* - staticUtility('list-image-none', [['list-style-image', 'none']]) functionalUtility('list-image', { themeKeys: ['--list-style-image'], handle: (value) => [decl('list-style-image', value)], + staticValues: { + none: [decl('list-style-image', 'none')], + }, }) staticUtility('appearance-none', [['appearance', 'none']]) @@ -1786,9 +1835,6 @@ export function createUtilities(theme: Theme) { staticUtility('scheme-only-dark', [['color-scheme', 'only dark']]) staticUtility('scheme-only-light', [['color-scheme', 'only light']]) - // columns-* - staticUtility('columns-auto', [['columns', 'auto']]) - functionalUtility('columns', { themeKeys: ['--columns', '--container'], handleBareValue: ({ value }) => { @@ -1796,6 +1842,9 @@ export function createUtilities(theme: Theme) { return value }, handle: (value) => [decl('columns', value)], + staticValues: { + auto: [decl('columns', 'auto')], + }, }) suggest('columns', () => [ @@ -1823,26 +1872,28 @@ export function createUtilities(theme: Theme) { staticUtility('grid-flow-row-dense', [['grid-auto-flow', 'row dense']]) staticUtility('grid-flow-col-dense', [['grid-auto-flow', 'column dense']]) - staticUtility('auto-cols-auto', [['grid-auto-columns', 'auto']]) - staticUtility('auto-cols-min', [['grid-auto-columns', 'min-content']]) - staticUtility('auto-cols-max', [['grid-auto-columns', 'max-content']]) - staticUtility('auto-cols-fr', [['grid-auto-columns', 'minmax(0, 1fr)']]) functionalUtility('auto-cols', { themeKeys: ['--grid-auto-columns'], handle: (value) => [decl('grid-auto-columns', value)], + staticValues: { + auto: [decl('grid-auto-columns', 'auto')], + min: [decl('grid-auto-columns', 'min-content')], + max: [decl('grid-auto-columns', 'max-content')], + fr: [decl('grid-auto-columns', 'minmax(0, 1fr)')], + }, }) - staticUtility('auto-rows-auto', [['grid-auto-rows', 'auto']]) - staticUtility('auto-rows-min', [['grid-auto-rows', 'min-content']]) - staticUtility('auto-rows-max', [['grid-auto-rows', 'max-content']]) - staticUtility('auto-rows-fr', [['grid-auto-rows', 'minmax(0, 1fr)']]) functionalUtility('auto-rows', { themeKeys: ['--grid-auto-rows'], handle: (value) => [decl('grid-auto-rows', value)], + staticValues: { + auto: [decl('grid-auto-rows', 'auto')], + min: [decl('grid-auto-rows', 'min-content')], + max: [decl('grid-auto-rows', 'max-content')], + fr: [decl('grid-auto-rows', 'minmax(0, 1fr)')], + }, }) - staticUtility('grid-cols-none', [['grid-template-columns', 'none']]) - staticUtility('grid-cols-subgrid', [['grid-template-columns', 'subgrid']]) functionalUtility('grid-cols', { themeKeys: ['--grid-template-columns'], handleBareValue: ({ value }) => { @@ -1850,10 +1901,12 @@ export function createUtilities(theme: Theme) { return `repeat(${value}, minmax(0, 1fr))` }, handle: (value) => [decl('grid-template-columns', value)], + staticValues: { + none: [decl('grid-template-columns', 'none')], + subgrid: [decl('grid-template-columns', 'subgrid')], + }, }) - staticUtility('grid-rows-none', [['grid-template-rows', 'none']]) - staticUtility('grid-rows-subgrid', [['grid-template-rows', 'subgrid']]) functionalUtility('grid-rows', { themeKeys: ['--grid-template-rows'], handleBareValue: ({ value }) => { @@ -1861,6 +1914,10 @@ export function createUtilities(theme: Theme) { return `repeat(${value}, minmax(0, 1fr))` }, handle: (value) => [decl('grid-template-rows', value)], + staticValues: { + none: [decl('grid-template-rows', 'none')], + subgrid: [decl('grid-template-rows', 'subgrid')], + }, }) suggest('grid-cols', () => [ @@ -2125,17 +2182,13 @@ export function createUtilities(theme: Theme) { ['rounded-br', ['border-bottom-right-radius']], ['rounded-bl', ['border-bottom-left-radius']], ] as const) { - staticUtility( - `${root}-none`, - properties.map((property) => [property, '0']), - ) - staticUtility( - `${root}-full`, - properties.map((property) => [property, 'calc(infinity * 1px)']), - ) functionalUtility(root, { themeKeys: ['--radius'], handle: (value) => properties.map((property) => decl(property, value)), + staticValues: { + none: properties.map((property) => decl(property, '0')), + full: properties.map((property) => decl(property, 'calc(infinity * 1px)')), + }, }) } } @@ -3628,18 +3681,20 @@ export function createUtilities(theme: Theme) { staticUtility('object-none', [['object-fit', 'none']]) staticUtility('object-scale-down', [['object-fit', 'scale-down']]) - staticUtility('object-top', [['object-position', 'top']]) - staticUtility('object-top-left', [['object-position', 'left top']]) - staticUtility('object-top-right', [['object-position', 'right top']]) - staticUtility('object-bottom', [['object-position', 'bottom']]) - staticUtility('object-bottom-left', [['object-position', 'left bottom']]) - staticUtility('object-bottom-right', [['object-position', 'right bottom']]) - staticUtility('object-left', [['object-position', 'left']]) - staticUtility('object-right', [['object-position', 'right']]) - staticUtility('object-center', [['object-position', 'center']]) functionalUtility('object', { themeKeys: ['--object-position'], handle: (value) => [decl('object-position', value)], + staticValues: { + top: [decl('object-position', 'top')], + 'top-left': [decl('object-position', 'left top')], + 'top-right': [decl('object-position', 'right top')], + bottom: [decl('object-position', 'bottom')], + 'bottom-left': [decl('object-position', 'left bottom')], + 'bottom-right': [decl('object-position', 'right bottom')], + left: [decl('object-position', 'left')], + right: [decl('object-position', 'right')], + center: [decl('object-position', 'center')], + }, }) for (let [name, property] of [ @@ -3867,10 +3922,12 @@ export function createUtilities(theme: Theme) { }, ]) - staticUtility('animate-none', [['animation', 'none']]) functionalUtility('animate', { themeKeys: ['--animate'], handle: (value) => [decl('animation', value)], + staticValues: { + none: [decl('animation', 'none')], + }, }) { @@ -3978,10 +4035,11 @@ export function createUtilities(theme: Theme) { decl('--tw-blur', `blur(${value})`), decl('filter', cssFilterValue), ], + staticValues: { + none: [filterProperties(), decl('--tw-blur', ' '), decl('filter', cssFilterValue)], + }, }) - staticUtility('blur-none', [filterProperties, ['--tw-blur', ' '], ['filter', cssFilterValue]]) - functionalUtility('backdrop-blur', { themeKeys: ['--backdrop-blur', '--blur'], handle: (value) => [ @@ -3990,15 +4048,16 @@ export function createUtilities(theme: Theme) { decl('-webkit-backdrop-filter', cssBackdropFilterValue), decl('backdrop-filter', cssBackdropFilterValue), ], + staticValues: { + none: [ + backdropFilterProperties(), + decl('--tw-backdrop-blur', ' '), + decl('-webkit-backdrop-filter', cssBackdropFilterValue), + decl('backdrop-filter', cssBackdropFilterValue), + ], + }, }) - staticUtility('backdrop-blur-none', [ - backdropFilterProperties, - ['--tw-backdrop-blur', ' '], - ['-webkit-backdrop-filter', cssBackdropFilterValue], - ['backdrop-filter', cssBackdropFilterValue], - ]) - functionalUtility('brightness', { themeKeys: ['--brightness'], handleBareValue: ({ value }) => { @@ -4476,36 +4535,6 @@ export function createUtilities(theme: Theme) { let defaultTimingFunction = `var(--tw-ease, ${theme.resolve(null, ['--default-transition-timing-function']) ?? 'ease'})` let defaultDuration = `var(--tw-duration, ${theme.resolve(null, ['--default-transition-duration']) ?? '0s'})` - staticUtility('transition-none', [['transition-property', 'none']]) - staticUtility('transition-all', [ - ['transition-property', 'all'], - ['transition-timing-function', defaultTimingFunction], - ['transition-duration', defaultDuration], - ]) - staticUtility('transition-colors', [ - [ - 'transition-property', - 'color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to', - ], - ['transition-timing-function', defaultTimingFunction], - ['transition-duration', defaultDuration], - ]) - staticUtility('transition-opacity', [ - ['transition-property', 'opacity'], - ['transition-timing-function', defaultTimingFunction], - ['transition-duration', defaultDuration], - ]) - staticUtility('transition-shadow', [ - ['transition-property', 'box-shadow'], - ['transition-timing-function', defaultTimingFunction], - ['transition-duration', defaultDuration], - ]) - staticUtility('transition-transform', [ - ['transition-property', 'transform, translate, scale, rotate'], - ['transition-timing-function', defaultTimingFunction], - ['transition-duration', defaultDuration], - ]) - functionalUtility('transition', { defaultValue: 'color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events', @@ -4515,6 +4544,37 @@ export function createUtilities(theme: Theme) { decl('transition-timing-function', defaultTimingFunction), decl('transition-duration', defaultDuration), ], + staticValues: { + none: [decl('transition-property', 'none')], + all: [ + decl('transition-property', 'all'), + decl('transition-timing-function', defaultTimingFunction), + decl('transition-duration', defaultDuration), + ], + colors: [ + decl( + 'transition-property', + 'color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to', + ), + decl('transition-timing-function', defaultTimingFunction), + decl('transition-duration', defaultDuration), + ], + opacity: [ + decl('transition-property', 'opacity'), + decl('transition-timing-function', defaultTimingFunction), + decl('transition-duration', defaultDuration), + ], + shadow: [ + decl('transition-property', 'box-shadow'), + decl('transition-timing-function', defaultTimingFunction), + decl('transition-duration', defaultDuration), + ], + transform: [ + decl('transition-property', 'transform, translate, scale, rotate'), + decl('transition-timing-function', defaultTimingFunction), + decl('transition-duration', defaultDuration), + ], + }, }) staticUtility('transition-discrete', [['transition-behavior', 'allow-discrete']]) @@ -4590,12 +4650,6 @@ export function createUtilities(theme: Theme) { return atRoot([property('--tw-ease')]) } - staticUtility('ease-initial', [transitionTimingFunctionProperty, ['--tw-ease', 'initial']]) - staticUtility('ease-linear', [ - transitionTimingFunctionProperty, - ['--tw-ease', 'linear'], - ['transition-timing-function', 'linear'], - ]) functionalUtility('ease', { themeKeys: ['--ease'], handle: (value) => [ @@ -4603,6 +4657,14 @@ export function createUtilities(theme: Theme) { decl('--tw-ease', value), decl('transition-timing-function', value), ], + staticValues: { + initial: [transitionTimingFunctionProperty(), decl('--tw-ease', 'initial')], + linear: [ + transitionTimingFunctionProperty(), + decl('--tw-ease', 'linear'), + decl('transition-timing-function', 'linear'), + ], + }, }) } @@ -4683,16 +4745,24 @@ export function createUtilities(theme: Theme) { staticUtility('forced-color-adjust-none', [['forced-color-adjust', 'none']]) staticUtility('forced-color-adjust-auto', [['forced-color-adjust', 'auto']]) - staticUtility('leading-none', [ - () => atRoot([property('--tw-leading')]), - ['--tw-leading', '1'], - ['line-height', '1'], - ]) - spacingUtility('leading', ['--leading', '--spacing'], (value) => [ - atRoot([property('--tw-leading')]), - decl('--tw-leading', value), - decl('line-height', value), - ]) + spacingUtility( + 'leading', + ['--leading', '--spacing'], + (value) => [ + atRoot([property('--tw-leading')]), + decl('--tw-leading', value), + decl('line-height', value), + ], + { + staticValues: { + none: [ + atRoot([property('--tw-leading')]), + decl('--tw-leading', '1'), + decl('line-height', '1'), + ], + }, + }, + ) functionalUtility('tracking', { supportsNegative: true, @@ -4931,7 +5001,6 @@ export function createUtilities(theme: Theme) { }, ]) - staticUtility('underline-offset-auto', [['text-underline-offset', 'auto']]) functionalUtility('underline-offset', { supportsNegative: true, themeKeys: ['--text-underline-offset'], @@ -4940,6 +5009,9 @@ export function createUtilities(theme: Theme) { return `${value}px` }, handle: (value) => [decl('text-underline-offset', value)], + staticValues: { + auto: [decl('text-underline-offset', 'auto')], + }, }) suggest('underline-offset', () => [