diff --git a/CHANGELOG.md b/CHANGELOG.md index 759bf95be7c1..1ec07535b622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure `@tailwindcss/cli` in `--watch` mode recovers when a tracked dependency is deleted and restored ([#20137](https://github.com/tailwindlabs/tailwindcss/pull/20137)) - Ensure standalone `@tailwindcss/cli` binaries are ignored when scanning for class candidates ([#20139](https://github.com/tailwindlabs/tailwindcss/pull/20139)) +### Changed + +- Generate `0` instead of `calc(var(--spacing) * 0)` for spacing utilities like `m-0` and `left-0` ([#20196](https://github.com/tailwindlabs/tailwindcss/pull/20196)) +- Generate `var(--spacing)` instead of `calc(var(--spacing) * 1)` for spacing utilities like `m-1` and `left-1` ([#20196](https://github.com/tailwindlabs/tailwindcss/pull/20196)) + ## [4.3.0] - 2026-05-08 ### Added diff --git a/packages/tailwindcss/src/css-functions.test.ts b/packages/tailwindcss/src/css-functions.test.ts index 7096c6bbc75b..0e4146fdff69 100644 --- a/packages/tailwindcss/src/css-functions.test.ts +++ b/packages/tailwindcss/src/css-functions.test.ts @@ -105,6 +105,100 @@ describe('--spacing(…)', () => { `) }) + describe('optimizations', () => { + test('--spacing(…) optimizes the output when the input is `0`', async () => { + expect( + await compileCss(css` + @theme { + --spacing: 0.25rem; + } + + .foo { + margin: --spacing(0); + padding: --spacing(0px); + } + `), + ).toMatchInlineSnapshot(` + " + .foo { + margin: 0; + padding: 0; + } + " + `) + }) + + test('--spacing(…) optimizes the output when the input is `0` (with an inlined theme value)', async () => { + expect( + await compileCss(css` + @theme inline { + --spacing: 0.25rem; + } + + .foo { + margin: --spacing(0); + padding: --spacing(0px); + } + `), + ).toMatchInlineSnapshot(` + " + .foo { + margin: 0; + padding: 0; + } + " + `) + }) + + test('--spacing(…) optimizes the output when the input is `1`', async () => { + expect( + await compileCss(css` + @theme { + --spacing: 0.25rem; + } + + .foo { + margin: --spacing(1); + padding: --spacing(1px); + } + `), + ).toMatchInlineSnapshot(` + " + :root, :host { + --spacing: .25rem; + } + + .foo { + margin: var(--spacing); + padding: var(--spacing); + } + " + `) + }) + + test('--spacing(…) optimizes the output when the input is `1` (with an inlined theme value)', async () => { + expect( + await compileCss(css` + @theme inline { + --spacing: 0.25rem; + } + + .foo { + margin: --spacing(1); + padding: --spacing(1px); + } + `), + ).toMatchInlineSnapshot(` + " + .foo { + margin: .25rem; + padding: .25rem; + } + " + `) + }) + }) + test('--spacing(…) relies on `--spacing` to be defined', async () => { await expect(() => compileCss(css` diff --git a/packages/tailwindcss/src/css-functions.ts b/packages/tailwindcss/src/css-functions.ts index bb3e305e4b0f..66b8b6728a0e 100644 --- a/packages/tailwindcss/src/css-functions.ts +++ b/packages/tailwindcss/src/css-functions.ts @@ -2,6 +2,7 @@ import { Features } from '.' import { type AstNode } from './ast' import type { DesignSystem } from './design-system' import { withAlpha } from './utilities' +import { dimensions } from './utils/dimensions' import { segment } from './utils/segment' import * as ValueParser from './value-parser' import { walk, WalkAction } from './walk' @@ -62,6 +63,22 @@ function spacing( ) } + // Optimization: + // + // - We know that at this point the `--spacing` value must be set. + // - We know that `--spacing` must be set to a `` unit, such as `0.25rem` + // - We can assume that the `--spacing` value is not set to a `0`-like value. + // Otherwise `p-` would calculate as `0` which wouldn't make sense. + // + // - That means that a value of `0` can be replaced by `0` + // - That means that a value of `1` can be replaced by `multiplier` + let valueDimension = dimensions.get(value) + if (valueDimension) { + if (valueDimension[0] === 0) return '0' + if (valueDimension[0] === 1) return multiplier + } + + // No known optimizations available, use full calculation return `calc(${multiplier} * ${value})` } diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index cefcf7d03b92..25344dcf9121 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -2095,7 +2095,7 @@ test('mx', async () => { } .mx-1 { - margin-inline: calc(var(--spacing) * 1); + margin-inline: var(--spacing); } .mx-4 { @@ -2187,7 +2187,7 @@ test('my', async () => { } .my-1 { - margin-block: calc(var(--spacing) * 1); + margin-block: var(--spacing); } .my-2\\.5 { @@ -2275,7 +2275,7 @@ test('mt', async () => { } .mt-1 { - margin-top: calc(var(--spacing) * 1); + margin-top: var(--spacing); } .mt-2\\.5 { @@ -2363,7 +2363,7 @@ test('ms', async () => { } .ms-1 { - margin-inline-start: calc(var(--spacing) * 1); + margin-inline-start: var(--spacing); } .ms-2\\.5 { @@ -2451,7 +2451,7 @@ test('me', async () => { } .me-1 { - margin-inline-end: calc(var(--spacing) * 1); + margin-inline-end: var(--spacing); } .me-2\\.5 { @@ -2539,7 +2539,7 @@ test('mbs', async () => { } .mbs-1 { - margin-block-start: calc(var(--spacing) * 1); + margin-block-start: var(--spacing); } .mbs-2\\.5 { @@ -2627,7 +2627,7 @@ test('mbe', async () => { } .mbe-1 { - margin-block-end: calc(var(--spacing) * 1); + margin-block-end: var(--spacing); } .mbe-2\\.5 { @@ -2715,7 +2715,7 @@ test('mr', async () => { } .mr-1 { - margin-right: calc(var(--spacing) * 1); + margin-right: var(--spacing); } .mr-2\\.5 { @@ -2803,7 +2803,7 @@ test('mb', async () => { } .mb-1 { - margin-bottom: calc(var(--spacing) * 1); + margin-bottom: var(--spacing); } .mb-2\\.5 { @@ -2891,7 +2891,7 @@ test('ml', async () => { } .ml-1 { - margin-left: calc(var(--spacing) * 1); + margin-left: var(--spacing); } .ml-2\\.5 { @@ -9922,9 +9922,22 @@ test('gap-y', async () => { test('space-x', async () => { expect( await run( - ['space-x-4', 'space-x-[4px]', '-space-x-4'], + [ + 'space-x-0', + 'space-x-[0]', + 'space-x-[0px]', + '-space-x-0', + 'space-x-[-0]', + 'space-x-[-0px]', + 'space-x-1', + '-space-x-1', + 'space-x-4', + 'space-x-[4px]', + '-space-x-4', + ], css` @theme { + --spacing: 0.25rem; --spacing-4: 1rem; } @tailwind utilities; @@ -9941,21 +9954,49 @@ test('space-x', async () => { } :root, :host { + --spacing: .25rem; --spacing-4: 1rem; } + :where(.-space-x-0 > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline: 0; + } + + :where(.-space-x-1 > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline-start: calc(calc(var(--spacing) * -1) * var(--tw-space-x-reverse)); + margin-inline-end: calc(calc(var(--spacing) * -1) * calc(1 - var(--tw-space-x-reverse))); + } + :where(.-space-x-4 > :not(:last-child)) { --tw-space-x-reverse: 0; margin-inline-start: calc(calc(var(--spacing-4) * -1) * var(--tw-space-x-reverse)); margin-inline-end: calc(calc(var(--spacing-4) * -1) * calc(1 - var(--tw-space-x-reverse))); } + :where(.space-x-0 > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline: 0; + } + + :where(.space-x-1 > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline-start: calc(var(--spacing) * var(--tw-space-x-reverse)); + margin-inline-end: calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse))); + } + :where(.space-x-4 > :not(:last-child)) { --tw-space-x-reverse: 0; margin-inline-start: calc(var(--spacing-4) * var(--tw-space-x-reverse)); margin-inline-end: calc(var(--spacing-4) * calc(1 - var(--tw-space-x-reverse))); } + :where(.space-x-\\[-0\\] > :not(:last-child)), :where(.space-x-\\[-0px\\] > :not(:last-child)), :where(.space-x-\\[0\\] > :not(:last-child)), :where(.space-x-\\[0px\\] > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline: 0; + } + :where(.space-x-\\[4px\\] > :not(:last-child)) { --tw-space-x-reverse: 0; margin-inline-start: calc(4px * var(--tw-space-x-reverse)); @@ -9975,9 +10016,22 @@ test('space-x', async () => { test('space-y', async () => { expect( await run( - ['space-y-4', 'space-y-[4px]', '-space-y-4'], + [ + 'space-y-0', + 'space-y-[0]', + 'space-y-[0px]', + '-space-y-0', + 'space-y-[-0]', + 'space-y-[-0px]', + 'space-y-1', + '-space-y-1', + 'space-y-4', + 'space-y-[4px]', + '-space-y-4', + ], css` @theme { + --spacing: 0.25rem; --spacing-4: 1rem; } @tailwind utilities; @@ -9994,21 +10048,49 @@ test('space-y', async () => { } :root, :host { + --spacing: .25rem; --spacing-4: 1rem; } + :where(.-space-y-0 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block: 0; + } + + :where(.-space-y-1 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * -1) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * -1) * calc(1 - var(--tw-space-y-reverse))); + } + :where(.-space-y-4 > :not(:last-child)) { --tw-space-y-reverse: 0; margin-block-start: calc(calc(var(--spacing-4) * -1) * var(--tw-space-y-reverse)); margin-block-end: calc(calc(var(--spacing-4) * -1) * calc(1 - var(--tw-space-y-reverse))); } + :where(.space-y-0 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block: 0; + } + + :where(.space-y-1 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(var(--spacing) * var(--tw-space-y-reverse)); + margin-block-end: calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse))); + } + :where(.space-y-4 > :not(:last-child)) { --tw-space-y-reverse: 0; margin-block-start: calc(var(--spacing-4) * var(--tw-space-y-reverse)); margin-block-end: calc(var(--spacing-4) * calc(1 - var(--tw-space-y-reverse))); } + :where(.space-y-\\[-0\\] > :not(:last-child)), :where(.space-y-\\[-0px\\] > :not(:last-child)), :where(.space-y-\\[0\\] > :not(:last-child)), :where(.space-y-\\[0px\\] > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block: 0; + } + :where(.space-y-\\[4px\\] > :not(:last-child)) { --tw-space-y-reverse: 0; margin-block-start: calc(4px * var(--tw-space-y-reverse)); @@ -10076,59 +10158,66 @@ test('space-y-reverse', async () => { }) test('divide-x', async () => { - expect(await run(['divide-x', 'divide-x-4', 'divide-x-123', 'divide-x-[4px]'])) + expect(await run(['divide-x', 'divide-x-0', 'divide-x-4', 'divide-x-123', 'divide-x-[4px]'])) .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-divide-x-reverse: 0; - --tw-border-style: solid; + " + @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-divide-x-reverse: 0; + --tw-border-style: solid; + } } } - } - :where(.divide-x > :not(:last-child)) { - --tw-divide-x-reverse: 0; - border-inline-style: var(--tw-border-style); - border-inline-start-width: calc(1px * var(--tw-divide-x-reverse)); - border-inline-end-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); - } + :where(.divide-x > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(1px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); + } - :where(.divide-x-4 > :not(:last-child)) { - --tw-divide-x-reverse: 0; - border-inline-style: var(--tw-border-style); - border-inline-start-width: calc(4px * var(--tw-divide-x-reverse)); - border-inline-end-width: calc(4px * calc(1 - var(--tw-divide-x-reverse))); - } + :where(.divide-x-0 > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(0px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc(0px * calc(1 - var(--tw-divide-x-reverse))); + } - :where(.divide-x-123 > :not(:last-child)) { - --tw-divide-x-reverse: 0; - border-inline-style: var(--tw-border-style); - border-inline-start-width: calc(123px * var(--tw-divide-x-reverse)); - border-inline-end-width: calc(123px * calc(1 - var(--tw-divide-x-reverse))); - } + :where(.divide-x-4 > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(4px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc(4px * calc(1 - var(--tw-divide-x-reverse))); + } - :where(.divide-x-\\[4px\\] > :not(:last-child)) { - --tw-divide-x-reverse: 0; - border-inline-style: var(--tw-border-style); - border-inline-start-width: calc(4px * var(--tw-divide-x-reverse)); - border-inline-end-width: calc(4px * calc(1 - var(--tw-divide-x-reverse))); - } + :where(.divide-x-123 > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(123px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc(123px * calc(1 - var(--tw-divide-x-reverse))); + } - @property --tw-divide-x-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; - } + :where(.divide-x-\\[4px\\] > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(4px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc(4px * calc(1 - var(--tw-divide-x-reverse))); + } - @property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; - } - " - `) + @property --tw-divide-x-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; + } + + @property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; + } + " + `) expect( await run([ '-divide-x', @@ -10190,63 +10279,71 @@ test('divide-x with custom default border width', async () => { }) test('divide-y', async () => { - expect(await run(['divide-y', 'divide-y-4', 'divide-y-123', 'divide-y-[4px]'])) + expect(await run(['divide-y', 'divide-y-0', 'divide-y-4', 'divide-y-123', 'divide-y-[4px]'])) .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-divide-y-reverse: 0; - --tw-border-style: solid; + " + @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-divide-y-reverse: 0; + --tw-border-style: solid; + } } } - } - :where(.divide-y > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(1px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - } + :where(.divide-y > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(1px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + } - :where(.divide-y-4 > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(4px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); - } + :where(.divide-y-0 > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(0px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); + } - :where(.divide-y-123 > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(123px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(123px * calc(1 - var(--tw-divide-y-reverse))); - } + :where(.divide-y-4 > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(4px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); + } - :where(.divide-y-\\[4px\\] > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(4px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); - } + :where(.divide-y-123 > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(123px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(123px * calc(1 - var(--tw-divide-y-reverse))); + } - @property --tw-divide-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; - } + :where(.divide-y-\\[4px\\] > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(4px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(4px * calc(1 - var(--tw-divide-y-reverse))); + } - @property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; - } - " - `) + @property --tw-divide-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; + } + + @property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; + } + " + `) expect( await run([ '-divide-y', @@ -16014,7 +16111,7 @@ test('mask-t-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-top: linear-gradient(to top, var(--tw-mask-top-from-color) var(--tw-mask-top-from-position), var(--tw-mask-top-to-color) var(--tw-mask-top-to-position)); - --tw-mask-top-from-position: calc(var(--spacing) * 0); + --tw-mask-top-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -16274,7 +16371,7 @@ test('mask-t-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-top: linear-gradient(to top, var(--tw-mask-top-from-color) var(--tw-mask-top-from-position), var(--tw-mask-top-to-color) var(--tw-mask-top-to-position)); - --tw-mask-top-to-position: calc(var(--spacing) * 0); + --tw-mask-top-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -16535,7 +16632,7 @@ test('mask-r-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-right: linear-gradient(to right, var(--tw-mask-right-from-color) var(--tw-mask-right-from-position), var(--tw-mask-right-to-color) var(--tw-mask-right-to-position)); - --tw-mask-right-from-position: calc(var(--spacing) * 0); + --tw-mask-right-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -16796,7 +16893,7 @@ test('mask-r-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-right: linear-gradient(to right, var(--tw-mask-right-from-color) var(--tw-mask-right-from-position), var(--tw-mask-right-to-color) var(--tw-mask-right-to-position)); - --tw-mask-right-to-position: calc(var(--spacing) * 0); + --tw-mask-right-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -17057,7 +17154,7 @@ test('mask-b-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-bottom: linear-gradient(to bottom, var(--tw-mask-bottom-from-color) var(--tw-mask-bottom-from-position), var(--tw-mask-bottom-to-color) var(--tw-mask-bottom-to-position)); - --tw-mask-bottom-from-position: calc(var(--spacing) * 0); + --tw-mask-bottom-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -17318,7 +17415,7 @@ test('mask-b-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-bottom: linear-gradient(to bottom, var(--tw-mask-bottom-from-color) var(--tw-mask-bottom-from-position), var(--tw-mask-bottom-to-color) var(--tw-mask-bottom-to-position)); - --tw-mask-bottom-to-position: calc(var(--spacing) * 0); + --tw-mask-bottom-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -17579,7 +17676,7 @@ test('mask-l-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-left: linear-gradient(to left, var(--tw-mask-left-from-color) var(--tw-mask-left-from-position), var(--tw-mask-left-to-color) var(--tw-mask-left-to-position)); - --tw-mask-left-from-position: calc(var(--spacing) * 0); + --tw-mask-left-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -17840,7 +17937,7 @@ test('mask-l-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-left: linear-gradient(to left, var(--tw-mask-left-from-color) var(--tw-mask-left-from-position), var(--tw-mask-left-to-color) var(--tw-mask-left-to-position)); - --tw-mask-left-to-position: calc(var(--spacing) * 0); + --tw-mask-left-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -18109,9 +18206,9 @@ test('mask-x-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-right: linear-gradient(to right, var(--tw-mask-right-from-color) var(--tw-mask-right-from-position), var(--tw-mask-right-to-color) var(--tw-mask-right-to-position)); - --tw-mask-right-from-position: calc(var(--spacing) * 0); + --tw-mask-right-from-position: 0; --tw-mask-left: linear-gradient(to left, var(--tw-mask-left-from-color) var(--tw-mask-left-from-position), var(--tw-mask-left-to-color) var(--tw-mask-left-to-position)); - --tw-mask-left-from-position: calc(var(--spacing) * 0); + --tw-mask-left-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -18416,9 +18513,9 @@ test('mask-x-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-right: linear-gradient(to right, var(--tw-mask-right-from-color) var(--tw-mask-right-from-position), var(--tw-mask-right-to-color) var(--tw-mask-right-to-position)); - --tw-mask-right-to-position: calc(var(--spacing) * 0); + --tw-mask-right-to-position: 0; --tw-mask-left: linear-gradient(to left, var(--tw-mask-left-from-color) var(--tw-mask-left-from-position), var(--tw-mask-left-to-color) var(--tw-mask-left-to-position)); - --tw-mask-left-to-position: calc(var(--spacing) * 0); + --tw-mask-left-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -18723,9 +18820,9 @@ test('mask-y-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-top: linear-gradient(to top, var(--tw-mask-top-from-color) var(--tw-mask-top-from-position), var(--tw-mask-top-to-color) var(--tw-mask-top-to-position)); - --tw-mask-top-from-position: calc(var(--spacing) * 0); + --tw-mask-top-from-position: 0; --tw-mask-bottom: linear-gradient(to bottom, var(--tw-mask-bottom-from-color) var(--tw-mask-bottom-from-position), var(--tw-mask-bottom-to-color) var(--tw-mask-bottom-to-position)); - --tw-mask-bottom-from-position: calc(var(--spacing) * 0); + --tw-mask-bottom-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -19030,9 +19127,9 @@ test('mask-y-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear: var(--tw-mask-left), var(--tw-mask-right), var(--tw-mask-bottom), var(--tw-mask-top); --tw-mask-top: linear-gradient(to top, var(--tw-mask-top-from-color) var(--tw-mask-top-from-position), var(--tw-mask-top-to-color) var(--tw-mask-top-to-position)); - --tw-mask-top-to-position: calc(var(--spacing) * 0); + --tw-mask-top-to-position: 0; --tw-mask-bottom: linear-gradient(to bottom, var(--tw-mask-bottom-from-color) var(--tw-mask-bottom-from-position), var(--tw-mask-bottom-to-color) var(--tw-mask-bottom-to-position)); - --tw-mask-bottom-to-position: calc(var(--spacing) * 0); + --tw-mask-bottom-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -19251,8 +19348,16 @@ test('mask-y-to', async () => { }) test('mask-linear', async () => { - expect(await run(['mask-linear-45', 'mask-linear-[3rad]', '-mask-linear-45'])) - .toMatchInlineSnapshot(` + expect( + await run([ + 'mask-linear-0', + 'mask-linear-1', + '-mask-linear-1', + 'mask-linear-45', + 'mask-linear-[3rad]', + '-mask-linear-45', + ]), + ).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)))) { @@ -19269,6 +19374,17 @@ test('mask-linear', async () => { } } + .-mask-linear-1 { + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + --tw-mask-linear: linear-gradient(var(--tw-mask-linear-stops, var(--tw-mask-linear-position))); + --tw-mask-linear-position: -1deg; + -webkit-mask-composite: source-in; + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + .-mask-linear-45 { -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); @@ -19280,6 +19396,28 @@ test('mask-linear', async () => { mask-composite: intersect; } + .mask-linear-0 { + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + --tw-mask-linear: linear-gradient(var(--tw-mask-linear-stops, var(--tw-mask-linear-position))); + --tw-mask-linear-position: 0deg; + -webkit-mask-composite: source-in; + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + + .mask-linear-1 { + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + --tw-mask-linear: linear-gradient(var(--tw-mask-linear-stops, var(--tw-mask-linear-position))); + --tw-mask-linear-position: 1deg; + -webkit-mask-composite: source-in; + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + .mask-linear-45 { -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); @@ -19446,7 +19584,7 @@ test('mask-linear-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear-stops: var(--tw-mask-linear-position), var(--tw-mask-linear-from-color) var(--tw-mask-linear-from-position), var(--tw-mask-linear-to-color) var(--tw-mask-linear-to-position); --tw-mask-linear: linear-gradient(var(--tw-mask-linear-stops)); - --tw-mask-linear-from-position: calc(var(--spacing) * 0); + --tw-mask-linear-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -19686,7 +19824,7 @@ test('mask-linear-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-linear-stops: var(--tw-mask-linear-position), var(--tw-mask-linear-from-color) var(--tw-mask-linear-from-position), var(--tw-mask-linear-to-color) var(--tw-mask-linear-to-position); --tw-mask-linear: linear-gradient(var(--tw-mask-linear-stops)); - --tw-mask-linear-to-position: calc(var(--spacing) * 0); + --tw-mask-linear-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -20175,7 +20313,7 @@ test('mask-radial-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-radial-stops: var(--tw-mask-radial-shape) var(--tw-mask-radial-size) at var(--tw-mask-radial-position), var(--tw-mask-radial-from-color) var(--tw-mask-radial-from-position), var(--tw-mask-radial-to-color) var(--tw-mask-radial-to-position); --tw-mask-radial: radial-gradient(var(--tw-mask-radial-stops)); - --tw-mask-radial-from-position: calc(var(--spacing) * 0); + --tw-mask-radial-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -20429,7 +20567,7 @@ test('mask-radial-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-radial-stops: var(--tw-mask-radial-shape) var(--tw-mask-radial-size) at var(--tw-mask-radial-position), var(--tw-mask-radial-from-color) var(--tw-mask-radial-from-position), var(--tw-mask-radial-to-color) var(--tw-mask-radial-to-position); --tw-mask-radial: radial-gradient(var(--tw-mask-radial-stops)); - --tw-mask-radial-to-position: calc(var(--spacing) * 0); + --tw-mask-radial-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -20606,8 +20744,16 @@ test('mask-radial-to', async () => { }) test('mask-conic', async () => { - expect(await run(['mask-conic-45', 'mask-conic-[3rad]', '-mask-conic-45'])) - .toMatchInlineSnapshot(` + expect( + await run([ + 'mask-conic-0', + 'mask-conic-1', + '-mask-conic-1', + 'mask-conic-45', + 'mask-conic-[3rad]', + '-mask-conic-45', + ]), + ).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)))) { @@ -20624,6 +20770,17 @@ test('mask-conic', async () => { } } + .-mask-conic-1 { + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + --tw-mask-conic: conic-gradient(var(--tw-mask-conic-stops, var(--tw-mask-conic-position))); + --tw-mask-conic-position: -1deg; + -webkit-mask-composite: source-in; + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + .-mask-conic-45 { -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); @@ -20635,6 +20792,28 @@ test('mask-conic', async () => { mask-composite: intersect; } + .mask-conic-0 { + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + --tw-mask-conic: conic-gradient(var(--tw-mask-conic-stops, var(--tw-mask-conic-position))); + --tw-mask-conic-position: 0deg; + -webkit-mask-composite: source-in; + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + + .mask-conic-1 { + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); + --tw-mask-conic: conic-gradient(var(--tw-mask-conic-stops, var(--tw-mask-conic-position))); + --tw-mask-conic-position: 1deg; + -webkit-mask-composite: source-in; + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + .mask-conic-45 { -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); -webkit-mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); @@ -20801,7 +20980,7 @@ test('mask-conic-from', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-conic-stops: from var(--tw-mask-conic-position), var(--tw-mask-conic-from-color) var(--tw-mask-conic-from-position), var(--tw-mask-conic-to-color) var(--tw-mask-conic-to-position); --tw-mask-conic: conic-gradient(var(--tw-mask-conic-stops)); - --tw-mask-conic-from-position: calc(var(--spacing) * 0); + --tw-mask-conic-from-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -21041,7 +21220,7 @@ test('mask-conic-to', async () => { mask-image: var(--tw-mask-linear), var(--tw-mask-radial), var(--tw-mask-conic); --tw-mask-conic-stops: from var(--tw-mask-conic-position), var(--tw-mask-conic-from-color) var(--tw-mask-conic-from-position), var(--tw-mask-conic-to-color) var(--tw-mask-conic-to-position); --tw-mask-conic: conic-gradient(var(--tw-mask-conic-stops)); - --tw-mask-conic-to-position: calc(var(--spacing) * 0); + --tw-mask-conic-to-position: 0; -webkit-mask-composite: source-in; -webkit-mask-composite: source-in; mask-composite: intersect; @@ -22346,7 +22525,7 @@ test('p', async () => { } .p-1 { - padding: calc(var(--spacing) * 1); + padding: var(--spacing); } .p-4 { @@ -22389,7 +22568,7 @@ test('px', async () => { } .px-1 { - padding-inline: calc(var(--spacing) * 1); + padding-inline: var(--spacing); } .px-2\\.5 { @@ -22432,7 +22611,7 @@ test('py', async () => { } .py-1 { - padding-block: calc(var(--spacing) * 1); + padding-block: var(--spacing); } .py-4 { @@ -22475,7 +22654,7 @@ test('pt', async () => { } .pt-1 { - padding-top: calc(var(--spacing) * 1); + padding-top: var(--spacing); } .pt-4 { @@ -22518,7 +22697,7 @@ test('ps', async () => { } .ps-1 { - padding-inline-start: calc(var(--spacing) * 1); + padding-inline-start: var(--spacing); } .ps-4 { @@ -22561,7 +22740,7 @@ test('pe', async () => { } .pe-1 { - padding-inline-end: calc(var(--spacing) * 1); + padding-inline-end: var(--spacing); } .pe-4 { @@ -22604,7 +22783,7 @@ test('pbs', async () => { } .pbs-1 { - padding-block-start: calc(var(--spacing) * 1); + padding-block-start: var(--spacing); } .pbs-4 { @@ -22647,7 +22826,7 @@ test('pbe', async () => { } .pbe-1 { - padding-block-end: calc(var(--spacing) * 1); + padding-block-end: var(--spacing); } .pbe-4 { @@ -22690,7 +22869,7 @@ test('pr', async () => { } .pr-1 { - padding-right: calc(var(--spacing) * 1); + padding-right: var(--spacing); } .pr-4 { @@ -22733,7 +22912,7 @@ test('pb', async () => { } .pb-1 { - padding-bottom: calc(var(--spacing) * 1); + padding-bottom: var(--spacing); } .pb-4 { @@ -22776,7 +22955,7 @@ test('pl', async () => { } .pl-1 { - padding-left: calc(var(--spacing) * 1); + padding-left: var(--spacing); } .pl-4 { @@ -26308,12 +26487,14 @@ test('text', async () => { // font-size / line-height / letter-spacing / font-weight 'text-sm', + 'text-sm/0', 'text-sm/6', 'text-sm/none', 'text-[10px]/none', 'text-sm/snug', 'text-sm/[4px]', 'text-[12px]', + 'text-[12px]/0', 'text-[12px]/6', 'text-[50%]', 'text-[50%]/6', @@ -26357,6 +26538,11 @@ test('text', async () => { line-height: 1; } + .text-\\[12px\\]\\/0 { + font-size: 12px; + line-height: 0; + } + .text-\\[12px\\]\\/6 { font-size: 12px; line-height: calc(var(--spacing) * 6); @@ -26387,6 +26573,11 @@ test('text', async () => { line-height: var(--tw-leading, var(--text-sm--line-height)); } + .text-sm\\/0 { + font-size: var(--text-sm); + line-height: 0; + } + .text-sm\\/6 { font-size: var(--text-sm); line-height: calc(var(--spacing) * 6); diff --git a/packages/tailwindcss/src/utilities.ts b/packages/tailwindcss/src/utilities.ts index 724449ca9566..2d9b4a3c6c82 100644 --- a/packages/tailwindcss/src/utilities.ts +++ b/packages/tailwindcss/src/utilities.ts @@ -15,9 +15,11 @@ import type { DesignSystem } from './design-system' import type { Theme, ThemeKey } from './theme' import { compareBreakpoints } from './utils/compare-breakpoints' import { DefaultMap } from './utils/default-map' +import { dimensions } from './utils/dimensions' import { unescape } from './utils/escape' import { inferDataType, + isLength, isPositiveInteger, isStrictPositiveInteger, isValidOpacityValue, @@ -550,14 +552,14 @@ export function createUtilities(theme: Theme) { if (!multiplier) return null if (!isValidSpacingMultiplier(value)) return null - return `calc(${multiplier} * ${value})` + return `--spacing(${value})` }, handleNegativeBareValue: ({ value }) => { let multiplier = theme.resolve(null, ['--spacing']) if (!multiplier) return null if (!isValidSpacingMultiplier(value)) return null - return `calc(${multiplier} * -${value})` + return `--spacing(-${value})` }, handle, staticValues, @@ -2119,31 +2121,65 @@ export function createUtilities(theme: Theme) { spacingUtility( 'space-x', ['--space', '--spacing'], - (value) => [ - atRoot([property('--tw-space-x-reverse', '0')]), + (value) => { + let zero = (() => { + if (value === '--spacing(0)') return true + if (value === '--spacing(-0)') return true + + let parsed = dimensions.get(value) + if (parsed && parsed[0] === 0 && (parsed[1] === null || isLength(value))) { + return true + } - styleRule(':where(& > :not(:last-child))', [ - decl('--tw-sort', 'row-gap'), - decl('--tw-space-x-reverse', '0'), - decl('margin-inline-start', `calc(${value} * var(--tw-space-x-reverse))`), - decl('margin-inline-end', `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`), - ]), - ], + return false + })() + + return [ + atRoot([property('--tw-space-x-reverse', '0')]), + + styleRule(':where(& > :not(:last-child))', [ + decl('--tw-sort', 'row-gap'), + decl('--tw-space-x-reverse', '0'), + decl('margin-inline-start', zero ? '0' : `calc(${value} * var(--tw-space-x-reverse))`), + decl( + 'margin-inline-end', + zero ? '0' : `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`, + ), + ]), + ] + }, { supportsNegative: true }, ) spacingUtility( 'space-y', ['--space', '--spacing'], - (value) => [ - atRoot([property('--tw-space-y-reverse', '0')]), - styleRule(':where(& > :not(:last-child))', [ - decl('--tw-sort', 'column-gap'), - decl('--tw-space-y-reverse', '0'), - decl('margin-block-start', `calc(${value} * var(--tw-space-y-reverse))`), - decl('margin-block-end', `calc(${value} * calc(1 - var(--tw-space-y-reverse)))`), - ]), - ], + (value) => { + let zero = (() => { + if (value === '--spacing(0)') return true + if (value === '--spacing(-0)') return true + + let parsed = dimensions.get(value) + if (parsed && parsed[0] === 0 && (parsed[1] === null || isLength(value))) { + return true + } + + return false + })() + + return [ + atRoot([property('--tw-space-y-reverse', '0')]), + styleRule(':where(& > :not(:last-child))', [ + decl('--tw-sort', 'column-gap'), + decl('--tw-space-y-reverse', '0'), + decl('margin-block-start', zero ? '0' : `calc(${value} * var(--tw-space-y-reverse))`), + decl( + 'margin-block-end', + zero ? '0' : `calc(${value} * calc(1 - var(--tw-space-y-reverse)))`, + ), + ]), + ] + }, { supportsNegative: true }, ) @@ -3241,7 +3277,7 @@ export function createUtilities(theme: Theme) { if (!multiplier) return if (!isValidSpacingMultiplier(candidate.value.value)) return - return desc.position(`calc(${multiplier} * ${candidate.value.value})`) + return desc.position(`--spacing(${candidate.value.value})`) } case 'percentage': { @@ -3416,13 +3452,19 @@ export function createUtilities(theme: Theme) { defaultValue: null, supportsNegative: true, supportsFractions: false, - handleBareValue(value) { - if (!isPositiveInteger(value.value)) return null - return `calc(1deg * ${value.value})` + handleBareValue({ value }) { + if (!isPositiveInteger(value)) return null + let valueAsNumber = Number(value) + if (valueAsNumber === 0) return '0deg' + if (valueAsNumber === 1) return '1deg' + return `calc(1deg * ${value})` }, - handleNegativeBareValue(value) { - if (!isPositiveInteger(value.value)) return null - return `calc(1deg * -${value.value})` + handleNegativeBareValue({ value }) { + if (!isPositiveInteger(value)) return null + let valueAsNumber = Number(value) + if (valueAsNumber === 0) return '0deg' + if (valueAsNumber === 1) return '-1deg' + return `calc(1deg * -${value})` }, handle: (value) => [ maskPropertiesGradient(), @@ -3634,13 +3676,19 @@ export function createUtilities(theme: Theme) { defaultValue: null, supportsNegative: true, supportsFractions: false, - handleBareValue(value) { - if (!isPositiveInteger(value.value)) return null - return `calc(1deg * ${value.value})` + handleBareValue({ value }) { + if (!isPositiveInteger(value)) return null + let valueAsNumber = Number(value) + if (valueAsNumber === 0) return '0deg' + if (valueAsNumber === 1) return '1deg' + return `calc(1deg * ${value})` }, - handleNegativeBareValue(value) { - if (!isPositiveInteger(value.value)) return null - return `calc(1deg * -${value.value})` + handleNegativeBareValue({ value }) { + if (!isPositiveInteger(value)) return null + let valueAsNumber = Number(value) + if (valueAsNumber === 0) return '0deg' + if (valueAsNumber === 1) return '-1deg' + return `calc(1deg * -${value})` }, handle: (value) => [ maskPropertiesGradient(), @@ -5227,7 +5275,7 @@ export function createUtilities(theme: Theme) { if (!modifier && isValidSpacingMultiplier(candidate.modifier.value)) { let multiplier = theme.resolve(null, ['--spacing']) if (!multiplier) return null - modifier = `calc(${multiplier} * ${candidate.modifier.value})` + modifier = `--spacing(${candidate.modifier.value})` } // Shorthand for `leading-none` @@ -5280,7 +5328,7 @@ export function createUtilities(theme: Theme) { if (!modifier && isValidSpacingMultiplier(candidate.modifier.value)) { let multiplier = theme.resolve(null, ['--spacing']) if (!multiplier) return null - modifier = `calc(${multiplier} * ${candidate.modifier.value})` + modifier = `--spacing(${candidate.modifier.value})` } // Shorthand for `leading-none`