Skip to content
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Nothing yet!
### Fixed

- Show warning when using unsupported bare value data type in `--value(…)` ([#17464](https://github.com/tailwindlabs/tailwindcss/pull/17464))

## [4.1.2] - 2025-04-03

Expand Down
30 changes: 29 additions & 1 deletion packages/tailwindcss/src/utilities.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, test } from 'vitest'
import { describe, expect, test, vi } from 'vitest'
import { compile } from '.'
import { compileCss, optimizeCss, run } from './test-utils/run'

Expand Down Expand Up @@ -26530,6 +26530,34 @@ describe('custom utilities', () => {
expect(await compileCss(input, ['tab-foo'])).toEqual('')
})

test('bare values with unsupported data types should result in a warning', async () => {
let spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
let input = css`
@utility paint-* {
paint: --value([color], color);
}

@tailwind utilities;
`

expect(await compileCss(input, ['paint-#0088cc', 'paint-red'])).toMatchInlineSnapshot(`""`)
expect(spy.mock.calls).toMatchInlineSnapshot(`
[
[
"Unsupported bare value data type: "color".
Only valid data types are: "number", "integer", "ratio", "percentage".
",
],
[
"\`\`\`css
--value([color],color)
^^^^^
\`\`\`",
],
]
`)
})

test('resolve literal values', async () => {
let input = css`
@utility tab-* {
Expand Down
48 changes: 41 additions & 7 deletions packages/tailwindcss/src/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5692,6 +5692,16 @@ export function createUtilities(theme: Theme) {
return utilities
}

// Only allowed bare value data types, to prevent creating new syntax that we
// typically don't support right now. E.g.: `--value(color)` would allow you to
// use `text-#0088cc` as a valid utility, which is not what we want.
export const BARE_VALUE_DATA_TYPES = [
'number', // 2.5
'integer', // 8
'ratio', // 2/3
'percentage', // 25%
]

export function createCssUtility(node: AtRule) {
let name = node.params

Expand Down Expand Up @@ -5824,7 +5834,6 @@ export function createCssUtility(node: AtRule) {
}
fn.nodes = ValueParser.parse(args.join(','))

// Track information for suggestions
for (let node of fn.nodes) {
// Track literal values
if (
Expand All @@ -5841,6 +5850,36 @@ export function createCssUtility(node: AtRule) {
let value = node.value.replace(/-\*.*$/g, '') as `--${string}`
storage[fn.value].themeKeys.add(value)
}

// Validate bare value data types
else if (
node.kind === 'word' &&
!(node.value[0] === '[' && node.value[node.value.length - 1] === ']') && // Ignore arbitrary values
!BARE_VALUE_DATA_TYPES.includes(node.value)
) {
console.warn(
`Unsupported bare value data type: "${node.value}".\nOnly valid data types are: ${BARE_VALUE_DATA_TYPES.map((x) => `"${x}"`).join(', ')}.\n`,
)
// TODO: Once we properly track the location of the node, we can
// clean this up in a better way.
let dataType = node.value
let copy = structuredClone(fn)
let sentinelValue = '¶'
ValueParser.walk(copy.nodes, (node, { replaceWith }) => {
if (node.kind === 'word' && node.value === dataType) {
replaceWith({ kind: 'word', value: sentinelValue })
}
})
let underline = '^'.repeat(ValueParser.toCss([node]).length)
let offset = ValueParser.toCss([copy]).indexOf(sentinelValue)
let output = [
'```css',
ValueParser.toCss([fn]),
' '.repeat(offset) + underline,
'```',
].join('\n')
console.warn(output)
}
}
})

Expand Down Expand Up @@ -6084,12 +6123,7 @@ function resolveValueFunction(
// Limit the bare value types, to prevent new syntax that we
// don't want to support. E.g.: `text-#000` is something we
// don't want to support, but could be built this way.
if (
arg.value !== 'number' &&
arg.value !== 'integer' &&
arg.value !== 'ratio' &&
arg.value !== 'percentage'
) {
if (!BARE_VALUE_DATA_TYPES.includes(arg.value)) {
continue
}

Expand Down