diff --git a/CHANGELOG.md b/CHANGELOG.md
index 247fe492c465..c8e0736f714c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+- Add consistent base styles for buttons and form controls ([#15036](https://github.com/tailwindlabs/tailwindcss/pull/15036))
- _Upgrade (experimental)_: Convert `group-[]:flex` to `in-[.group]:flex` ([#15054](https://github.com/tailwindlabs/tailwindcss/pull/15054))
+- _Upgrade (experimental)_: Add form reset styles to CSS files for compatibility with v3 ([#15036](https://github.com/tailwindlabs/tailwindcss/pull/15036))
### Fixed
diff --git a/integrations/upgrade/index.test.ts b/integrations/upgrade/index.test.ts
index f4dbb2ad2c5a..1d91788d3e7e 100644
--- a/integrations/upgrade/index.test.ts
+++ b/integrations/upgrade/index.test.ts
@@ -131,6 +131,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@utility foo {
color: red;
@@ -223,6 +238,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
.btn {
@apply tw:rounded-md! tw:px-2 tw:py-1 tw:bg-blue-500 tw:text-white;
@@ -287,6 +317,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
.a {
@apply flex;
@@ -359,6 +404,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@layer base {
html {
@@ -436,6 +496,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@utility btn {
@apply rounded-md px-2 py-1 bg-blue-500 text-white;
@@ -545,6 +620,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/base.css ---
html {
@@ -1049,6 +1139,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/utilities.css ---
@utility no-scrollbar {
@@ -1497,6 +1602,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/root.2/index.css ---
/* Already contains @config */
@@ -1521,6 +1641,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/root.3/index.css ---
/* Inject missing @config above first @theme */
@@ -1555,6 +1690,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/root.4/index.css ---
/* Inject missing @config due to nested imports with tailwind imports */
@@ -1584,6 +1734,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/root.4/utilities.css ---
@import 'tailwindcss/utilities' layer(utilities);
@@ -1615,6 +1780,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -1795,6 +1975,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@layer base {
html {
@@ -1931,6 +2126,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@layer base {
html {
@@ -2048,6 +2258,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@layer base {
html {
@@ -2123,6 +2348,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/styles/components.css ---
.btn {
@@ -2340,6 +2580,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- index.html ---
@@ -2422,6 +2677,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- index.html ---
@@ -2534,6 +2804,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- ./src/components.css ---
@import './typography.css';
diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts
index e0173bcf367c..e683986d1cb6 100644
--- a/integrations/upgrade/js-config.test.ts
+++ b/integrations/upgrade/js-config.test.ts
@@ -284,6 +284,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- src/test.js ---
export default {
@@ -393,6 +408,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
@@ -475,6 +505,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
@@ -549,6 +594,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
@@ -627,6 +687,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
@@ -701,6 +776,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
@@ -813,6 +903,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- project-b/src/input.css ---
@import 'tailwindcss';
@@ -838,6 +943,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -915,6 +1035,21 @@ test(
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -975,6 +1110,21 @@ describe('border compatibility', () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -1037,6 +1187,21 @@ describe('border compatibility', () => {
border-color: oklch(0.623 0.214 259.815);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -1081,6 +1246,22 @@ describe('border compatibility', () => {
"
--- src/input.css ---
@import 'tailwindcss';
+
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -1141,6 +1322,21 @@ describe('border compatibility', () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
@@ -1210,6 +1406,21 @@ describe('border compatibility', () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
--- src/input.css ---
@import './base.css';
@@ -1328,6 +1539,21 @@ describe('border compatibility', () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
.container {
width: calc(var(--spacing) * 2);
@@ -1442,6 +1668,21 @@ describe('border compatibility', () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
.container {
width: var(--spacing-2);
@@ -1545,6 +1786,21 @@ describe('border compatibility', () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
"
`)
},
diff --git a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap
index 17c977f96e35..263e01037d26 100644
--- a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap
+++ b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap
@@ -393,6 +393,11 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
font-feature-settings: var(--default-font-feature-settings, normal);
font-variation-settings: var(--default-font-variation-settings, normal);
-webkit-tap-highlight-color: transparent;
+ --lightningcss-light: initial;
+ --lightningcss-dark: ;
+ --lightningcss-light: initial;
+ --lightningcss-dark: ;
+ color-scheme: light;
}
body {
@@ -459,7 +464,33 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
border-collapse: collapse;
}
- button, input, optgroup, select, textarea {
+ :-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;
@@ -475,34 +506,13 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
color: inherit;
}
- button, input:where([type="button"], [type="reset"], [type="submit"]) {
- appearance: button;
- background: none;
- }
-
- ::file-selector-button {
- appearance: button;
- background: none;
- }
-
- :-moz-focusring {
- outline: auto;
- }
-
- :-moz-ui-invalid {
- box-shadow: none;
- }
-
- progress {
- vertical-align: baseline;
- }
-
- ::-webkit-inner-spin-button {
- height: auto;
+ ::placeholder {
+ opacity: 1;
+ color: color-mix(in oklch, currentColor 50%, transparent);
}
- ::-webkit-outer-spin-button {
- height: auto;
+ textarea {
+ resize: vertical;
}
::-webkit-search-decoration {
@@ -558,31 +568,107 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
padding-block: 0;
}
- summary {
- display: list-item;
+ :-moz-ui-invalid {
+ box-shadow: none;
}
- ol, ul, menu {
- list-style: none;
+ button, input:where([type="button"], [type="reset"], [type="submit"]) {
+ appearance: button;
}
- textarea {
- resize: vertical;
+ ::file-selector-button {
+ appearance: button;
}
- ::placeholder {
+ ::-webkit-inner-spin-button {
+ height: auto;
+ }
+
+ ::-webkit-outer-spin-button {
+ height: auto;
+ }
+
+ select, textarea, input:where([type="text"], [type="email"], [type="password"], [type="date"], [type="datetime-local"], [type="month"], [type="number"], [type="search"], [type="time"], [type="week"], [type="tel"], [type="url"]) {
+ color: var(--lightningcss-light, #000) var(--lightningcss-dark, #fff);
+ background-color: var(--lightningcss-light, #fff) var(--lightningcss-dark, #ffffff1a);
+ border: 1px solid var(--lightningcss-light, #00000080) var(--lightningcss-dark, #ffffff40);
+ border-radius: 3px;
+ padding-block: 2px;
+ padding-inline: 4px;
+ }
+
+ :where(select:not([multiple], [size])) option, :where(select:not([multiple], [size])) optgroup {
+ color: fieldtext;
+ background-color: field;
+ }
+
+ :where(select:is([multiple], [size])) optgroup {
+ font-weight: bold;
+ }
+
+ :where(select:is([multiple], [size])) optgroup option {
+ padding-inline-start: 20px;
+ }
+
+ select:where(:disabled), textarea:where(:disabled), input:where([type="text"], [type="email"], [type="password"], [type="date"], [type="datetime-local"], [type="month"], [type="number"], [type="search"], [type="time"], [type="week"], [type="tel"], [type="url"]):where(:disabled) {
opacity: 1;
- color: color-mix(in oklch, currentColor 50%, transparent);
+ color: var(--lightningcss-light, #00000080) var(--lightningcss-dark, #ffffff80);
+ background-color: var(--lightningcss-light, #00000006) var(--lightningcss-dark, #ffffff1a);
+ border-color: var(--lightningcss-light, #0003) var(--lightningcss-dark, #ffffff26);
}
- img, svg, video, canvas, audio, iframe, embed, object {
- vertical-align: middle;
- display: block;
+ button, input:where([type="button"], [type="reset"], [type="submit"]) {
+ color: var(--lightningcss-light, #000) var(--lightningcss-dark, #fff);
+ background-color: var(--lightningcss-light, #0000000d) var(--lightningcss-dark, #fff6);
+ border: 1px solid var(--lightningcss-light, #00000080) var(--lightningcss-dark, #ffffff1a);
+ border-radius: 4px;
+ padding-block: 2px;
+ padding-inline: 4px;
}
- img, video {
- max-width: 100%;
- height: auto;
+ ::file-selector-button {
+ color: var(--lightningcss-light, #000) var(--lightningcss-dark, #fff);
+ background-color: var(--lightningcss-light, #0000000d) var(--lightningcss-dark, #fff6);
+ border: 1px solid var(--lightningcss-light, #00000080) var(--lightningcss-dark, #ffffff1a);
+ border-radius: 4px;
+ padding-block: 2px;
+ padding-inline: 4px;
+ }
+
+ button:where(:hover), input:where([type="button"], [type="reset"], [type="submit"]):where(:hover) {
+ background-color: var(--lightningcss-light, #0000001a) var(--lightningcss-dark, #ffffff73);
+ }
+
+ ::file-selector-button:where(:hover) {
+ background-color: var(--lightningcss-light, #0000001a) var(--lightningcss-dark, #ffffff73);
+ }
+
+ button:where(:active), input:where([type="button"], [type="reset"], [type="submit"]):where(:active) {
+ background-color: var(--lightningcss-light, #00000006) var(--lightningcss-dark, #ffffff4d);
+ }
+
+ ::file-selector-button:where(:active) {
+ background-color: var(--lightningcss-light, #00000006) var(--lightningcss-dark, #ffffff4d);
+ }
+
+ button:where(:disabled), input:where([type="button"], [type="reset"], [type="submit"]):where(:disabled) {
+ opacity: 1;
+ background-color: var(--lightningcss-light, #00000006) var(--lightningcss-dark, #ffffff40);
+ border-color: var(--lightningcss-light, #0003) var(--lightningcss-dark, #ffffff1a);
+ }
+
+ :where(input:disabled)::file-selector-button {
+ opacity: 1;
+ background-color: var(--lightningcss-light, #00000006) var(--lightningcss-dark, #ffffff40);
+ border-color: var(--lightningcss-light, #0003) var(--lightningcss-dark, #ffffff1a);
+ }
+
+ input:where([type="file"]:disabled) {
+ color: var(--lightningcss-light, #00000080) var(--lightningcss-dark, #ffffff80);
+ }
+
+ ::file-selector-button {
+ margin-inline-end: 4px;
}
[hidden]:where(:not([hidden="until-found"])) {
diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-forms-reset.test.ts b/packages/@tailwindcss-upgrade/src/codemods/migrate-forms-reset.test.ts
new file mode 100644
index 000000000000..da7118ab223c
--- /dev/null
+++ b/packages/@tailwindcss-upgrade/src/codemods/migrate-forms-reset.test.ts
@@ -0,0 +1,305 @@
+import dedent from 'dedent'
+import postcss from 'postcss'
+import { expect, it } from 'vitest'
+import { formatNodes } from './format-nodes'
+import { migrateFormsReset } from './migrate-forms-reset'
+import { sortBuckets } from './sort-buckets'
+
+const css = dedent
+
+async function migrate(input: string) {
+ return postcss()
+ .use(migrateFormsReset())
+ .use(sortBuckets())
+ .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';
+
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }"
+ `)
+})
+
+it('should add the compatibility CSS after the last `@import`', async () => {
+ expect(
+ await migrate(css`
+ @import 'tailwindcss';
+ @import './foo.css';
+ @import './bar.css';
+ `),
+ ).toMatchInlineSnapshot(`
+ "@import 'tailwindcss';
+ @import './foo.css';
+ @import './bar.css';
+
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }"
+ `)
+})
+
+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, base;
+
+ /**!
+ * License header
+ */
+
+ @import 'tailwindcss';
+ @import './foo.css';
+ @import './bar.css';
+ `),
+ ).toMatchInlineSnapshot(`
+ "@charset "UTF-8";
+ @layer foo, bar, baz, base;
+
+ /**!
+ * License header
+ */
+
+ @import 'tailwindcss';
+ @import './foo.css';
+ @import './bar.css';
+
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }"
+ `)
+})
+
+it('should add the compatibility CSS before the first `@layer base` (if the "tailwindcss" import exists)', async () => {
+ expect(
+ await migrate(css`
+ @import 'tailwindcss';
+
+ @variant foo {
+ }
+
+ @utility bar {
+ }
+
+ @layer base {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+ `),
+ ).toMatchInlineSnapshot(`
+ "@import 'tailwindcss';
+
+ @variant foo {
+ }
+
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
+
+ @utility bar {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+
+ @layer base {
+ }"
+ `)
+})
+
+it('should add the compatibility CSS before the first `@layer base` (if the "tailwindcss/preflight" import exists)', async () => {
+ expect(
+ await migrate(css`
+ @import 'tailwindcss/preflight';
+
+ @variant foo {
+ }
+
+ @utility bar {
+ }
+
+ @layer base {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+ `),
+ ).toMatchInlineSnapshot(`
+ "@import 'tailwindcss/preflight';
+
+ @variant foo {
+ }
+
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
+
+ @utility bar {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+
+ @layer base {
+ }"
+ `)
+})
+
+it('should not add the backwards compatibility CSS when no `@import "tailwindcss"` or `@import "tailwindcss/preflight"` exists', async () => {
+ expect(
+ await migrate(css`
+ @variant foo {
+ }
+
+ @utility bar {
+ }
+
+ @layer base {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+ `),
+ ).toMatchInlineSnapshot(`
+ "@variant foo {
+ }
+
+ @utility bar {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+
+ @layer base {
+ }"
+ `)
+})
+
+it('should not add the backwards compatibility CSS when another `@import "tailwindcss"` import exists such as theme or utilities', async () => {
+ expect(
+ await migrate(css`
+ @import 'tailwindcss/theme';
+
+ @variant foo {
+ }
+
+ @utility bar {
+ }
+
+ @layer base {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+ `),
+ ).toMatchInlineSnapshot(`
+ "@import 'tailwindcss/theme';
+
+ @variant foo {
+ }
+
+ @utility bar {
+ }
+
+ @utility baz {
+ }
+
+ @layer base {
+ }
+
+ @layer base {
+ }"
+ `)
+})
diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-forms-reset.ts b/packages/@tailwindcss-upgrade/src/codemods/migrate-forms-reset.ts
new file mode 100644
index 000000000000..875095318c80
--- /dev/null
+++ b/packages/@tailwindcss-upgrade/src/codemods/migrate-forms-reset.ts
@@ -0,0 +1,49 @@
+import dedent from 'dedent'
+import postcss, { type Plugin, type Root } from 'postcss'
+
+const css = dedent
+
+const FORMS_RESET_CSS = css`
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
+`
+
+export function migrateFormsReset(): Plugin {
+ function migrate(root: Root) {
+ let isTailwindRoot = false
+ root.walkAtRules('import', (node) => {
+ if (
+ /['"]tailwindcss['"]/.test(node.params) ||
+ /['"]tailwindcss\/preflight['"]/.test(node.params)
+ ) {
+ isTailwindRoot = true
+ return false
+ }
+ })
+
+ if (!isTailwindRoot) return
+
+ let compatibilityCssString = `\n@tw-bucket compatibility {\n${FORMS_RESET_CSS}\n}\n`
+ let compatibilityCss = postcss.parse(compatibilityCssString)
+
+ root.append(compatibilityCss)
+ }
+
+ return {
+ postcssPlugin: '@tailwindcss/upgrade/migrate-forms-reset',
+ OnceExit: migrate,
+ }
+}
diff --git a/packages/@tailwindcss-upgrade/src/index.test.ts b/packages/@tailwindcss-upgrade/src/index.test.ts
index 65d74937b42b..1381480714fd 100644
--- a/packages/@tailwindcss-upgrade/src/index.test.ts
+++ b/packages/@tailwindcss-upgrade/src/index.test.ts
@@ -113,6 +113,21 @@ it('should migrate a stylesheet', async () => {
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@utility b {
z-index: 2;
@@ -183,6 +198,21 @@ it('should migrate a stylesheet (with imports)', async () => {
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
+ }
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
}"
`)
})
@@ -226,6 +256,21 @@ it('should migrate a stylesheet (with preceding rules that should be wrapped in
border-color: var(--color-gray-200, currentColor);
}
}
+ /*
+ In Tailwind CSS v4, basic styles are applied to form elements by default. To
+ maintain compatibility with v3, the following resets have been added:
+ */
+ @layer base {
+ input,
+ textarea,
+ select,
+ button {
+ border: 0px solid;
+ border-radius: 0;
+ padding: 0;
+ background-color: transparent;
+ }
+ }
@layer base {
html {
diff --git a/packages/@tailwindcss-upgrade/src/migrate.ts b/packages/@tailwindcss-upgrade/src/migrate.ts
index 1e6af335ab5d..7ddca0e520e3 100644
--- a/packages/@tailwindcss-upgrade/src/migrate.ts
+++ b/packages/@tailwindcss-upgrade/src/migrate.ts
@@ -10,6 +10,7 @@ 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 { migrateFormsReset } from './codemods/migrate-forms-reset'
import { migrateImport } from './codemods/migrate-import'
import { migrateMediaScreen } from './codemods/migrate-media-screen'
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
@@ -51,6 +52,7 @@ export async function migrateContents(
.use(migrateTailwindDirectives(options))
.use(migrateConfig(stylesheet, options))
.use(migrateBorderCompatibility(options))
+ .use(migrateFormsReset())
.use(migrateThemeToVar(options))
.process(stylesheet.root, { from: stylesheet.file ?? undefined })
}
diff --git a/packages/tailwindcss/preflight.css b/packages/tailwindcss/preflight.css
index 6b8cbbae41db..7ccadd35d6ff 100644
--- a/packages/tailwindcss/preflight.css
+++ b/packages/tailwindcss/preflight.css
@@ -23,6 +23,7 @@
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
7. Disable tap highlights on iOS.
+ 8. Set a default `color-scheme`.
*/
html,
@@ -43,6 +44,7 @@ html,
font-feature-settings: var(--default-font-feature-settings, normal); /* 5 */
font-variation-settings: var(--default-font-variation-settings, normal); /* 6 */
-webkit-tap-highlight-color: transparent; /* 7 */
+ color-scheme: light; /* 8 */
}
/*
@@ -175,67 +177,102 @@ table {
}
/*
- Inherit font styles in all browsers.
+ Use the modern Firefox focus style for all focusable elements.
*/
-button,
-input,
-optgroup,
-select,
-textarea,
-::file-selector-button {
- font: inherit;
- font-feature-settings: inherit;
- font-variation-settings: inherit;
- letter-spacing: inherit;
- color: inherit;
+:-moz-focusring {
+ outline: auto;
}
/*
- 1. Remove the default background color of buttons by default.
- 2. Correct the inability to style the border radius in iOS Safari.
+ Add the correct vertical alignment in Chrome and Firefox.
*/
-button,
-input:where([type='button'], [type='reset'], [type='submit']),
-::file-selector-button {
- background: transparent; /* 1 */
- appearance: button; /* 2 */
+progress {
+ vertical-align: baseline;
}
/*
- Use the modern Firefox focus style for all focusable elements.
+ Add the correct display in Chrome and Safari.
*/
-:-moz-focusring {
- outline: auto;
+summary {
+ display: list-item;
}
/*
- Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
+ Make lists unstyled by default.
*/
-:-moz-ui-invalid {
- box-shadow: none;
+ol,
+ul,
+menu {
+ list-style: none;
}
/*
- Add the correct vertical alignment in Chrome and Firefox.
+ 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
+ 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
+ This can trigger a poorly considered lint error in some tools but is included by design.
*/
-progress {
- vertical-align: baseline;
+img,
+svg,
+video,
+canvas,
+audio,
+iframe,
+embed,
+object {
+ display: block; /* 1 */
+ vertical-align: middle; /* 2 */
}
/*
- Correct the cursor style of increment and decrement buttons in Safari.
+ Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
-::-webkit-inner-spin-button,
-::-webkit-outer-spin-button {
+img,
+video {
+ max-width: 100%;
height: auto;
}
+/*
+ Inherit font styles in all browsers.
+*/
+
+button,
+input,
+select,
+optgroup,
+textarea,
+::file-selector-button {
+ font: inherit;
+ font-feature-settings: inherit;
+ font-variation-settings: inherit;
+ letter-spacing: inherit;
+ color: inherit;
+}
+
+/*
+ 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
+ 2. Set the default placeholder color to a semi-transparent version of the current text color.
+*/
+
+::placeholder {
+ opacity: 1; /* 1 */
+ color: color-mix(in oklch, currentColor 50%, transparent); /* 2 */
+}
+
+/*
+ Prevent resizing textareas horizontally by default.
+*/
+
+textarea {
+ resize: vertical;
+}
+
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
@@ -283,67 +320,137 @@ progress {
}
/*
- Add the correct display in Chrome and Safari.
+ Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
-summary {
- display: list-item;
+:-moz-ui-invalid {
+ box-shadow: none;
}
/*
- Make lists unstyled by default.
+ Correct the inability to style the border radius in iOS Safari.
*/
-ol,
-ul,
-menu {
- list-style: none;
+button,
+input:where([type='button'], [type='reset'], [type='submit']),
+::file-selector-button {
+ appearance: button;
}
/*
- Prevent resizing textareas horizontally by default.
+ Correct the cursor style of increment and decrement buttons in Safari.
*/
-textarea {
- resize: vertical;
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
}
/*
- 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
- 2. Set the default placeholder color to a semi-transparent version of the current text color.
+ Apply consistent base form control styles across browsers.
*/
-::placeholder {
- opacity: 1; /* 1 */
- color: color-mix(in oklch, currentColor 50%, transparent); /* 2 */
+select,
+textarea,
+input:where(
+ [type='text'],
+ [type='email'],
+ [type='password'],
+ [type='date'],
+ [type='datetime-local'],
+ [type='month'],
+ [type='number'],
+ [type='search'],
+ [type='time'],
+ [type='week'],
+ [type='tel'],
+ [type='url']
+ ) {
+ border-radius: 3px;
+ padding-inline: 4px;
+ padding-block: 2px;
+ color: light-dark(black, white);
+ background-color: light-dark(white, rgb(255 255 255 / 10%));
+ border: 1px solid light-dark(rgb(0 0 0 / 50%), rgb(255 255 255 / 25%));
+}
+
+:where(select:not([multiple], [size])) option,
+:where(select:not([multiple], [size])) optgroup {
+ color: FieldText;
+ background-color: Field;
+}
+
+:where(select:is([multiple], [size])) optgroup {
+ font-weight: bold;
+}
+
+:where(select:is([multiple], [size])) optgroup option {
+ padding-inline-start: 20px;
+}
+
+select:where(:disabled),
+textarea:where(:disabled),
+input:where(
+ [type='text'],
+ [type='email'],
+ [type='password'],
+ [type='date'],
+ [type='datetime-local'],
+ [type='month'],
+ [type='number'],
+ [type='search'],
+ [type='time'],
+ [type='week'],
+ [type='tel'],
+ [type='url']
+ ):where(:disabled) {
+ opacity: 1;
+ color: light-dark(rgb(0 0 0 / 50%), rgb(255 255 255 / 50%));
+ background-color: light-dark(rgb(0 0 0 / 2.5%), rgb(255 255 255 / 10%));
+ border-color: light-dark(rgb(0 0 0 / 20%), rgb(255 255 255 / 15%));
}
/*
- 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
- 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
- This can trigger a poorly considered lint error in some tools but is included by design.
+ Apply consistent base button styles across browsers.
*/
-img,
-svg,
-video,
-canvas,
-audio,
-iframe,
-embed,
-object {
- display: block; /* 1 */
- vertical-align: middle; /* 2 */
+button,
+::file-selector-button,
+input:where([type='button'], [type='reset'], [type='submit']) {
+ border-radius: 4px;
+ padding-inline: 4px;
+ padding-block: 2px;
+ color: light-dark(#000, #fff);
+ background-color: light-dark(rgb(0 0 0 / 5%), rgb(255 255 255 / 40%));
+ border: 1px solid light-dark(rgb(0 0 0 / 50%), rgb(255 255 255 / 10%));
}
-/*
- Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
-*/
+button:where(:hover),
+::file-selector-button:where(:hover),
+input:where([type='button'], [type='reset'], [type='submit']):where(:hover) {
+ background-color: light-dark(rgb(0 0 0 / 10%), rgb(255 255 255 / 45%));
+}
-img,
-video {
- max-width: 100%;
- height: auto;
+button:where(:active),
+::file-selector-button:where(:active),
+input:where([type='button'], [type='reset'], [type='submit']):where(:active) {
+ background-color: light-dark(rgb(0 0 0 / 2.5%), rgb(255 255 255 / 30%));
+}
+
+button:where(:disabled),
+:where(input:disabled)::file-selector-button,
+input:where([type='button'], [type='reset'], [type='submit']):where(:disabled) {
+ opacity: 1;
+ background-color: light-dark(rgb(0 0 0 / 2.5%), rgb(255 255 255 / 25%));
+ border-color: light-dark(rgb(0 0 0 / 20%), rgb(255 255 255 / 10%));
+}
+
+input:where([type='file']:disabled) {
+ color: light-dark(rgb(0 0 0 / 50%), rgb(255 255 255 / 50%));
+}
+
+::file-selector-button {
+ margin-inline-end: 4px;
}
/*