diff --git a/CHANGELOG.md b/CHANGELOG.md index 900f23abe..891e64756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - _Upgrade (experimental)_: Migrate `max-w-screen-*` utilities to `max-w-[var(…)]`([#14754](https://github.com/tailwindlabs/tailwindcss/pull/14754)) - _Upgrade (experimental)_: Migrate `@variants` and `@responsive` directives ([#14748](https://github.com/tailwindlabs/tailwindcss/pull/14748)) - _Upgrade (experimental)_: Migrate `@screen` directive ([#14749](https://github.com/tailwindlabs/tailwindcss/pull/14749)) +- _Upgrade (experimental)_: Generate compatibility styles for legacy default border color ([#14746](https://github.com/tailwindlabs/tailwindcss/pull/14746)) +- _Upgrade (experimental)_: Generate compatibility styles for legacy default border width on form elements ([#14746](https://github.com/tailwindlabs/tailwindcss/pull/14746)) ### Fixed diff --git a/integrations/upgrade/index.test.ts b/integrations/upgrade/index.test.ts index f7ad73c86..bfe40b0b1 100644 --- a/integrations/upgrade/index.test.ts +++ b/integrations/upgrade/index.test.ts @@ -47,6 +47,40 @@ test( --- ./src/input.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @utility foo { color: red; } @@ -108,6 +142,40 @@ test( --- ./src/input.css --- @import 'tailwindcss' prefix(tw); + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + .btn { @apply tw:rounded-md! tw:px-2 tw:py-1 tw:bg-blue-500 tw:text-white; } @@ -154,6 +222,40 @@ test( --- ./src/index.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + .a { @apply flex; } @@ -208,6 +310,41 @@ test( --- ./src/index.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @layer base { html { color: #333; @@ -267,6 +404,40 @@ test( --- ./src/index.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @utility btn { @apply rounded-md px-2 py-1 bg-blue-500 text-white; } @@ -635,6 +806,39 @@ test( @import 'tailwindcss'; @import './utilities.css'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/utilities.css --- @utility no-scrollbar { &::-webkit-scrollbar { @@ -689,6 +893,39 @@ test( @import 'tailwindcss/utilities' layer(utilities); @import './utilities.css'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/utilities.css --- @utility no-scrollbar { &::-webkit-scrollbar { @@ -800,13 +1037,82 @@ test( @import 'tailwindcss/utilities' layer(utilities); @import './a.1.css' layer(utilities); @import './a.1.utilities.1.css'; - @import './b.1.css'; + @import './b.1.css' layer(components); + @import './b.1.utilities.css'; @import './c.1.css'; @import './c.1.utilities.css'; @import './d.1.css'; + @import './d.1.utilities.css'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } --- ./src/a.1.css --- - @import './a.1.utilities.css' + @import './a.1.utilities.css'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } --- ./src/a.1.utilities.1.css --- @import './a.1.utilities.utilities.css'; @@ -830,6 +1136,41 @@ test( } --- ./src/b.1.css --- + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + + --- ./src/b.1.utilities.css --- @import './b.1.components.css'; @utility bar-from-b { color: red; @@ -837,6 +1178,40 @@ test( --- ./src/c.1.css --- @import './c.2.css' layer(utilities); + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } .baz-from-c { color: green; } @@ -846,6 +1221,40 @@ test( --- ./src/c.2.css --- @import './c.3.css'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } #baz { --keep: me; } @@ -867,12 +1276,121 @@ test( } --- ./src/d.1.css --- - @import './d.2.css' + @import './d.2.css' layer(utilities); + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + + --- ./src/d.1.utilities.css --- + @import './d.2.utilities.css' --- ./src/d.2.css --- - @import './d.3.css' + @import './d.3.css'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + + --- ./src/d.2.utilities.css --- + @import './d.3.utilities.css' --- ./src/d.3.css --- + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + + --- ./src/d.3.utilities.css --- @import './d.4.css' --- ./src/d.4.css --- @@ -938,13 +1456,112 @@ test( @import 'tailwindcss/utilities' layer(utilities); @import './a.1.css' layer(utilities); + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/root.2.css --- @import 'tailwindcss/utilities' layer(utilities); @import './a.1.css' layer(components); + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/root.3.css --- @import 'tailwindcss/utilities' layer(utilities); @import './a.1.css' layer(utilities); + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } " `) }, @@ -1033,16 +1650,115 @@ test( --- ./src/root.1.css --- /* Inject missing @config */ @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../tailwind.config.ts'; --- ./src/root.2.css --- /* Already contains @config */ @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config "../tailwind.config.js"; --- ./src/root.3.css --- /* Inject missing @config above first @theme */ @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../tailwind.config.ts'; @variant hocus (&:hover, &:focus); @@ -1059,21 +1775,189 @@ test( /* Inject missing @config due to nested imports with tailwind imports */ @import './root.4/base.css'; @import './root.4/utilities.css'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../tailwind.config.ts'; --- ./src/root.5.css --- @import './root.5/tailwind.css'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/root.4/base.css --- @import 'tailwindcss/theme' layer(theme); @import 'tailwindcss/preflight' layer(base); + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/root.4/utilities.css --- @import 'tailwindcss/utilities' layer(utilities); + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + --- ./src/root.5/tailwind.css --- /* Inject missing @config in this file, due to full import */ @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../../tailwind.config.ts'; " `) diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index 22e4ace9c..17ebde28e 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -1,4 +1,4 @@ -import { expect } from 'vitest' +import { describe, expect } from 'vitest' import { css, html, json, test, ts } from '../utils' test( @@ -92,6 +92,40 @@ test( --- src/input.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @source '../node_modules/my-external-lib/**/*.{html}'; @variant dark (&:where(.dark, .dark *)); @@ -212,6 +246,40 @@ test( --- src/input.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @plugin '@tailwindcss/typography'; @plugin '../custom-plugin' { is-null: null; @@ -279,6 +347,40 @@ test( --- src/input.css --- @import 'tailwindcss'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @theme { --color-gray-50: oklch(0.985 0 0); --color-gray-100: oklch(0.97 0 0); @@ -345,6 +447,40 @@ test( " --- src/input.css --- @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../tailwind.config.ts'; " `) @@ -403,6 +539,40 @@ test( " --- src/input.css --- @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../tailwind.config.ts'; " `) @@ -457,6 +627,40 @@ test( " --- src/input.css --- @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config '../tailwind.config.ts'; " `) @@ -478,3 +682,215 @@ test( `) }, ) + +describe('border compatibility', () => { + test( + 'migrate border compatibility', + { + fs: { + 'package.json': json` + { + "dependencies": { + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + + // Empty / default config + export default { + theme: { + extend: {}, + }, + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs }) => { + await exec('npx @tailwindcss/upgrade') + + expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + " + `) + }, + ) + + test( + 'migrate border compatibility if a custom border color is used', + { + fs: { + 'package.json': json` + { + "dependencies": { + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + + export default { + theme: { + extend: { + borderColor: ({ colors }) => ({ + DEFAULT: colors.blue[500], + }), + }, + }, + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs }) => { + await exec('npx @tailwindcss/upgrade') + + expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: oklch(0.623 0.214 259.815); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + " + `) + }, + ) + + test( + 'migrate border compatibility if a custom border color is used, that matches the default v4 border color', + { + fs: { + 'package.json': json` + { + "dependencies": { + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + + export default { + theme: { + extend: { + borderColor: ({ colors }) => ({ + DEFAULT: 'currentColor', + }), + }, + }, + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs }) => { + await exec('npx @tailwindcss/upgrade') + + expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + " + `) + }, + ) +}) diff --git a/packages/@tailwindcss-upgrade/package.json b/packages/@tailwindcss-upgrade/package.json index 136435645..b8a61f934 100644 --- a/packages/@tailwindcss-upgrade/package.json +++ b/packages/@tailwindcss-upgrade/package.json @@ -29,6 +29,7 @@ "dependencies": { "@tailwindcss/node": "workspace:^", "@tailwindcss/oxide": "workspace:^", + "dedent": "1.5.3", "enhanced-resolve": "^5.17.1", "globby": "^14.0.2", "jiti": "^2.0.0-beta.3", @@ -44,7 +45,6 @@ }, "devDependencies": { "@types/node": "catalog:", - "@types/postcss-import": "^14.0.3", - "dedent": "1.5.3" + "@types/postcss-import": "^14.0.3" } } diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-border-compatibility.test.ts b/packages/@tailwindcss-upgrade/src/codemods/migrate-border-compatibility.test.ts new file mode 100644 index 000000000..df633df5f --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/migrate-border-compatibility.test.ts @@ -0,0 +1,254 @@ +import { __unstable__loadDesignSystem } from '@tailwindcss/node' +import dedent from 'dedent' +import postcss from 'postcss' +import { expect, it } from 'vitest' +import { formatNodes } from './format-nodes' +import { migrateBorderCompatibility } from './migrate-border-compatibility' + +const css = dedent + +async function migrate(input: string) { + let designSystem = await __unstable__loadDesignSystem( + css` + @import 'tailwindcss'; + `, + { base: __dirname }, + ) + + return postcss() + .use(migrateBorderCompatibility({ designSystem })) + .use(formatNodes()) + .process(input, { from: expect.getState().testPath }) + .then((result) => result.css) +} + +it("should add compatibility CSS after the `@import 'tailwindcss'`", async () => { + expect( + await migrate(css` + @import 'tailwindcss'; + `), + ).toMatchInlineSnapshot(` + "@import 'tailwindcss'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + }" + `) +}) + +it('should add the compatibility CSS after the last `@import`', async () => { + expect( + await migrate(css` + @import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + `), + ).toMatchInlineSnapshot(` + "@import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + }" + `) +}) + +it('should add the compatibility CSS after the last import, even if a body-less `@layer` exists', async () => { + expect( + await migrate(css` + @charset "UTF-8"; + @layer foo, bar, baz; + + /**! + * License header + */ + + @import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + `), + ).toMatchInlineSnapshot(` + "@charset "UTF-8"; + @layer foo, bar, baz; + + /**! + * License header + */ + + @import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + }" + `) +}) + +it('should add the compatibility CSS before the first `@layer base`', async () => { + expect( + await migrate(css` + @import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + + @variant foo { + } + + @utility bar { + } + + @layer base { + } + + @utility baz { + } + + @layer base { + } + `), + ).toMatchInlineSnapshot(` + "@import 'tailwindcss/base'; + @import 'tailwindcss/components'; + @import 'tailwindcss/utilities'; + + @variant foo { + } + + @utility bar { + } + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + + @layer base { + } + + @utility baz { + } + + @layer base { + }" + `) +}) diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-border-compatibility.ts b/packages/@tailwindcss-upgrade/src/codemods/migrate-border-compatibility.ts new file mode 100644 index 000000000..a81465d9c --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/migrate-border-compatibility.ts @@ -0,0 +1,202 @@ +import dedent from 'dedent' +import postcss, { AtRule, type Plugin, type Root } from 'postcss' +import type { Config } from 'tailwindcss' +import { keyPathToCssProperty } from '../../../tailwindcss/src/compat/apply-config-to-theme' +import type { DesignSystem } from '../../../tailwindcss/src/design-system' +import { toKeyPath } from '../../../tailwindcss/src/utils/to-key-path' +import * as ValueParser from '../../../tailwindcss/src/value-parser' + +// Defaults in v4 +const DEFAULT_BORDER_COLOR = 'currentColor' + +const css = dedent +const BORDER_COLOR_COMPATIBILITY_CSS = css` + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: theme(borderColor.DEFAULT); + } + } +` + +const BORDER_WIDTH_COMPATIBILITY_CSS = css` + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } +` + +export function migrateBorderCompatibility({ + designSystem, + userConfig, +}: { + designSystem: DesignSystem + userConfig?: Config +}): Plugin { + // @ts-expect-error + let defaultBorderColor = userConfig?.theme?.borderColor?.DEFAULT + + function canResolveThemeValue(path: string) { + let variable = `--${keyPathToCssProperty(toKeyPath(path))}` as const + return Boolean(designSystem.theme.get([variable])) + } + + function migrate(root: Root) { + let targetNode = null as AtRule | null + + root.walkAtRules((node) => { + if (node.name === 'import') { + targetNode = node + } else if (node.name === 'layer' && node.params === 'base') { + targetNode = node + return false + } + }) + + if (!targetNode) return + + // Figure out the compatibility CSS to inject + let compatibilityCssString = '' + if (defaultBorderColor !== DEFAULT_BORDER_COLOR) { + compatibilityCssString += BORDER_COLOR_COMPATIBILITY_CSS + compatibilityCssString += '\n\n' + } + + compatibilityCssString += BORDER_WIDTH_COMPATIBILITY_CSS + let compatibilityCss = postcss.parse(compatibilityCssString) + + // Replace the `theme(…)` with v3 values if we can't resolve the theme + // value. + compatibilityCss.walkDecls((decl) => { + if (decl.value.includes('theme(')) { + decl.value = substituteFunctionsInValue(ValueParser.parse(decl.value), (path) => { + if (canResolveThemeValue(path)) { + return defaultBorderColor + } else { + if (path === 'borderColor.DEFAULT') { + return 'var(--color-gray-200, currentColor)' + } + } + return null + }) + } + }) + + // Cleanup `--border-color` definition in `theme(…)` + root.walkAtRules('theme', (node) => { + node.walkDecls('--border-color', (decl) => { + decl.remove() + }) + + if (node.nodes?.length === 0) { + node.remove() + } + }) + + // Inject the compatibility CSS + if (targetNode.name === 'import') { + targetNode.after(compatibilityCss) + + let next = targetNode.next() + if (next) next.raws.before = '\n\n' + } else { + let rawsBefore = compatibilityCss.last?.raws.before + + targetNode.before(compatibilityCss) + + let prev = targetNode.prev() + if (prev) prev.raws.before = rawsBefore + } + } + + return { + postcssPlugin: '@tailwindcss/upgrade/migrate-border-compatibility', + OnceExit: migrate, + } +} + +function substituteFunctionsInValue( + ast: ValueParser.ValueAstNode[], + handle: (value: string, fallback?: string) => string | null, +) { + ValueParser.walk(ast, (node, { replaceWith }) => { + if (node.kind === 'function' && node.value === 'theme') { + if (node.nodes.length < 1) return + + let pathNode = node.nodes[0] + if (pathNode.kind !== 'word') return + + let path = pathNode.value + + // For the theme function arguments, we require all separators to contain + // comma (`,`), spaces alone should be merged into the previous word to + // avoid splitting in this case: + // + // theme(--color-red-500 / 75%) theme(--color-red-500 / 75%, foo, bar) + // + // We only need to do this for the first node, as the fallback values are + // passed through as-is. + let skipUntilIndex = 1 + for (let i = skipUntilIndex; i < node.nodes.length; i++) { + if (node.nodes[i].value.includes(',')) { + break + } + path += ValueParser.toCss([node.nodes[i]]) + skipUntilIndex = i + 1 + } + + path = eventuallyUnquote(path) + let fallbackValues = node.nodes.slice(skipUntilIndex + 1) + + let replacement = + fallbackValues.length > 0 ? handle(path, ValueParser.toCss(fallbackValues)) : handle(path) + if (replacement === null) return + + replaceWith(ValueParser.parse(replacement)) + } + }) + + return ValueParser.toCss(ast) +} + +function eventuallyUnquote(value: string) { + if (value[0] !== "'" && value[0] !== '"') return value + + let unquoted = '' + let quoteChar = value[0] + for (let i = 1; i < value.length - 1; i++) { + let currentChar = value[i] + let nextChar = value[i + 1] + + if (currentChar === '\\' && (nextChar === quoteChar || nextChar === '\\')) { + unquoted += nextChar + i++ + } else { + unquoted += currentChar + } + } + + return unquoted +} diff --git a/packages/@tailwindcss-upgrade/src/index.test.ts b/packages/@tailwindcss-upgrade/src/index.test.ts index 655eae25b..28b5a36cf 100644 --- a/packages/@tailwindcss-upgrade/src/index.test.ts +++ b/packages/@tailwindcss-upgrade/src/index.test.ts @@ -97,6 +97,41 @@ it('should migrate a stylesheet', async () => { @config './tailwind.config.js'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } + @layer base { html { overflow: hidden; @@ -149,6 +184,39 @@ it('should migrate a stylesheet (with imports)', async () => { @import './my-base.css' layer(base); @import './my-components.css' layer(components); @import './my-utilities.css' layer(utilities); + + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @config './tailwind.config.js';" `) }) @@ -175,6 +243,38 @@ it('should migrate a stylesheet (with preceding rules that should be wrapped in /**! My license comment */ @import 'tailwindcss'; @config './tailwind.config.js'; + /* + The default border color has changed to \`currentColor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + } + /* + Form elements have a 1px border by default in Tailwind CSS v4, so we've + added these compatibility styles to make sure everything still looks the + same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add \`border-0\` to + any form elements that shouldn't have a border. + */ + @layer base { + input:where(:not([type='button'], [type='reset'], [type='submit'])), + select, + textarea { + border-width: 0; + } + } @layer base { html { color: red; diff --git a/packages/@tailwindcss-upgrade/src/migrate.ts b/packages/@tailwindcss-upgrade/src/migrate.ts index 4220904c9..44c0fc545 100644 --- a/packages/@tailwindcss-upgrade/src/migrate.ts +++ b/packages/@tailwindcss-upgrade/src/migrate.ts @@ -6,6 +6,7 @@ import { DefaultMap } from '../../tailwindcss/src/utils/default-map' import { segment } from '../../tailwindcss/src/utils/segment' import { migrateAtApply } from './codemods/migrate-at-apply' import { migrateAtLayerUtilities } from './codemods/migrate-at-layer-utilities' +import { migrateBorderCompatibility } from './codemods/migrate-border-compatibility' import { migrateConfig } from './codemods/migrate-config' import { migrateMediaScreen } from './codemods/migrate-media-screen' import { migrateMissingLayers } from './codemods/migrate-missing-layers' @@ -37,13 +38,14 @@ export async function migrateContents( return postcss() .use(migrateAtApply(options)) - .use(migrateThemeToVar(options)) .use(migrateMediaScreen(options)) .use(migrateVariantsDirective()) .use(migrateAtLayerUtilities(stylesheet)) .use(migrateMissingLayers()) .use(migrateTailwindDirectives(options)) .use(migrateConfig(stylesheet, options)) + .use(migrateBorderCompatibility(options)) + .use(migrateThemeToVar(options)) .process(stylesheet.root, { from: stylesheet.file ?? undefined }) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba225832c..2b0c649ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -279,6 +279,9 @@ importers: '@tailwindcss/oxide': specifier: workspace:^ version: link:../../crates/node + dedent: + specifier: 1.5.3 + version: 1.5.3 enhanced-resolve: specifier: ^5.17.1 version: 5.17.1 @@ -322,9 +325,6 @@ importers: '@types/postcss-import': specifier: ^14.0.3 version: 14.0.3 - dedent: - specifier: 1.5.3 - version: 1.5.3 packages/@tailwindcss-vite: dependencies: