Fixes#16636
This PR enables URL rebasing for PostCSS. Furthermore it fixes an issue
where transitive imports rebased against the importer CSS file instead
of the input CSS file. While fixing this we noticed that this is also
broken in Vite right now and that our integration test swallowed that
when testing because it did not import any Tailwind CSS code and thus
was not considered a Tailwind file.
## Test plan
- Added regression integration tests
- Also validated it against the repro of
https://github.com/tailwindlabs/tailwindcss/issues/16962:
<img width="1149" alt="Screenshot 2025-03-05 at 16 41 01"
src="https://github.com/user-attachments/assets/85396659-d3d0-48c0-b1c7-6125ff8e73ac"
/>
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This PR ensures we bundle the relevant JS APIs in the Standalone CLI
like `tailwindcss`, `tailwindcss/plugin`, `tailwindcss/colors`, etc…
Before, when loading plugins or configs, imports for those resources
would fail.
Fixes#15235
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Fixes#16725
When using `@reference "tailwindcss";` inside a separate CSS root (e.g.
Svelte `<style>` components, CSS modules, etc.), we have no guarantee
that the CSS variables will be defined in the main stylesheet (or if
there even is one). To work around potential issues with this we decided
in #16676 that we would emit all used CSS variables from the `@theme`
inside the `@reference` block.
However, this is not only a bit surprising but also unexpected in CSS
modules and Next.js that **requires CSS module files to only create
scope-able declarations**. To fix this issue, we decided to not emit CSS
variables but instead ensure all `var(…)` calls we create for theme
values in reference mode will simply have their fallback value added.
This ensures styles work as-expected even if the root Tailwind file does
not pick up the variable as being used or _if you don't add a root at
all_. Furthermore we do not duplicate any variable declarations across
your stylesheets and you still have the ability to change variables at
runtime.
## Test plan
- Updated snapshots everywhere (see diff)
- New Next.js CSS modules integration test
Fixes#16732
If we can not get the mtime from a file, chances are that the resource
is a virtual module. This is perfectly legit and we can fall back to
what we did before the changes in `4.0.8` (which is to rebuild the root
every time a change contains a dependency like that).
## Test plan
Added a test to mimic the setup from the repor in #16732. Also ensured
the repro now passes:
<img width="1278" alt="Screenshot 2025-02-24 at 17 29 38"
src="https://github.com/user-attachments/assets/d111273d-579f-44c2-82f5-aa32d6a1879a"
/>
Note that importing virtual modules directly in CSS does not work as the
resolver we use does not resolve against the Vite runtime it seems. This
is unrelated to the regression added in `4.0.8` though and something to
look into in the future.
Right now we sort the nodes based on a pre-defined sort order based on
the properties that are being used. The property sort order is defined
in a list we maintain.
We also have to make sure that the property count is taken into account
such that if all the "sorts" are the same, that we fallback to the
property count. Most amount of properties should be first such that we
can override it with more specific utilities that have fewer properties.
However, if a property doesn't exist, then it wouldn't be included in a
list of properties therefore the total count was off.
This PR fixes that by counting all the used properties. If a property
already exists it is counted twice. E.g.:
```css
.foo {
color: red;
&:hover {
color: blue;
}
}
```
In this case, we have 2 properties, not 1 even though it's the same
`color` property.
## Test plan:
1. Updated the tests that are now sorted correctly
2. Added an integration test to make sure that `prose-invert` is defined
after the `prose-stone` classes when using the `@tailwindcss/typography`
plugin where this problem originated from.
Note how in this play (https://play.tailwindcss.com/wt3LYDaljN) the
`prose-invert` comes _before_ the `prose-stone` which means that you
can't apply the `prose-invert` classes to invert `prose-stone`.
This PR re-enables the changes necessary to remove unused theme
variables and keyframes form your CSS.
This change was initially landed as #16211 and then later reverted in
#16403 because we found some unexpected interactions with using `@apply`
and CSS variables in multi-root setups like CSS modules or Vue inline
`<style>` blocks that were no longer seeing their required variables
defined.
This issue is fixed by now ensuring that theme variables that are
defined within an `@reference "…"` boundary will still be emitted in the
generated CSS when used (as this would otherwise not generate a valid
stylesheet).
So given the following input CSS:
```css
@reference "tailwindcss";
.text-red {
@apply text-red-500;
}
```
We will now compile this to:
```css
@layer theme {
:root, :host {
--text-red-500: oklch(0.637 0.237 25.331);
}
}
.text-red {
color: var(--text-red-500);
}
```
This PR also improves the initial implementation to not mark theme
variables as used if they are only used to define other theme variables.
For example:
```css
@theme {
--font-sans:
ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--font-mono:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
monospace;
--default-font-family: var(--font-sans);
--default-mono-font-family: var(--font-mono);
}
.default-font-family {
font-family: var(--default-font-family);
}
```
This would be reduced to the following now as `--font-mono` is only used
to define another variable and never used outside the theme block:
```css
:root, :host {
--font-sans:
ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--default-font-family: var(--font-sans);
}
.default-font-family {
font-family: var(--default-font-family);
}
```
## Test plan
- See updated unit and integration tests
- Validated it works end-to-end by using a SvelteKit example
The Nuxt preview server always starts on port 3000 even if that port is
taken. With the added tests in #16631 there is now a higher chance these
ports are already taken since e.g. react router prefers to start at port
3000 and so do other servers.
This PR changes this so that we assign a random port inside the test
instead.
## Test plan
- Ensure Windows CI is green again
Alternative to #16425Fixes#16585Fixes#16389Fixes#16252Fixes#15794Fixes#16646Fixes#16358
This PR changes the Vite plugin to use the file-system to discover
potential class names instead of relying on the module-graph. This comes
after a lot of testing and various issue reports where builds that span
different Vite instances were missing class names.
Because we now scan for candidates using the file-system, we can also
remove a lot of the bookkeeping necessary to make production builds and
development builds work as we no longer have to change the resulting
stylesheet based on the `transform` callbacks of other files that might
happen later.
This change comes at a small performance penalty that is noticeable
especially on very large projects with many files to scan. However, we
offset that change by fixing an issue that I found in the current Vite
integration that did a needless rebuild of the whole Tailwind root
whenever any source file changed. Because of how impactful this change
is, I expect many normal to medium sized projects to actually see a
performance improvement after these changes. Furthermore we do plan to
continue to use the module-graph to further improve the performance in
dev mode.
## Test plan
- Added new integration tests with cases found across the issues above.
- Manual testing by adding a local version of the Vite plugin to repos
from the issue list above and the [tailwindcss
playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
Closes#16171
This PR handles `darkMode` variant configs containing braces (so
creating sub-rules) the same way we handle it in the interop layer.
Since the interop layer runs inside the `addVariant` API that we do not
run here, I instead oped to copy the one liner.
## Test plan
Updated one of the migration tests to include a rule that wasn't working
before. Ensured the new output works via
https://play.tailwindcss.com/nR99uhKtv3
This reverts #16211
We found some unexpected interactions with using `@apply` and CSS
variables in multi-root setups like CSS modules or Vue inline `<style>`
blocks that were broken due to that change. We plan to re-enable this
soon and include a proper fix for those scenarios.
## Test plan
- Updated snapshots
- Tested using the CLI in a new project:
<img width="1523" alt="Screenshot 2025-02-10 at 13 08 42"
src="https://github.com/user-attachments/assets/defe0858-adb3-4d61-9d2c-87166558fd68"
/>
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR bumps the Prettier dependencies, and also pins the version.
Noticed that a PR with a single empty commit started failing at the time
of writing this
(https://github.com/tailwindlabs/tailwindcss/pull/16306). This is
because prettier released a new minor version which results in slightly
different output.
Let's bump prettier and handle the differences, but also pin the version
to avoid this in the future.
Fixes#16233
Vite has a number of special parameters that can be appended to `.css`
files that make it actually load as a JavaScript module. One such
parameter that we haven't handled before is the `?commonjs-proxy` flag.
When importing e.g. `plotly.js/lib/core`, the dependency tree would
eventually load a file called `*.css?commonjs-proxy`. We previously
scanned this for candidates even though it was not, in-fact, a
stylesheet.
This PR fixes this by adding the `?commonjs-proxy` to the ignore list. I
have also updated `SPECIAL_QUERY_RE` to more closely match the Vite
implementation. It does seem like this was the only condition we were
missing, though:
2b2299cbac/packages/vite/src/node/plugins/css.ts (L511-L517)
## Test plan
Add and import `plotly.js/lib/core` into a Vite app. I also added an
integration test to do that.
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR is an optimization where it will not emit empty rules and
at-rules. I noticed this while working on
https://github.com/tailwindlabs/tailwindcss/pull/16120 where we emitted:
```css
:root, :host {
}
```
There are some exceptions for "empty" at-rules, such as:
```css
@charset "UTF-8";
@layer foo, bar, baz;
@custom-media --modern (color), (hover);
@namespace "http://www.w3.org/1999/xhtml";
```
These don't have a body, but they still have a purpose and therefore
they will be emitted.
However, if you look at this:
```css
/* Empty rule */
.foo {
}
/* Empty rule, with nesting */
.foo {
.bar {
}
.baz {
}
}
/* Empty rule, with special case '&' rules */
.foo {
& {
&:hover {
}
&:focus {
}
}
}
/* Empty at-rule */
@media (min-width: 768px) {
}
/* Empty at-rule with nesting*/
@media (min-width: 768px) {
.foo {
}
@media (min-width: 1024px) {
.bar {
}
}
}
```
None of these will be emitted.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Fixes#16036
This adds a new rule to treat `<style>` blocks found within `.html` file
as Tailwind CSS targets.
## Test plan
- Tested using the Vite extension (dev) and a new integration test
(prod)
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR fixes na issue where `@keyframes` were emitted if they wre in a
`@theme
reference` and anothe `@theme {}` (that is not a reference) was present.
E.g.:
```css
@reference "tailwindcss";
@theme {
/* ... */
}
```
Produces:
```css
:root, :host {
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes ping {
75%, 100% {
transform: scale(2);
opacity: 0;
}
}
@keyframes pulse {
50% {
opacity: 0.5;
}
}
@keyframes bounce {
0%, 100% {
transform: translateY(-25%);
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
}
50% {
transform: none;
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
}
```
With this PR, the produced CSS looks like this instead:
```css
:root, :host {
}
```
Note: the empty `:root, :host` will be solved in a subsequent PR.
### Test plan
Added some unit tests, and a dedicated integration test.
Fixes#16045
This PR fixes two Vite issues found with SolidStart:
- SolidStart seems to emit an empty HTML chunk (where the content is
literally just `/`) with _no pathname_. Since we use the path to
generate an `id` for HTML chunks, this would currently cause a crash.
This was reported in #16045
- While testing the fix for the above, we also found that hot reloading
was not working in SolidStart since `4.0.0-alpha.22`. After doing some
bisecting we found that this is happening as SolidStart has the same
module ID in different servers and we were invalidating the root when we
shouldn't. After trying to restructure this code so that it only cleans
up the root when it is _no longer part of any server_, we noticed some
other compatibility issues with Nuxt and SvelteKit. It seems that the
safest bet is to no longer update a root at all during rebuilds in the
SSR step. This makes `invalidateAllRoots` a function that only notifiers
the servers about a change which is conceptually also less confusing.
## Test plan
- Added an integration test for SolidStart dev mode
- Manually tested the dev mode across all Vite based templates in
https://github.com/philipp-spiess/tailwindcss-playgrounds: Astro, Nuxt,
Remix, Solid, SvelteKit, and Vue.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Resolves#15722
This PR adds a list of ignored dependencies (in the current form only
`tailwind-merge`) to the Vite extension which, when included in the
dependency tree, are no longer scanned by the Tailwind CSS compiler for
candidates. This is to work around an issue where some dependencies
contain vast lists of valid Tailwind CSS class names which would
otherwise always be inlined in the build.
## Test plan
This was tested in our Vite playground on both dev and prod builds
across macOS and Windows:
### Windows prod build before

### Windows prod build after
This includes a debug `console.log(…)` to make sure it matches the right
module.

### Windows dev build after
This includes a debug `console.log(…)` to make sure it matches the right
module.

Resolves#15799Resolves#14478
Part-of #15005
Adds a `:host` selector for the `@theme` layer. This is necessary for
the `@theme` layer to work correctly in shadow DOM.
Also updates the snapshots for the tests that were affected by this
change (in a separate commit).
## Test plan
Tested via the Vite playground:
<img width="1121" alt="Screenshot 2025-01-29 at 15 06 49"
src="https://github.com/user-attachments/assets/a7908135-5ff8-472f-a053-d2c6d5c81e1b"
/>
Additionally made sure that `@property` defaults also work across
Firefox, Chrome, and Safari (the `@property` definition from the root is
pulled in) and added a UI spec.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Right now, when Oxide is scanning for files, it considers ignore files
in the "root" directory it is scanning as well as all parent
directories.
We honor .gitignore files even when not in a git repo as an optimization
in case a project has been created, contains a .gitignore, but no repo
has actually been initialized. However, this has an unintended side
effect of including ignore files _ouside of a repo_ when there is one.
This means that if you have a .gitignore file in your home folder it'll
get applied even when you're inside a git repo which is not what you'd
expect.
This PR addresses this by checking to see the folder being scanned is
inside a repo and turns on a flag that ensures .gitignore files from the
repo are the only ones used (global ignore files configured in git still
work tho).
This still needs lots of tests to make sure things work as expected.
Fixes#15876
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR replaces `@variant` with `@custom-variant` for registering
custom variants via your CSS.
In addition, this PR introduces `@variant` that can be used in your CSS
to use a variant while writing custom CSS.
E.g.:
```css
.btn {
background: white;
@variant dark {
background: black;
}
}
```
Compiles to:
```css
.btn {
background: white;
}
@media (prefers-color-scheme: dark) {
.btn {
background: black;
}
}
```
For backwards compatibility, the `@variant` rules that don't have a body
and are
defined inline:
```css
@variant hocus (&:hover, &:focus);
```
And `@variant` rules that are defined with a body and a `@slot`:
```css
@variant hocus {
&:hover, &:focus {
@slot;
}
}
```
Will automatically be upgraded to `@custom-variant` internally, so no
breaking changes are introduced with this PR.
---
TODO:
- [x] ~~Decide whether we want to allow multiple variants and if so,
what syntax should be used. If not, nesting `@variant <variant> {}` will
be the way to go.~~ Only a single `@variant <variant>` can be used, if
you want to use multiple, nesting should be used:
```css
.foo {
@variant hover {
@variant focus {
color: red;
}
}
}
```
This PR improves the codemod tool to simplify 2 things:
1. Whenever you have a `theme(…)` call, we try to change it to a
`var(…)`, but if that doesn't work for some reason, we will make sure to
at least convert it to the more modern `--theme(…)`.
2. When converting `theme(spacing.2)`, we used to convert it to
`calc(var(--spacing)*2)`, but now we will convert it to `--spacing(2)`
instead.
This PR prevents the migration of utilities detected in function names,
e.g.: the use of `shadow` inside `filter: 'drop-shadow(…)'`.
## Test plan
Have content like this in a project you're migrating using the upgrade
tool:
```js
{
filter: 'drop-shadow(0 0 0.5rem #000)'
}
```
This was verified by adding unit tests to the specific codemods and
adding an integration test.
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)
Tried a lot of different things to make sure that these tests are fast
and stable.
---
The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.
For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.
These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.
These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.
---
Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.
To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.
We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.
<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>
Comparing this branch against the `next` branch, this is what CI looks
like right now:
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |
There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.
Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |
They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>
The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.
---
### Nest steps
I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.
1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
Closes#15219
This PR adds a new feature, `@import "…" reference` that can be used to
load Tailwind CSS configuration files without adding any style rules to
the CSS.
The idea is that you can use this in combination with your Tailwind CSS
root file when you need to have access to your full CSS config outside
of the main stylesheet. A common example is for Vue, Svelte, or CSS
modules:
```css
@import "./tailwind.css" reference;
.link {
@apply underline;
}
```
Importing a file as a reference will convert all `@theme` block to be
`reference`, so no CSS variables will be emitted. Furthermore it will
strip out all custom styles from the stylesheet. Furthermore plugins
registered via `@plugin` or `@config` inside reference-mode files will
not add any content to the CSS file via `addBase()`.
## Test Plan
Added unit test for when we handle the import resolution and when
`postcss-import` does it outside of Tailwind CSS. I also changed the
Svelte and Vue integration tests to use this new syntax to ensure it
works end to end.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Closes#15250
This PR simplifies our Vite integration even more. It turns out that in
some projects (see #15250 for the exact repro), the way we invoke
`svelte-preprocess` was actually causing issues in Vite since with Vite,
it's expected to use the `sveltePreprocess` version exported by
`sveltejs/vite-plugin-svelte`.
While trying to change this we noticed that there are different versions
of `sveltejs/vite-plugin-svelte` for Vite 5 and Vite 6 which caused us
to investigate even more and we noticed that we do not even need to
recursively call into the `sveltePreprocess()` as every plugin is run
after each other anyways. This allows us to drop the dependency on
`svelte-preprocess` and simplify the code a bit more, registering only a
`(string) => string` style transformer.
## Test Plan
This was tsted on the repro repo from #15250 as well as the SvelteKit
setup from [my
playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
Furthermore we tested various combinations of `svelte`,
`@sveltejs/vite-plugin-svelte` and `vite` in our integration test to
ensure everything works as expected.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Closes#15159
This PR extends the `@tailwindcss/node` packages to be able to overwrite
the CSS and JS resolvers. This is necessary as some bundlers, in
particular Vite, have a custom module resolution system that can be
individually configured. E.g. in Vite it is possible to add custom
[resolver
configs](https://vite.dev/config/shared-options.html#resolve-conditions)
that is expected to be taken into account.
With the new `customCssResolver` and `customJsResolver` option, we're
able to use the Vite resolvers which take these configs into account.
## Test Plan
Tested in the playground by configuring [resolver
conditions](https://vite.dev/config/shared-options.html#resolve-conditions)
(with Vite 5.4 and Vite 6 beta). An integration test was added for both
the JS and CSS resolvers to ensure it keeps working as expected.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR updates all our Vite dependencies to the newly released v6.
Nothing changed in our released Vite extension so this does not need a
user-facing changelog.
## Test Plan
The Vite playground still works. Furthermore we have a pretty extensive
Vite integration test suite that now runs on v6.
Resolves#15193
This PR fixes an issue where `group` and `peer` would not have their
prefixes migrated as part of the upgrade script. We do this by
registering `group` and `peer` as utilities during the codemods. This
way, `parseCandidate` will find these classes to be valid Tailwind
candidates and the prefix can be migrated just like any other utility.
## Test Plan
Tried it with the v3 upgrade playground in the repo and it worked fine:
<img width="1257" alt="Screenshot 2024-11-27 at 12 17 25"
src="https://github.com/user-attachments/assets/1ee101e1-1d6a-4ce0-b0d4-8d51e5f6b0d2">
I've also added tests to our prefix upgrade integration test and the
prefix migration unit tests.
Closes#15071
This PR reverts the changes in #15036 which add consistent base styles
for buttons and form controls to Preflight.
While this felt like a good idea (for the reasons explained in that PR),
practically this is just too disruptive of a change for people upgrading
from v3 to v4.
While updating some of our projects to v4 we found ourselves adding
classes to undo styles more often than we expected, and it also felt
inconsistent to have to use a different set of classes to style a link
or a button when we wanted them to look the same.
We also decided it feels a little strange that you could change the
border color of an element without ever specifying that it should have a
border, for example this just feels a little wrong:
```html
<button class="border-blue-500">
```
We also needed to set a default `color-scheme` value for any of this
stuff to work which breaks the ability to use the `color-scheme` meta
tag.
Since this change was a fairly major breaking change and we aren't
feeling much benefit from it, it doesn't feel worth making this change
for v4.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
This PR improves compatibility for named `opacity` theme values. One of
the changes in v4 is that we use the CSS `color-mix()` function to apply
opacity to colors. This, however, is limited to percentage values only:
```css
color: color-mix(in oklch, var(--color-red-500) 50%, transparent);
/* ^^^ */
/* This needs to be a percentage value */
```
In v3, however, it was common to specify custom opacity values as
decimal numbers. That's also what we did in our default config:
6069a81187/stubs/config.full.js (L703-L725)
This PR improves interop with these values by:
1. Converting decimal numbers in the range of `[0, 1]` to their
percentage value equivalent when using the interop layer.
2. Adjusts the codemod that migrates `opacity` theme keys to Tailwind v4
theme variables to include the same conversion.
3. Furthermore, due to the added support of named opacity modifers, we
can also drop theme keys that would now be the default. For example the
following config would not be necessary in v4 as the opacity modifier
would accept the value `50` by default:
```js
module.exports = {
theme: {
opacity: {
50: 0.5
}
}
}
```
## Test Plan
Added a new integration test for the codemod and a unit test for the
interop layer. I also re-ran the codemod on the Commit template and it's
now working as expected (left is v3, right is v4):
<img width="2560" alt="Screenshot 2024-11-21 at 17 25 32"
src="https://github.com/user-attachments/assets/f0c87243-ca80-4c39-ae5e-c1ab48fbe614">
While testing the latest alpha release across Tailwind v3 projects, we
noticed one regression in relation to the default color of `<button>`
elements. In v3, the reset would change the default to `inherit` but in
v4 we would _not include it in the reset snippet inserted by the upgrade
too_.
This PR changes the upgrade snippet to include it:
```diff
/*
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;
+ color: inherit;
background-color: transparent;
}
}
```
This PR also ensures that there's a newline between the two code
snippets.
## Test Plan
### Before

### After
<img width="1354" alt="Screenshot 2024-11-21 at 15 42 58"
src="https://github.com/user-attachments/assets/9a4503fe-683f-4d08-abf2-7dd111ed5428">
This PR makes the candidate parser more strict by not allowing empty
arbitrary values.
Examples that are not allowed anymore:
- `bg-[]` — arbitrary value
- `bg-()` — arbitrary value, var shorthand
- `bg-[length:]` — arbitrary value, with typehint
- `bg-(length:)` — arbitrary value, with typehint, var shorthand
- `bg-red-500/[]` — arbitrary modifier
- `bg-red-500/()` — arbitrary modifier, var shorthand
- `data-[]:flex` — arbitrary value for variant
- `data-():flex` — arbitrary value for variant, var shorthand
- `group-visible/[]:flex` — arbitrary modifier for variant
- `group-visible/():flex` — arbitrary modifier for variant, var
shorthand
If you are trying to trick the parser by injecting some spaces like
this:
- `bg-[_]`
Then that is also not allowed.
This PR introduces consistent base styles for buttons and form controls
in Tailwind CSS v4.
## Motivation
In v3, form elements lack default styles, which can be
confusing—especially when certain elements, like a text input without a
placeholder or value, are rendered completely invisible on the page.
The goal of this change is to provide reasonable default styles for
buttons, inputs, selects, and textareas that are (mostly) consistent
across all browsers while remaining easy to customize with your own
styles.
This improvement should make Tailwind more accessible for developers new
to the framework and more convenient in scenarios where you need to
quickly create demos (e.g., using Tailwind Play).
## Light and dark mode support
These styles support both light and dark mode, achieved using the
`light-dark()` CSS function. While browser support for this function is
still somewhat limited, Lightning CSS transpiles it to a CSS
variable-based approach that works in older browsers.
For this approach to function correctly, a default `color-scheme` must
be set in your CSS (as explained in [the Lightning CSS
documentation](https://lightningcss.dev/transpilation.html#light-dark()-color-function)).
This PR addresses this requirement by setting the `color-scheme` to
`light` on the `html` element in Preflight.
<img width="1712" alt="image"
src="https://github.com/user-attachments/assets/dba56368-1427-47b3-9419-7c2f6313a944">
<img width="1709" alt="image"
src="https://github.com/user-attachments/assets/3d84fcd2-9606-4626-8e03-164a1dce9018">
## Breaking changes
While we don’t expect these changes to significantly impact v3 users
upgrading to v4, there may be minor differences for those relying on the
simpler v3 styles.
For example, Preflight now applies a `border-radius` to buttons and form
controls. If you weren’t explicitly setting the border radius to `0` in
your project, you’ll need to do so to restore the previous look.
Thankfully, reverting to the v3 styles is straightforward—just add the
following reset to your CSS:
```css
@layer base {
input,
textarea,
select,
button {
border: 0px solid;
border-radius: 0;
padding: 0;
background-color: transparent;
}
}
```
It’s worth noting that this reset doesn't touch the
`::file-selector-button` styles that were added in this PR. This is
because it's not possible to reliably "undo" these styles and restore
the original user-agent styles (which is what was used in v3), as these
are different in each browser. However, these new styles actually match
the defaults in most browsers pretty closely, so hopefully this just
won't be an issue.
## Codemod
This PR includes a codemod that automatically inserts the above
mentioned v3 reset to help avoid breaking changes during the upgrade.
The codemod will insert the following 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;
}
}
```
## Testing
These changes have been tested across a wide range of browsers,
including Chrome, Safari, Firefox, Edge, and Opera on macOS and Windows,
as well as Safari, Chrome, Firefox, and several lesser-known browsers on
iOS and Android.
However, some quirks still exist in certain mobile browsers, such as iOS
Safari, which adds too much bottom padding below date and time inputs:
<img width="1548" alt="Screenshot 2024-11-20 at 3 57 20 PM"
src="https://github.com/user-attachments/assets/507c7724-ac41-4634-a2b3-61ac4917ebce">
The only reliable way to address these issues is by applying
`appearance: none` to these form controls. However, this felt too
opinionated for Preflight, so we’ve opted to leave such adjustments to
user-land implementations.
---------
Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This PR fixes an issue where the Tailwind root file detection was wrong.
Whenever a CSS file contains any of the `@tailwind` directives or an
`@import` to any of the Tailwind files, the file is considered a
Tailwind root file.
If multiple CSS files are part of the same tree, then we make the
nearest common parent the root file.
This root file will be the file where we add `@config` and/or inject
other changes during the migration process.
However, if your folder structure looked like this:
```css
/* index.css */
@import "./base.css";
@import "./typography.css";
@import "tailwindcss/components"; /* This makes index.css a root file */
@import "./utilities.css";
/* base.css */
@tailwind base; /* This makes base.css a root file */
/* utilities.css */
@tailwind utilities; /* This makes utilities.css a root file */
```
Then we computed that `index.css` nad `base.css` were considered root
files even though they belong to the same tree (because `base.css` is
imported by `index.css`).
This PR fixes that behaviour by essentially being less smart, and just
checking again if any sheets are part of the same tree.
# Test plan:
Added an integration test that covers this scenario and fails before the
fix.
Also ran it on our tailwindcss.com codebase.
| Before | After |
| --- | --- |
| <img width="1072" alt="image"
src="https://github.com/user-attachments/assets/8ee99a59-335e-4221-b368-a8cd81e85191">
| <img width="1072" alt="image"
src="https://github.com/user-attachments/assets/fe5acae4-d3fc-43a4-bd31-eee768a3a6a5">
|
(Yes, I know the migration still fails, but that's a different issue.)
This PR uses the `enhanced-resolve` instead of
`createRequire(…).resolve` which improves the usability when running the
upgrade tool locally using Bun.
While testing, we also noticed that it is not possible to use a
`cjs`-only plugin inside of an `esm` project. It was also not possible
to use an `esm`-only plugin inside of a `cjs` project.
# Test plan
We added integration tests in both the CLI (the CLI is an mjs project)
and in the PostCSS (where we can configure a `cjs` and `esm` PostCSS
config) integration tests where we created an `esm` and `cjs` based
project with 4 plugins (`cjs`-only, `esm`-only, and TypeScript based
plugins: `cts`-only and `mts`-only).
Closes#15012
We do not have replacements for these plugins _just yet_. In order to
increase compatibility with setups that rely on some of these legacy
plugins, this PR bundles `@tailwindcss/forms`,
`@tailwindcss/typography`, and `@tailwindcss/aspect-ratio` (after
https://github.com/tailwindlabs/tailwindcss/pull/15029) with the
standalone build now.
In comparison to v3, this omits the `@tailwindcss/container-queries`
plugin since is not a first-party feature of Tailwind CSS v4.
## Test Plan
Added an integration test. I also tested this by running the standalone
binary in a temporary folder with as simple input css:
```css
@import "tailwindcss";
@plugin "@tailwindcss/typography";
```