Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure adjacent rules are merged together after handling nesting when generating optimized CSS ([#14873](https://github.com/tailwindlabs/tailwindcss/pull/14873))
- Rebase `url()` inside imported CSS files when using Vite ([#14877](https://github.com/tailwindlabs/tailwindcss/pull/14877))
- Ensure that CSS transforms from other Vite plugins correctly work in full builds (e.g. `:deep()` in Vue) ([#14871](https://github.com/tailwindlabs/tailwindcss/pull/14871))
- Ensure the CSS `theme()` function handles newlines and tabs in its arguments list ([#14917](https://github.com/tailwindlabs/tailwindcss/pull/14917))
- Don't unset keys like `--inset-shadow-*` when unsetting keys like `--inset-*` ([#14906](https://github.com/tailwindlabs/tailwindcss/pull/14906))
- _Upgrade (experimental)_: Install `@tailwindcss/postcss` next to `tailwindcss` ([#14830](https://github.com/tailwindlabs/tailwindcss/pull/14830))
- _Upgrade (experimental)_: Remove whitespace around `,` separator when print arbitrary values ([#14838](https://github.com/tailwindlabs/tailwindcss/pull/14838))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ function substituteFunctionsInValue(
if (node.kind === 'function' && node.value === 'theme') {
if (node.nodes.length < 1) return

// Ignore whitespace before the first argument
if (node.nodes[0].kind === 'separator' && node.nodes[0].value.trim() === '') {
node.nodes.shift()
}

let pathNode = node.nodes[0]
if (pathNode.kind !== 'word') return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ function substituteFunctionsInValue(
if (node.kind === 'function' && node.value === 'theme') {
if (node.nodes.length < 1) return

// Ignore whitespace before the first argument
if (node.nodes[0].kind === 'separator' && node.nodes[0].value.trim() === '') {
node.nodes.shift()
}

let pathNode = node.nodes[0]
if (pathNode.kind !== 'word') return

Expand Down
20 changes: 20 additions & 0 deletions packages/tailwindcss/src/css-functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,26 @@ describe('theme function', () => {
}"
`)
})

test('theme(\n\tfontFamily.unknown,\n\tHelvetica Neue,\n\tHelvetica,\n\tsans-serif\n)', async () => {
expect(
// prettier-ignore
await compileCss(css`
.fam {
font-family: theme(
fontFamily.unknown,
Helvetica Neue,
Helvetica,
sans-serif
);
}
`),
).toMatchInlineSnapshot(`
".fam {
font-family: Helvetica Neue, Helvetica, sans-serif;
}"
`)
})
})

describe('recursive theme()', () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/tailwindcss/src/css-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export function substituteFunctionsInValue(
)
}

// Ignore whitespace before the first argument
if (node.nodes[0].kind === 'separator' && node.nodes[0].value.trim() === '') {
node.nodes.shift()
}

let pathNode = node.nodes[0]
if (pathNode.kind !== 'word') {
throw new Error(
Expand Down
16 changes: 16 additions & 0 deletions packages/tailwindcss/src/value-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ describe('parse', () => {
])
})

it('should parse a function with multiple arguments across lines', () => {
expect(parse('theme(\n\tfoo,\n\tbar\n)')).toEqual([
{
kind: 'function',
value: 'theme',
nodes: [
{ kind: 'separator', value: '\n\t' },
{ kind: 'word', value: 'foo' },
{ kind: 'separator', value: ',\n\t' },
{ kind: 'word', value: 'bar' },
{ kind: 'separator', value: '\n' },
],
},
])
})

it('should parse a function with nested arguments', () => {
expect(parse('theme(foo, theme(bar))')).toEqual([
{
Expand Down
30 changes: 18 additions & 12 deletions packages/tailwindcss/src/value-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ const CLOSE_PAREN = 0x29
const COLON = 0x3a
const COMMA = 0x2c
const DOUBLE_QUOTE = 0x22
const EQUALS = 0x3d
const GREATER_THAN = 0x3e
const LESS_THAN = 0x3c
const NEWLINE = 0x0a
const OPEN_PAREN = 0x28
const SINGLE_QUOTE = 0x27
const SPACE = 0x20
const LESS_THAN = 0x3c
const GREATER_THAN = 0x3e
const EQUALS = 0x3d
const SLASH = 0x2f
const SPACE = 0x20
const TAB = 0x09

export function parse(input: string) {
input = input.replaceAll('\r\n', '\n')
Expand Down Expand Up @@ -144,11 +146,13 @@ export function parse(input: string) {
// ```
case COLON:
case COMMA:
case SPACE:
case SLASH:
case LESS_THAN:
case EQUALS:
case GREATER_THAN:
case EQUALS: {
case LESS_THAN:
case NEWLINE:
case SLASH:
case SPACE:
case TAB: {
// 1. Handle everything before the separator as a word
// Handle everything before the closing paren as a word
if (buffer.length > 0) {
Expand All @@ -169,11 +173,13 @@ export function parse(input: string) {
if (
peekChar !== COLON &&
peekChar !== COMMA &&
peekChar !== SPACE &&
peekChar !== SLASH &&
peekChar !== LESS_THAN &&
peekChar !== EQUALS &&
peekChar !== GREATER_THAN &&
peekChar !== EQUALS
peekChar !== LESS_THAN &&
peekChar !== NEWLINE &&
peekChar !== SLASH &&
peekChar !== SPACE &&
peekChar !== TAB
) {
break
}
Expand Down