Add codemod for border style compatibility (#14746)

This PR adds a codemod that ensures that the border styles from Tailwind CSS v3 work as expected once your project is migrated to Tailwind CSS v4.

In Tailwind CSS v3, the default border color is `colors.gray.200` and in Tailwind CSS v4 the default border color is `currentColor`.

Similarly in Tailwind CSS v3, DOM elements such as `input`, `select`, and `textarea` have a border width of `0px`, in Tailwind CSS v4, we don't change the border width of these elements and keep them as `1px`.

If your project happens to already use the same value for the default border color (`currentColor`) as we use in Tailwind CSS v4, then nothing happens. But this is very unlikely, so we will make sure that we honor your `borderColor.DEFAULT` value.

If you didn't change the default values in your `tailwind.config.js`, then we will inject compatibility CSS using the default Tailwind CSS v3 values to ensure the default color and width are applied correctly.
This commit is contained in:
RobinMalfait 2024-10-22 17:41:50 +00:00
parent 816e7307de
commit c6572ab929
9 changed files with 1871 additions and 11 deletions

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}
"
`)
},
)
})

View File

@ -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"
}
}

View File

@ -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 {
}"
`)
})

View File

@ -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
}

View File

@ -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;

View File

@ -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 })
}

6
pnpm-lock.yaml generated
View File

@ -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: