Support opacity values in increments of 0.25 by default (#14980)

This PR updates all areas in the framework that accept opacity values
(`opacity-*`, `backdrop-opacity-*`, `bg-red-500/*`, etc.) to accept
fractional values in increments of 0.25 instead of just whole numbers.

We noticed we use values like `2.5` and `7.5` pretty regularly in our
templates and don't see why those should be treated as any more "weird"
than something like `opacity-63` which we already support, so baking
this in to core.

IntelliSense will still only suggest values in increments of `5` like it
did before.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
This commit is contained in:
Adam Wathan 2024-11-12 14:04:13 -05:00 committed by GitHub
parent 437579d3f0
commit 5ce575a83c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 362 additions and 13 deletions

View File

@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Nothing yet!
### Added
- Support opacity values in increments of `0.25` by default ([#14980](https://github.com/tailwindlabs/tailwindcss/pull/14980))
## [4.0.0-alpha.33] - 2024-11-11

View File

@ -98,6 +98,18 @@ exports[`border-* 1`] = `
border-color: var(--color-red-500);
}
.border-red-500\\/2\\.5 {
border-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-red-500\\/2\\.25 {
border-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-red-500\\/2\\.75 {
border-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-red-500\\/50 {
border-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -219,6 +231,18 @@ exports[`border-b-* 1`] = `
border-bottom-color: var(--color-red-500);
}
.border-b-red-500\\/2\\.5 {
border-bottom-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-b-red-500\\/2\\.25 {
border-bottom-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-b-red-500\\/2\\.75 {
border-bottom-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-b-red-500\\/50 {
border-bottom-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -340,6 +364,18 @@ exports[`border-e-* 1`] = `
border-inline-end-color: var(--color-red-500);
}
.border-e-red-500\\/2\\.5 {
border-inline-end-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-e-red-500\\/2\\.25 {
border-inline-end-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-e-red-500\\/2\\.75 {
border-inline-end-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-e-red-500\\/50 {
border-inline-end-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -461,6 +497,18 @@ exports[`border-l-* 1`] = `
border-left-color: var(--color-red-500);
}
.border-l-red-500\\/2\\.5 {
border-left-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-l-red-500\\/2\\.25 {
border-left-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-l-red-500\\/2\\.75 {
border-left-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-l-red-500\\/50 {
border-left-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -582,6 +630,18 @@ exports[`border-r-* 1`] = `
border-right-color: var(--color-red-500);
}
.border-r-red-500\\/2\\.5 {
border-right-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-r-red-500\\/2\\.25 {
border-right-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-r-red-500\\/2\\.75 {
border-right-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-r-red-500\\/50 {
border-right-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -703,6 +763,18 @@ exports[`border-s-* 1`] = `
border-inline-start-color: var(--color-red-500);
}
.border-s-red-500\\/2\\.5 {
border-inline-start-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-s-red-500\\/2\\.25 {
border-inline-start-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-s-red-500\\/2\\.75 {
border-inline-start-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-s-red-500\\/50 {
border-inline-start-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -824,6 +896,18 @@ exports[`border-t-* 1`] = `
border-top-color: var(--color-red-500);
}
.border-t-red-500\\/2\\.5 {
border-top-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-t-red-500\\/2\\.25 {
border-top-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-t-red-500\\/2\\.75 {
border-top-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-t-red-500\\/50 {
border-top-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -945,6 +1029,18 @@ exports[`border-x-* 1`] = `
border-inline-color: var(--color-red-500);
}
.border-x-red-500\\/2\\.5 {
border-inline-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-x-red-500\\/2\\.25 {
border-inline-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-x-red-500\\/2\\.75 {
border-inline-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-x-red-500\\/50 {
border-inline-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -1066,6 +1162,18 @@ exports[`border-y-* 1`] = `
border-block-color: var(--color-red-500);
}
.border-y-red-500\\/2\\.5 {
border-block-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.border-y-red-500\\/2\\.25 {
border-block-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.border-y-red-500\\/2\\.75 {
border-block-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.border-y-red-500\\/50 {
border-block-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}

View File

@ -8022,6 +8022,9 @@ test('accent', async () => {
[
'accent-red-500',
'accent-red-500/50',
'accent-red-500/2.25',
'accent-red-500/2.5',
'accent-red-500/2.75',
'accent-red-500/[0.5]',
'accent-red-500/[50%]',
'accent-current',
@ -8065,6 +8068,18 @@ test('accent', async () => {
accent-color: var(--color-red-500);
}
.accent-red-500\\/2\\.5 {
accent-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.accent-red-500\\/2\\.25 {
accent-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.accent-red-500\\/2\\.75 {
accent-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.accent-red-500\\/50, .accent-red-500\\/\\[0\\.5\\], .accent-red-500\\/\\[50\\%\\] {
accent-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -8122,6 +8137,9 @@ test('caret', async () => {
[
'caret-red-500',
'caret-red-500/50',
'caret-red-500/2.25',
'caret-red-500/2.5',
'caret-red-500/2.75',
'caret-red-500/[0.5]',
'caret-red-500/[50%]',
'caret-current',
@ -8165,6 +8183,18 @@ test('caret', async () => {
caret-color: var(--color-red-500);
}
.caret-red-500\\/2\\.5 {
caret-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.caret-red-500\\/2\\.25 {
caret-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.caret-red-500\\/2\\.75 {
caret-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.caret-red-500\\/50, .caret-red-500\\/\\[0\\.5\\], .caret-red-500\\/\\[50\\%\\] {
caret-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -8220,6 +8250,9 @@ test('divide-color', async () => {
[
'divide-red-500',
'divide-red-500/50',
'divide-red-500/2.25',
'divide-red-500/2.5',
'divide-red-500/2.75',
'divide-red-500/[0.5]',
'divide-red-500/[50%]',
'divide-current',
@ -8263,6 +8296,18 @@ test('divide-color', async () => {
border-color: var(--color-red-500);
}
:where(.divide-red-500\\/2\\.5 > :not(:last-child)) {
border-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
:where(.divide-red-500\\/2\\.25 > :not(:last-child)) {
border-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
:where(.divide-red-500\\/2\\.75 > :not(:last-child)) {
border-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
:where(.divide-red-500\\/50 > :not(:last-child)), :where(.divide-red-500\\/\\[0\\.5\\] > :not(:last-child)), :where(.divide-red-500\\/\\[50\\%\\] > :not(:last-child)) {
border-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -9889,6 +9934,9 @@ for (let prefix of prefixes) {
// Color
classes.push(`${prefix}-red-500`)
classes.push(`${prefix}-red-500/50`)
classes.push(`${prefix}-red-500/2.25`)
classes.push(`${prefix}-red-500/2.5`)
classes.push(`${prefix}-red-500/2.75`)
classes.push(`${prefix}-[#0088cc]`)
classes.push(`${prefix}-[#0088cc]/50`)
classes.push(`${prefix}-current`)
@ -9988,6 +10036,9 @@ test('bg', async () => {
// background-color
'bg-red-500',
'bg-red-500/50',
'bg-red-500/2.25',
'bg-red-500/2.5',
'bg-red-500/2.75',
'bg-red-500/[0.5]',
'bg-red-500/[50%]',
'bg-current',
@ -10132,6 +10183,18 @@ test('bg', async () => {
background-color: var(--color-red-500);
}
.bg-red-500\\/2\\.5 {
background-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.bg-red-500\\/2\\.25 {
background-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.bg-red-500\\/2\\.75 {
background-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.bg-red-500\\/50, .bg-red-500\\/\\[0\\.5\\], .bg-red-500\\/\\[50\\%\\] {
background-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -11578,6 +11641,9 @@ test('fill', async () => {
[
'fill-red-500',
'fill-red-500/50',
'fill-red-500/2.25',
'fill-red-500/2.5',
'fill-red-500/2.75',
'fill-red-500/[0.5]',
'fill-red-500/[50%]',
'fill-current',
@ -11621,6 +11687,18 @@ test('fill', async () => {
fill: var(--color-red-500);
}
.fill-red-500\\/2\\.5 {
fill: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.fill-red-500\\/2\\.25 {
fill: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.fill-red-500\\/2\\.75 {
fill: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.fill-red-500\\/50, .fill-red-500\\/\\[0\\.5\\], .fill-red-500\\/\\[50\\%\\] {
fill: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -11664,6 +11742,9 @@ test('stroke', async () => {
// Color
'stroke-red-500',
'stroke-red-500/50',
'stroke-red-500/2.25',
'stroke-red-500/2.5',
'stroke-red-500/2.75',
'stroke-red-500/[0.5]',
'stroke-red-500/[50%]',
'stroke-current',
@ -11747,6 +11828,18 @@ test('stroke', async () => {
stroke: var(--color-red-500);
}
.stroke-red-500\\/2\\.5 {
stroke: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.stroke-red-500\\/2\\.25 {
stroke: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.stroke-red-500\\/2\\.75 {
stroke: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.stroke-red-500\\/50, .stroke-red-500\\/\\[0\\.5\\], .stroke-red-500\\/\\[50\\%\\] {
stroke: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -12667,6 +12760,9 @@ test('placeholder', async () => {
[
'placeholder-red-500',
'placeholder-red-500/50',
'placeholder-red-500/2.25',
'placeholder-red-500/2.5',
'placeholder-red-500/2.75',
'placeholder-red-500/[0.5]',
'placeholder-red-500/[50%]',
'placeholder-current',
@ -12710,6 +12806,18 @@ test('placeholder', async () => {
color: var(--color-red-500);
}
.placeholder-red-500\\/2\\.5::placeholder {
color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.placeholder-red-500\\/2\\.25::placeholder {
color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.placeholder-red-500\\/2\\.75::placeholder {
color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.placeholder-red-500\\/50::placeholder, .placeholder-red-500\\/\\[0\\.5\\]::placeholder, .placeholder-red-500\\/\\[50\\%\\]::placeholder {
color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -13377,6 +13485,9 @@ test('backdrop-filter', async () => {
'backdrop-invert-[var(--value)]',
'backdrop-opacity-50',
'backdrop-opacity-71',
'backdrop-opacity-1.25',
'backdrop-opacity-2.5',
'backdrop-opacity-3.75',
'backdrop-opacity-[0.5]',
'backdrop-saturate-0',
'backdrop-saturate-[1.75]',
@ -13494,6 +13605,24 @@ test('backdrop-filter', async () => {
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-opacity-1\\.25 {
--tw-backdrop-opacity: opacity(1.25%);
-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, );
}
.backdrop-opacity-2\\.5 {
--tw-backdrop-opacity: opacity(2.5%);
-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, );
}
.backdrop-opacity-3\\.75 {
--tw-backdrop-opacity: opacity(3.75%);
-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, );
}
.backdrop-opacity-50 {
--tw-backdrop-opacity: opacity(50%);
-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, );
@ -14720,8 +14849,28 @@ test('outline-offset', async () => {
})
test('opacity', async () => {
expect(await run(['opacity-15', 'opacity-[var(--value)]'])).toMatchInlineSnapshot(`
".opacity-15 {
expect(
await run([
'opacity-15',
'opacity-2.5',
'opacity-3.25',
'opacity-4.75',
'opacity-[var(--value)]',
]),
).toMatchInlineSnapshot(`
".opacity-2\\.5 {
opacity: .025;
}
.opacity-3\\.25 {
opacity: .0325;
}
.opacity-4\\.75 {
opacity: .0475;
}
.opacity-15 {
opacity: .15;
}
@ -14733,6 +14882,7 @@ test('opacity', async () => {
await run([
'opacity',
'opacity--15',
'opacity-1.125',
'-opacity-15',
'-opacity-[var(--value)]',
'opacity-unknown',
@ -14822,6 +14972,9 @@ test('text', async () => {
// color
'text-red-500',
'text-red-500/50',
'text-red-500/2.25',
'text-red-500/2.5',
'text-red-500/2.75',
'text-red-500/[0.5]',
'text-red-500/[50%]',
'text-current',
@ -14985,6 +15138,18 @@ test('text', async () => {
color: var(--color-red-500);
}
.text-red-500\\/2\\.5 {
color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.text-red-500\\/2\\.25 {
color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.text-red-500\\/2\\.75 {
color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.text-red-500\\/50, .text-red-500\\/\\[0\\.5\\], .text-red-500\\/\\[50\\%\\] {
color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -15044,6 +15209,9 @@ test('shadow', async () => {
// Colors
'shadow-red-500',
'shadow-red-500/50',
'shadow-red-500/2.25',
'shadow-red-500/2.5',
'shadow-red-500/2.75',
'shadow-red-500/[0.5]',
'shadow-red-500/[50%]',
'shadow-current',
@ -15131,6 +15299,18 @@ test('shadow', async () => {
--tw-shadow-color: var(--color-red-500);
}
.shadow-red-500\\/2\\.5 {
--tw-shadow-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.shadow-red-500\\/2\\.25 {
--tw-shadow-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.shadow-red-500\\/2\\.75 {
--tw-shadow-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.shadow-red-500\\/50, .shadow-red-500\\/\\[0\\.5\\], .shadow-red-500\\/\\[50\\%\\] {
--tw-shadow-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -15272,6 +15452,9 @@ test('inset-shadow', async () => {
// Colors
'inset-shadow-red-500',
'inset-shadow-red-500/50',
'inset-shadow-red-500/2.25',
'inset-shadow-red-500/2.5',
'inset-shadow-red-500/2.75',
'inset-shadow-red-500/[0.5]',
'inset-shadow-red-500/[50%]',
'inset-shadow-current',
@ -15359,6 +15542,18 @@ test('inset-shadow', async () => {
--tw-inset-shadow-color: var(--color-red-500);
}
.inset-shadow-red-500\\/2\\.5 {
--tw-inset-shadow-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.inset-shadow-red-500\\/2\\.25 {
--tw-inset-shadow-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.inset-shadow-red-500\\/2\\.75 {
--tw-inset-shadow-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.inset-shadow-red-500\\/50, .inset-shadow-red-500\\/\\[0\\.5\\], .inset-shadow-red-500\\/\\[50\\%\\] {
--tw-inset-shadow-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -15490,6 +15685,9 @@ test('ring', async () => {
'ring-inset',
'ring-red-500',
'ring-red-500/50',
'ring-red-500/2.25',
'ring-red-500/2.5',
'ring-red-500/2.75',
'ring-red-500/[0.5]',
'ring-red-500/[50%]',
'ring-current',
@ -15601,6 +15799,18 @@ test('ring', async () => {
--tw-ring-color: var(--color-red-500);
}
.ring-red-500\\/2\\.5 {
--tw-ring-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.ring-red-500\\/2\\.25 {
--tw-ring-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.ring-red-500\\/2\\.75 {
--tw-ring-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.ring-red-500\\/50, .ring-red-500\\/\\[0\\.5\\], .ring-red-500\\/\\[50\\%\\] {
--tw-ring-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}
@ -15750,6 +15960,9 @@ test('inset-ring', async () => {
// ring color
'inset-ring-red-500',
'inset-ring-red-500/50',
'inset-ring-red-500/2.25',
'inset-ring-red-500/2.5',
'inset-ring-red-500/2.75',
'inset-ring-red-500/[0.5]',
'inset-ring-red-500/[50%]',
'inset-ring-current',
@ -15861,6 +16074,18 @@ test('inset-ring', async () => {
--tw-inset-ring-color: var(--color-red-500);
}
.inset-ring-red-500\\/2\\.5 {
--tw-inset-ring-color: color-mix(in oklch, var(--color-red-500) 2.5%, transparent);
}
.inset-ring-red-500\\/2\\.25 {
--tw-inset-ring-color: color-mix(in oklch, var(--color-red-500) 2.25%, transparent);
}
.inset-ring-red-500\\/2\\.75 {
--tw-inset-ring-color: color-mix(in oklch, var(--color-red-500) 2.75%, transparent);
}
.inset-ring-red-500\\/50, .inset-ring-red-500\\/\\[0\\.5\\], .inset-ring-red-500\\/\\[50\\%\\] {
--tw-inset-ring-color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
}

View File

@ -2,7 +2,12 @@ import { atRoot, atRule, decl, styleRule, type AstNode } from './ast'
import type { Candidate, CandidateModifier, NamedUtilityValue } from './candidate'
import type { Theme, ThemeKey } from './theme'
import { DefaultMap } from './utils/default-map'
import { inferDataType, isPositiveInteger, isValidSpacingMultiplier } from './utils/infer-data-type'
import {
inferDataType,
isPositiveInteger,
isValidOpacityValue,
isValidSpacingMultiplier,
} from './utils/infer-data-type'
import { replaceShadowColors } from './utils/replace-shadow-colors'
import { segment } from './utils/segment'
@ -125,7 +130,7 @@ export function asColor(value: string, modifier: CandidateModifier | null): stri
return withAlpha(value, modifier.value)
}
if (!isPositiveInteger(modifier.value)) {
if (!isValidOpacityValue(modifier.value)) {
return null
}
@ -3395,7 +3400,7 @@ export function createUtilities(theme: Theme) {
functionalUtility('backdrop-opacity', {
themeKeys: ['--backdrop-opacity', '--opacity'],
handleBareValue: ({ value }) => {
if (!isPositiveInteger(value)) return null
if (!isValidOpacityValue(value)) return null
return `${value}%`
},
handle: (value) => [
@ -3849,7 +3854,7 @@ export function createUtilities(theme: Theme) {
functionalUtility('opacity', {
themeKeys: ['--opacity'],
handleBareValue: ({ value }) => {
if (!isPositiveInteger(value)) return null
if (!isValidOpacityValue(value)) return null
return `${value}%`
},
handle: (value) => [decl('opacity', value)],

View File

@ -322,17 +322,26 @@ function isVector(value: string) {
}
/**
* Returns true of the value can be parsed as a positive whole number.
* Returns true if the value can be parsed as a positive whole number.
*/
export function isPositiveInteger(value: any) {
let num = Number(value)
return Number.isInteger(num) && num >= 0 && String(num) === String(value)
}
/**
* Returns true if the value is either a positive whole number or a multiple of 0.25.
*/
export function isValidSpacingMultiplier(value: any) {
let num = Number(value)
return num >= 0 && num % 0.25 === 0 && String(num) === String(value)
return isMultipleOf(value, 0.25)
}
export function isValidOpacityValue(value: any) {
return isMultipleOf(value, 0.25)
}
/**
* Ensures a number (or numeric string) is a multiple of another number, and
* that it has no unnecessary leading or trailing zeros.
*/
function isMultipleOf(value: string | number, divisor: number) {
let num = Number(value)
return num >= 0 && num % divisor === 0 && String(num) === String(value)
}