Skip to content

Commit 4cacb94

Browse files
committed
Merge branch 'next' into feat/v4-no-named-opacity-modifier-suggestions
2 parents f882552 + d6a67be commit 4cacb94

File tree

7 files changed

+395
-40
lines changed

7 files changed

+395
-40
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Support TypeScript for `@plugin` and `@config` files ([#14317](https://github.com/tailwindlabs/tailwindcss/pull/14317))
13+
- Add `default` option to `@theme` to support overriding default theme values from plugins/JS config files ([#14327](https://github.com/tailwindlabs/tailwindcss/pull/14327))
1314

1415
### Fixed
1516

1617
- Ensure content globs defined in `@config` files are relative to that file ([#14314](https://github.com/tailwindlabs/tailwindcss/pull/14314))
1718
- Ensure CSS `theme()` functions are evaluated in media query ranges with collapsed whitespace ((#14321)[https://github.com/tailwindlabs/tailwindcss/pull/14321])
1819
- Fix support for Nuxt projects in the Vite plugin (requires Nuxt 3.13.1+) ([#14319](https://github.com/tailwindlabs/tailwindcss/pull/14319))
1920
- Don’t suggest named opacity modifiers in intellisense ([#14339](https://github.com/tailwindlabs/tailwindcss/pull/14339))
21+
- Evaluate theme functions in plugins and JS config files ([#14326](https://github.com/tailwindlabs/tailwindcss/pull/14326))
22+
- Ensure theme values overridden with `reference` values don't generate stale CSS variables ([#14327](https://github.com/tailwindlabs/tailwindcss/pull/14327))
2023

2124
## [4.0.0-alpha.21] - 2024-09-02
2225

packages/tailwindcss/src/compat/apply-config-to-theme.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ export function applyConfigToTheme(designSystem: DesignSystem, configs: ConfigFi
1010

1111
designSystem.theme.add(`--${name}`, value as any, {
1212
isInline: true,
13-
isReference: false,
13+
isReference: true,
14+
isDefault: true,
1415
})
1516
}
1617

packages/tailwindcss/src/functions.test.ts

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import fs from 'node:fs/promises'
22
import path from 'node:path'
33
import { describe, expect, test } from 'vitest'
4-
import { compileCss } from './test-utils/run'
4+
import { compile } from '.'
5+
import plugin from './plugin'
6+
import { compileCss, optimizeCss } from './test-utils/run'
57

68
const css = String.raw
79

@@ -618,3 +620,121 @@ describe('theme function', () => {
618620
})
619621
})
620622
})
623+
624+
describe('in plugins', () => {
625+
test('CSS theme functions in plugins are properly evaluated', async () => {
626+
let compiled = await compile(
627+
css`
628+
@layer base, utilities;
629+
@plugin "my-plugin";
630+
@theme reference {
631+
--color-red: red;
632+
--color-orange: orange;
633+
--color-blue: blue;
634+
--color-pink: pink;
635+
}
636+
@layer utilities {
637+
@tailwind utilities;
638+
}
639+
`,
640+
{
641+
async loadPlugin() {
642+
return plugin(({ addBase, addUtilities }) => {
643+
addBase({
644+
'.my-base-rule': {
645+
color: 'theme(colors.red)',
646+
'outline-color': 'theme(colors.orange / 15%)',
647+
'background-color': 'theme(--color-blue)',
648+
'border-color': 'theme(--color-pink / 10%)',
649+
},
650+
})
651+
652+
addUtilities({
653+
'.my-utility': {
654+
color: 'theme(colors.red)',
655+
},
656+
})
657+
})
658+
},
659+
},
660+
)
661+
662+
expect(optimizeCss(compiled.build(['my-utility'])).trim()).toMatchInlineSnapshot(`
663+
"@layer base {
664+
.my-base-rule {
665+
color: red;
666+
background-color: #00f;
667+
border-color: #ffc0cb1a;
668+
outline-color: #ffa50026;
669+
}
670+
}
671+
672+
@layer utilities {
673+
.my-utility {
674+
color: red;
675+
}
676+
}"
677+
`)
678+
})
679+
})
680+
681+
describe('in JS config files', () => {
682+
test('CSS theme functions in config files are properly evaluated', async () => {
683+
let compiled = await compile(
684+
css`
685+
@layer base, utilities;
686+
@config "./my-config.js";
687+
@theme reference {
688+
--color-red: red;
689+
--color-orange: orange;
690+
}
691+
@layer utilities {
692+
@tailwind utilities;
693+
}
694+
`,
695+
{
696+
loadConfig: async () => ({
697+
theme: {
698+
extend: {
699+
colors: {
700+
primary: 'theme(colors.red)',
701+
secondary: 'theme(--color-orange)',
702+
},
703+
},
704+
},
705+
plugins: [
706+
plugin(({ addBase, addUtilities }) => {
707+
addBase({
708+
'.my-base-rule': {
709+
background: 'theme(colors.primary)',
710+
color: 'theme(colors.secondary)',
711+
},
712+
})
713+
714+
addUtilities({
715+
'.my-utility': {
716+
color: 'theme(colors.red)',
717+
},
718+
})
719+
}),
720+
],
721+
}),
722+
},
723+
)
724+
725+
expect(optimizeCss(compiled.build(['my-utility'])).trim()).toMatchInlineSnapshot(`
726+
"@layer base {
727+
.my-base-rule {
728+
color: orange;
729+
background: red;
730+
}
731+
}
732+
733+
@layer utilities {
734+
.my-utility {
735+
color: red;
736+
}
737+
}"
738+
`)
739+
})
740+
})

packages/tailwindcss/src/index.test.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,215 @@ describe('Parsing themes values from CSS', () => {
12921292
}"
12931293
`)
12941294
})
1295+
1296+
test('`default` theme values can be overridden by regular theme values`', async () => {
1297+
expect(
1298+
await compileCss(
1299+
css`
1300+
@theme {
1301+
--color-potato: #ac855b;
1302+
}
1303+
@theme default {
1304+
--color-potato: #efb46b;
1305+
}
1306+
1307+
@tailwind utilities;
1308+
`,
1309+
['bg-potato'],
1310+
),
1311+
).toMatchInlineSnapshot(`
1312+
":root {
1313+
--color-potato: #ac855b;
1314+
}
1315+
1316+
.bg-potato {
1317+
background-color: var(--color-potato, #ac855b);
1318+
}"
1319+
`)
1320+
})
1321+
1322+
test('`default` and `inline` can be used together', async () => {
1323+
expect(
1324+
await compileCss(
1325+
css`
1326+
@theme default inline {
1327+
--color-potato: #efb46b;
1328+
}
1329+
1330+
@tailwind utilities;
1331+
`,
1332+
['bg-potato'],
1333+
),
1334+
).toMatchInlineSnapshot(`
1335+
":root {
1336+
--color-potato: #efb46b;
1337+
}
1338+
1339+
.bg-potato {
1340+
background-color: #efb46b;
1341+
}"
1342+
`)
1343+
})
1344+
1345+
test('`default` and `reference` can be used together', async () => {
1346+
expect(
1347+
await compileCss(
1348+
css`
1349+
@theme default reference {
1350+
--color-potato: #efb46b;
1351+
}
1352+
1353+
@tailwind utilities;
1354+
`,
1355+
['bg-potato'],
1356+
),
1357+
).toMatchInlineSnapshot(`
1358+
".bg-potato {
1359+
background-color: var(--color-potato, #efb46b);
1360+
}"
1361+
`)
1362+
})
1363+
1364+
test('`default`, `inline`, and `reference` can be used together', async () => {
1365+
expect(
1366+
await compileCss(
1367+
css`
1368+
@theme default reference inline {
1369+
--color-potato: #efb46b;
1370+
}
1371+
1372+
@tailwind utilities;
1373+
`,
1374+
['bg-potato'],
1375+
),
1376+
).toMatchInlineSnapshot(`
1377+
".bg-potato {
1378+
background-color: #efb46b;
1379+
}"
1380+
`)
1381+
})
1382+
1383+
test('`default` can be used in `media(…)`', async () => {
1384+
expect(
1385+
await compileCss(
1386+
css`
1387+
@media theme() {
1388+
@theme {
1389+
--color-potato: #ac855b;
1390+
}
1391+
}
1392+
@media theme(default) {
1393+
@theme {
1394+
--color-potato: #efb46b;
1395+
--color-tomato: tomato;
1396+
}
1397+
}
1398+
1399+
@tailwind utilities;
1400+
`,
1401+
['bg-potato', 'bg-tomato'],
1402+
),
1403+
).toMatchInlineSnapshot(`
1404+
":root {
1405+
--color-potato: #ac855b;
1406+
--color-tomato: tomato;
1407+
}
1408+
1409+
.bg-potato {
1410+
background-color: var(--color-potato, #ac855b);
1411+
}
1412+
1413+
.bg-tomato {
1414+
background-color: var(--color-tomato, tomato);
1415+
}"
1416+
`)
1417+
})
1418+
1419+
test('`default` theme values can be overridden by plugin theme values', async () => {
1420+
let { build } = await compile(
1421+
css`
1422+
@theme default {
1423+
--color-red: red;
1424+
}
1425+
@theme {
1426+
--color-orange: orange;
1427+
}
1428+
@plugin "my-plugin";
1429+
@tailwind utilities;
1430+
`,
1431+
{
1432+
loadPlugin: async () => {
1433+
return plugin(({}) => {}, {
1434+
theme: {
1435+
extend: {
1436+
colors: {
1437+
red: 'tomato',
1438+
orange: '#f28500',
1439+
},
1440+
},
1441+
},
1442+
})
1443+
},
1444+
},
1445+
)
1446+
1447+
expect(optimizeCss(build(['text-red', 'text-orange'])).trim()).toMatchInlineSnapshot(`
1448+
":root {
1449+
--color-orange: orange;
1450+
}
1451+
1452+
.text-orange {
1453+
color: var(--color-orange, orange);
1454+
}
1455+
1456+
.text-red {
1457+
color: tomato;
1458+
}"
1459+
`)
1460+
})
1461+
1462+
test('`default` theme values can be overridden by config theme values', async () => {
1463+
let { build } = await compile(
1464+
css`
1465+
@theme default {
1466+
--color-red: red;
1467+
}
1468+
@theme {
1469+
--color-orange: orange;
1470+
}
1471+
@config "./my-config.js";
1472+
@tailwind utilities;
1473+
`,
1474+
{
1475+
loadConfig: async () => {
1476+
return {
1477+
theme: {
1478+
extend: {
1479+
colors: {
1480+
red: 'tomato',
1481+
orange: '#f28500',
1482+
},
1483+
},
1484+
},
1485+
}
1486+
},
1487+
},
1488+
)
1489+
1490+
expect(optimizeCss(build(['text-red', 'text-orange'])).trim()).toMatchInlineSnapshot(`
1491+
":root {
1492+
--color-orange: orange;
1493+
}
1494+
1495+
.text-orange {
1496+
color: var(--color-orange, orange);
1497+
}
1498+
1499+
.text-red {
1500+
color: tomato;
1501+
}"
1502+
`)
1503+
})
12951504
})
12961505

12971506
describe('plugins', () => {

0 commit comments

Comments
 (0)