Skip to content
Merged
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
94 changes: 94 additions & 0 deletions packages/tailwindcss/src/css-functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
17 changes: 17 additions & 0 deletions packages/tailwindcss/src/css-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 `<length>` unit, such as `0.25rem`
// - We can assume that the `--spacing` value is not set to a `0`-like value.
// Otherwise `p-<anything>` 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})`
}

Expand Down
Loading