Skip to content
Merged
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix `border-[12px_4px]` being interpreted as a `border-color` instead of a `border-width` ([#17248](https://github.com/tailwindlabs/tailwindcss/pull/17248))
- Use the `oklab(…)` function when applying opacity to `currentColor` to work around a crash in Safari 16.4 and 16.5 ([#17247](https://github.com/tailwindlabs/tailwindcss/pull/17247))
- Pre-process `<template lang="…">` in Vue files ([#17252](https://github.com/tailwindlabs/tailwindcss/pull/17252))
- Ensure that all CSS variables used by preflight are prefixed ([#17036](https://github.com/tailwindlabs/tailwindcss/pull/17036))

### Changed

- The `--theme(…)` function now returns CSS variables from your theme variables unless used inside positions where CSS variables are invalid (e.g. inside `@media` queries) ([#17036](https://github.com/tailwindlabs/tailwindcss/pull/17036))

## [4.0.14] - 2025-03-13

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
--text-2xl--line-height: calc(2 / 1.5);
--font-weight-bold: 700;
--default-font-family: var(--font-sans);
--default-font-feature-settings: var(--font-sans--font-feature-settings);
--default-font-variation-settings: var(--font-sans--font-variation-settings);
--default-mono-font-family: var(--font-mono);
--default-mono-font-feature-settings: var(--font-mono--font-feature-settings);
--default-mono-font-variation-settings: var(--font-mono--font-variation-settings);
Comment on lines -13 to -17
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the --default-font-feature-settings theme variable is defined as:

@theme {
  --default-font-feature-settings: --theme(--font-sans--font-feature-settings, initial);
}

The value is resolved to initial (and thus omitted) because we know that --font-sans--font-feature-settings is not defined in the theme. Note that this implies that values referenced via --theme(…) can not be defined at runtime only and NEED to be in the @theme block. I think this is a reasonable thing to do since we also error when it's not in the @theme block (unless a fallback is specified like it is here).

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function migrateMediaScreen({
let screens = resolvedConfig?.theme?.screens || {}

let mediaQueries = new DefaultMap<string, string | null>((name) => {
let value = designSystem?.resolveThemeValue(`--breakpoint-${name}`) ?? screens?.[name]
let value = designSystem?.resolveThemeValue(`--breakpoint-${name}`, true) ?? screens?.[name]
if (typeof value === 'string') return `(width >= theme(--breakpoint-${name}))`
return value ? buildMediaQuery(value) : null
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ export async function legacyClasses(

if (fromThemeKey && toThemeKey) {
// Migrating something that resolves to a value in the theme.
let customFrom = designSystem.resolveThemeValue(fromThemeKey)
let defaultFrom = defaultDesignSystem.resolveThemeValue(fromThemeKey)
let customTo = designSystem.resolveThemeValue(toThemeKey)
let customFrom = designSystem.resolveThemeValue(fromThemeKey, true)
let defaultFrom = defaultDesignSystem.resolveThemeValue(fromThemeKey, true)
let customTo = designSystem.resolveThemeValue(toThemeKey, true)
let defaultTo = defaultDesignSystem.resolveThemeValue(toThemeKey)

// The new theme value is not defined, which means we can't safely
Expand Down
12 changes: 6 additions & 6 deletions packages/tailwindcss/preflight.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ html,
line-height: 1.5; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
tab-size: 4; /* 3 */
font-family: var(
font-family: --theme(
--default-font-family,
ui-sans-serif,
system-ui,
Expand All @@ -40,8 +40,8 @@ html,
'Segoe UI Symbol',
'Noto Color Emoji'
); /* 4 */
font-feature-settings: var(--default-font-feature-settings, normal); /* 5 */
font-variation-settings: var(--default-font-variation-settings, normal); /* 6 */
font-feature-settings: --theme(--default-font-feature-settings, normal); /* 5 */
font-variation-settings: --theme(--default-font-variation-settings, normal); /* 6 */
-webkit-tap-highlight-color: transparent; /* 7 */
}

Expand Down Expand Up @@ -118,7 +118,7 @@ code,
kbd,
samp,
pre {
font-family: var(
font-family: --theme(
--default-mono-font-family,
ui-monospace,
SFMono-Regular,
Expand All @@ -129,8 +129,8 @@ pre {
'Courier New',
monospace
); /* 1 */
font-feature-settings: var(--default-mono-font-feature-settings, normal); /* 2 */
font-variation-settings: var(--default-mono-font-variation-settings, normal); /* 3 */
font-feature-settings: --theme(--default-mono-font-feature-settings, normal); /* 2 */
font-variation-settings: --theme(--default-mono-font-variation-settings, normal); /* 3 */
font-size: 1em; /* 4 */
}

Expand Down
249 changes: 249 additions & 0 deletions packages/tailwindcss/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,252 @@ exports[`compiling CSS > \`@tailwind utilities\` is replaced by utilities using
initial-value: 0 0 #0000;
}"
`;

exports[`compiling CSS > prefix all CSS variables inside preflight 1`] = `
"@layer theme {
:root, :host {
--tw-font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--tw-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--tw-default-font-family: var(--tw-font-sans);
--tw-default-mono-font-family: var(--tw-font-mono);
}
}

@layer base {
*, :after, :before, ::backdrop {
box-sizing: border-box;
border: 0 solid;
margin: 0;
padding: 0;
}

::file-selector-button {
box-sizing: border-box;
border: 0 solid;
margin: 0;
padding: 0;
}

html, :host {
-webkit-text-size-adjust: 100%;
tab-size: 4;
line-height: 1.5;
font-family: var(--tw-default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
font-feature-settings: var(--tw-default-font-feature-settings, normal);
font-variation-settings: var(--tw-default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent;
}

body {
line-height: inherit;
}

hr {
height: 0;
color: inherit;
border-top-width: 1px;
}

abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}

h1, h2, h3, h4, h5, h6 {
font-size: inherit;
font-weight: inherit;
}

a {
color: inherit;
-webkit-text-decoration: inherit;
-webkit-text-decoration: inherit;
-webkit-text-decoration: inherit;
text-decoration: inherit;
}

b, strong {
font-weight: bolder;
}

code, kbd, samp, pre {
font-family: var(--tw-default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
font-feature-settings: var(--tw-default-mono-font-feature-settings, normal);
font-variation-settings: var(--tw-default-mono-font-variation-settings, normal);
font-size: 1em;
}

small {
font-size: 80%;
}

sub, sup {
vertical-align: baseline;
font-size: 75%;
line-height: 0;
position: relative;
}

sub {
bottom: -.25em;
}

sup {
top: -.5em;
}

table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse;
}

:-moz-focusring {
outline: auto;
}

progress {
vertical-align: baseline;
}

summary {
display: list-item;
}

ol, ul, menu {
list-style: none;
}

img, svg, video, canvas, audio, iframe, embed, object {
vertical-align: middle;
display: block;
}

img, video {
max-width: 100%;
height: auto;
}

button, input, select, optgroup, textarea {
font: inherit;
font-feature-settings: inherit;
font-variation-settings: inherit;
letter-spacing: inherit;
color: inherit;
opacity: 1;
background-color: #0000;
border-radius: 0;
}

::file-selector-button {
font: inherit;
font-feature-settings: inherit;
font-variation-settings: inherit;
letter-spacing: inherit;
color: inherit;
opacity: 1;
background-color: #0000;
border-radius: 0;
}

:where(select:is([multiple], [size])) optgroup {
font-weight: bolder;
}

:where(select:is([multiple], [size])) optgroup option {
padding-inline-start: 20px;
}

::file-selector-button {
margin-inline-end: 4px;
}

::placeholder {
opacity: 1;
color: oklab(from currentColor l a b / 50%);
}

textarea {
resize: vertical;
}

::-webkit-search-decoration {
-webkit-appearance: none;
}

::-webkit-date-and-time-value {
min-height: 1lh;
text-align: inherit;
}

::-webkit-datetime-edit {
display: inline-flex;
}

::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}

::-webkit-datetime-edit {
padding-block: 0;
}

::-webkit-datetime-edit-year-field {
padding-block: 0;
}

::-webkit-datetime-edit-month-field {
padding-block: 0;
}

::-webkit-datetime-edit-day-field {
padding-block: 0;
}

::-webkit-datetime-edit-hour-field {
padding-block: 0;
}

::-webkit-datetime-edit-minute-field {
padding-block: 0;
}

::-webkit-datetime-edit-second-field {
padding-block: 0;
}

::-webkit-datetime-edit-millisecond-field {
padding-block: 0;
}

::-webkit-datetime-edit-meridiem-field {
padding-block: 0;
}

:-moz-ui-invalid {
box-shadow: none;
}

button, input:where([type="button"], [type="reset"], [type="submit"]) {
appearance: button;
}

::file-selector-button {
appearance: button;
}

::-webkit-inner-spin-button {
height: auto;
}

::-webkit-outer-spin-button {
height: auto;
}

[hidden]:where(:not([hidden="until-found"])) {
display: none !important;
}
}

@layer components, utilities;"
`;
7 changes: 7 additions & 0 deletions packages/tailwindcss/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) {

// Track variables defined in `@theme`
if (context.theme && node.property[0] === '-' && node.property[1] === '-') {
// Variables that resolve to `initial` should never be emitted. This can happen because of
// the `--theme(…)` being used and evaluated lazily
if (node.value === 'initial') {
node.value = undefined
return
}

if (!context.keyframes) {
cssThemeVariables.get(parent).add(node)
}
Expand Down
10 changes: 5 additions & 5 deletions packages/tailwindcss/src/compat/apply-compat-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ export async function applyCompatibilityHooks({
// compatibility concerns localized to our compatibility layer.
let resolveThemeVariableValue = designSystem.resolveThemeValue

designSystem.resolveThemeValue = function resolveThemeValue(path: string) {
designSystem.resolveThemeValue = function resolveThemeValue(path: string, forceInline?: boolean) {
if (path.startsWith('--')) {
return resolveThemeVariableValue(path)
return resolveThemeVariableValue(path, forceInline)
}

// If the theme value is not found in the simple resolver, we upgrade to the full backward
Expand All @@ -149,7 +149,7 @@ export async function applyCompatibilityHooks({
configs: [],
pluginDetails: [],
})
return designSystem.resolveThemeValue(path)
return designSystem.resolveThemeValue(path, forceInline)
}

// If there are no plugins or configs registered, we don't need to register
Expand Down Expand Up @@ -260,8 +260,8 @@ function upgradeToFullPluginSupport({
// config files are actually being used. In the future we may want to optimize
// this further by only doing this if plugins or config files _actually_
// registered JS config objects.
designSystem.resolveThemeValue = function resolveThemeValue(path: string, defaultValue?: string) {
let resolvedValue = pluginApi.theme(path, defaultValue)
designSystem.resolveThemeValue = function resolveThemeValue(path: string, forceInline?: boolean) {
let resolvedValue = pluginApi.theme(path, forceInline)

if (Array.isArray(resolvedValue) && resolvedValue.length === 2) {
// When a tuple is returned, return the first element
Expand Down
Loading