Closes#17621
Chrome as a pretty ugly rendering glitch when using a `skew-*` utility
in Tailwind 4: https://play.tailwindcss.com/HuiZtbrHOc
The was not an issue in v3 since transforms were set up in a different
way. Without the `var(…)` syntax, the difference boils down to this:
```css
.skew-v3 {
transform: rotate(0) skewX(-20deg);
}
.skew-v4 {
transform: rotateX(0) rotateY(0) rotateZ(0) skewX(-20deg);
}
```
It appears that using any of the single-dimension rotate functions will
cause the Chrome rendering to glitch.
After doing some digging, we found [that initially these `@property`s
were defined as type `<transform-function>` and later changed to
`*`](https://github.com/tailwindlabs/tailwindcss/issues/15144). With a
type of `*`, it makes sense that the initial value of these variables
can default to `initial` without any compromises, allowing us to default
to something like this now:
```css
.skew-new {
transform: skewX(-20deg);
}
```
Tested this change in the latest version of Chrome (135) and it does
make the rendering glitch in the initial issue disappear. By using the
`var(--tw-rotate-x,)` syntax we also ensure this works on older versions
of Safari (tested on Safari 15.5 and 16.4).
Note, however, that there are still glitches happening when you combine
rotate and skew in the latest version of Chrome or when you transition
the `skew(…)` variable. This also happens in plain CSS with no variables
though, so there isn't something we can do about this:
https://play.tailwindcss.com/g3FXPEJHpn
## Test plan
- Tested on latest Chrome, Firefox, and Safari as well as Safari 15.5
and 16.4.
<img width="564" alt="Screenshot 2025-04-09 at 18 01 51"
src="https://github.com/user-attachments/assets/2e0b1c96-7c4d-41a8-b3d0-0f6134a3e635"
/>
Fixes#17643.
This PR completely removes the `color-mix()` function for
`shadow-inherit`. This does mean intensity and alpha channel support has
been removed when using `shadow-inherit`[^1].
With intensity modifiers in #17398, all colors are wrapped in
`color-mix()`. However, it seems `inherit` does not work as a value in
`color-mix()` in Firefox or Chrome (don't have a means to test Safari).
[^1]: While writing this, I noticed other color utilities allow alpha
channel modifier syntax for `inherit` - do we want to look at removing
those too?
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Fixes#17622.
Adds a specific handling case in `themeableValues()` in
`packages/tailwindcss/src/compat/apply-config-to-theme.ts`. It seems
like this has unique handling in v3 for an array value, whereby the
second value is treated as a `line-height`.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Fixes#17614.
Candidate parsing for variants only account for the root `@` if there no
hyphens. It seems like the current logic assumes if it *does* have a
hyphen, then it would be one of `@min` or `@max`. However, with:
```css
@theme {
--container-foo-bar: 1440px;
}
```
Then `@foo-bar` should be valid. However, we only check for `@foo-bar`
and `@foo` as roots, but never `@`. This PR adds a check for `@` at the
very end after iterating through root permutations.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Discovered while triaging #17556
This PR improves the `color-mix(...)` polyfill to ensure it works when a
theme key links to another theme key via a `var(...)` call.
Imagine this setup:
```css
@theme {
--color-red: var(--color-red-500);
--color-red-500: oklch(63.7% 0.237 25.331);
}
@source inline("text-red/50");
````
Since `--color-red` will link to `--color-red-500` _which is also a
known theme variable_, we can inline the value of `--color-red-500` into
the fallback now:
```css
.text-red\\/50 {
color: var(--color-red);
}
@supports (color: color-mix(in lab, red, red)) {
.text-red\\/50 {
color: color-mix(in oklab, var(--color-red) 50%, transparent);
}
}
```
This will allow for slightly less confusing behavior.
The code added also handles recursive definitions where a color is
linking to another color that is again linking to the first one (by
adding a `Set` to keep track of already seen variable names).
## Test plan
- Added unit test
This PR fixes an issue we noticed while investigating #17553, where the
unused CSS variable removal didn't work properly when the theme variable
it tried to remove was modified by a polyfill rule.
The way the bookkeeping for the unused CSS variable worked was that it
tired to find the declaration inside it's parent after the traversal.
However, the `color-mix(…)` polyfill has since then made changes to the
declaration so it can't find it's position correctly anymore and will
thus instead delete the last declaration of the node (this caused
unrelated CSS variables to be eliminated while the ones with
`color-mix(…)` were unexpectedly kept).
To fix this, we decided to apply the polyfills after any eventual
deletions. This also ensures that no `@supports` query for the variables
are created and simplifies the code a bit since all polyfills are now
colocated.
## Test plan
- Added a unit test for the example we discovered in #17553
- Luckily the conditions of this seemed rare enough so that it doesn't
cause any other of our tests to update.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Closes#17508
This PR fixes another issue we found that caused dev builds with Next.js
and Turbopack to resolve the CSS file that was saved one revision before
the latest update.
When debugging this we noticed that the PostCSS entry is called twice
for every one update when changing the input CSS file directly. That was
caused by the input file itself being added as a _dependency_ so you
would first get the callback that a _dependency_ has updated (at which
point we look at the file system and figure out we need a full-rebuild
because the input.css file has changed) and then another callback for
when the _input file_ has updated. The problem with the second callback
was that the file-system was already scanned for updates and since this
includes the `mtimes` for the input file, we seemingly thought that the
input file did not change. However, the issue is that the first callback
actually came with an outdated PostCSS input AST...
We found that this problem arises when you register the input CSS as a
dependency of itself. This is not expected and we actually guard against
this in the PostCSS client. However, we found that the input `from`
argument is _a relative path when using Next.js with Turbopack_ so that
check was not working as expected.
## Test plan
Added the change to the repro from #17508 and it seems to work fine now.
https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1
Also added a unit test to ensure we document that the input file path
can be a relative path.
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR will show a warning if you are using a bare value data type that
is not supported.
Let's say you want to create a new utility that allows `color` to be a
bare value data type like this:
```css
@utility paint-* {
paint: --value([color], color);
}
```
This means that this would enable new syntax that we don't support yet.
E.g.: `paint-#0088cc`.
The only supported data types for bare values are:
- `number` — `2.5`
- `integer` — `2`
- `ratio` — `1/2`
- `percentage` — `50%`
All other data types are not supported in this position. This PR will
now show a warning:
~~~
Unsupported bare value data type: "color".
Only valid data types are: "number", "integer", "ratio", "percentage".
```css
--value([color],color)
^^^^^
```
~~~
Once we have better sourcemap / location tracking support, this warning
will point to the exact spot, but for now, only a re-print of the AST
can be used.
If you _do_ want to use other data types, then you will have to use
arbitrary value syntax with `[…]` instead.
```css
@utility paint-* {
paint: --value([color]);
}
```
This will allow for `paint-[#0088cc]` for example.
Note: this is not a behavioral change, we already didn't support other
data types, but we silently ignored them. This means that we have to do
more parsing at runtime when evaluating the utility.
With this change, a warning is shown when registering the `@utility`,
not when using it.
Closes#17512
One of the changes of the Oxide API in 4.1 is that it now emits the
input CSS file itself as a dependency. This was fine in most of our
testing but it turns out that certain integrations (in this case a Qwik
project) don't like this and will silently crash with no CSS file being
added anymore.
This PR fixes this by making sure we don't add the input file as a
dependency on itself and also adds an integration test to ensure this
won't regress again.
## Test plan
- Tested with the repro provided in #17512
- Added a minimal integration test based on that reproduction that I
also validated will _fail_, if the fix is reverted.
This PR further improves the `color-mix(…)` polyfill to create a
reasonable fallback if dynamic values that can not statically be
resolved are used. This refers to either the use of `currentcolor` or
any variables that are not static theme variables.
Here are two examples that now generate a reasonable fallback instead of
not showing any color at all:
```css
.text-\\(--my-color\\)\\/\\(--my-opacity\\) {
color: var(--my-color);
}
@supports (color: color-mix(in lab, red, red)) {
.text-\\(--my-color\\)\\/\\(--my-opacity\\) {
color: color-mix(in oklab, var(--my-color) var(--my-opacity), transparent);
}
}
```
```css
.text-current\\/50 {
color: currentColor;
}
@supports (color: color-mix(in lab, red, red)) {
.text-current\\/50 {
color: color-mix(in oklab, currentColor 50%, transparent);
}
}
```
## Test plan
- Made sure the test diffs are looking reasonable
- Tested this on a production site with `<p className="text-shadow-lg/50
[--my-color:red] text-shadow-(color:--my-color)">shadow test</p>`
- Browsers that do not support `color-mix(…)` will properly show a red
shadow now albeit with 100% opacity: iOS 15.5 and Chrome 110
- Browsers that I have tested to make sure it still works there with
opacity: Firefox 127, Firefox 128, Latest Chrome, Safari, Firefox
- Browsers that do show a black shadow because of `var(…)var(…)` being
chained with no space by lightningcss: Chrome 111
This PR fixes an issue where if you use Next.js with `--turbopack` a
race condition happens because the `@tailwindcss/postcss` plugin is
called twice in rapid succession.
The first call sees an update and does a partial update with the new
classes. Next some internal `mtimes` are updated. The second call
therefore doesn't see any changes anymore because the `mtimes` are the
same, therefore it's serving its stale data.
Fixes: #17508
## Test plan
- Tested with the repro provided in #17508
- Added a new unit test that calls into the PostCSS plugin directly for
the same change from the same JavaScript run-loop.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
It seems that I broke support for multiple drop-shadow filters when
`@theme inline` was used in v4.1. This PR fixes that by segmenting the
drop shadow value on top-level commas and wrapping each segment with
`drop-shadow(…)` like we did in v4.0.
Fixes#17520
Fixes multi-value inset shadows to apply the `inset` prefix to each
component instead of only the first.
Feel free to make the code nicer 😄
## Test plan
Ensure that a multi-value inset shadow now applies each shadow _inset_:
<img width="505" alt="Screenshot 2025-04-03 at 10 50 29"
src="https://github.com/user-attachments/assets/5d38de45-a16f-48fd-8e3c-b50d2740eb49"
/>
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
This PR changes how polyfills for `@property` are inserted. The main
motivation is to remove the need to rely on the correct placement of
`@layer base;`—Something that's not really required right not in
Tailwind CSS v4 and we'd like to keep it this way.
The idea is that the polyfills are inserted for you automatically. To
ensure they always take precedence, we insert an empty `@layer
properties;` at the top of the CSS file so that later, when we emit all
`@property` rules and their fallback, we can use this new named layer to
ensure the rules have a higher order.
Unfortunately, just putting `@layer properties;` at the beginning of a
file would not work as `lightningcss` incorrectly hoists all content
into the first occurrence of a layer name meaning these rules might be
inserted _before_ eventual external imports:

To work around this, we have to insert that layer name after any
eventual remaining external `@imports` for now.
## Test plan
- Updated snapshot tests
- Deployed a new version of the website with the patch applied to ensure
it works across browsers:
https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/.
Tested on: Safari on iOS 15.5, Safari on iOS 16.0, Firefox 127, Firefox
128, Chrome 110, Chrome latest, Safari latest, Firefox latest
Closes#17501
Seems like an oversight. The CLI does have a dependency on
`@tailwindcss/node` so it should use it from the public import like the
other stuff.
This PR fixes an issue where polyfills were injected at the top, but
they should be after `@import` and body-less `@layer` rules.
This is necessary in case you are using Google fonts like this for
example:
```css
@import url('https://fonts.google.com');
@import "tailwindcss";
```
While the `@import url(…);` sits above `@import "tailwindcss";` in the
final generated CSS we injected the polyfills at the very beginning.
This PR will inject the polyfills after the first AST Node that is not:
1. A comment
2. An external import — `@import url(…)`
3. A body-less layer — `@layer foo, bar, baz;`
The snapshots look a little confusing, but that's because Lightning CSS
is optimizing the output and moving things around a bit:
<img width="1482" alt="image"
src="https://github.com/user-attachments/assets/a0552c8b-93df-4e1d-ad90-8b8abf9492b1"
/>
[Lightning CSS
Playground](https://lightningcss.dev/playground/index.html#%7B%22minify%22%3Afalse%2C%22customMedia%22%3Atrue%2C%22cssModules%22%3Afalse%2C%22analyzeDependencies%22%3Afalse%2C%22targets%22%3A%7B%22chrome%22%3A6225920%7D%2C%22include%22%3A0%2C%22exclude%22%3A0%2C%22source%22%3A%22%40layer%20theme%2C%20base%2C%20components%2C%20utilities%3B%5Cn%5Cn%40supports%20(((-webkit-hyphens%3A%20none))%20and%20(not%20(margin-trim%3A%20inline)))%20or%20((-moz-orient%3A%20inline)%20and%20(not%20(color%3A%20rgb(from%20red%20r%20g%20b))))%20%7B%5Cn%20%20%40layer%20base%20%7B%5Cn%20%20%20%20*%2C%20%3Abefore%2C%20%3Aafter%2C%20%3A%3Abackdrop%20%7B%5Cn%20%20%20%20%20%20--tw-font-weight%3A%20initial%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40layer%20theme%20%7B%5Cn%20%20%3Aroot%2C%20%3Ahost%20%7B%5Cn%20%20%20%20--font-sans%3A%20ui-sans-serif%2C%20system-ui%2C%20sans-serif%2C%20%5C%22Apple%20Color%20Emoji%5C%22%2C%20%5C%22Segoe%20UI%20Emoji%5C%22%2C%20%5C%22Segoe%20UI%20Symbol%5C%22%2C%20%5C%22Noto%20Color%20Emoji%5C%22%3B%5Cn%20%20%20%20--font-mono%3A%20ui-monospace%2C%20SFMono-Regular%2C%20Menlo%2C%20Monaco%2C%20Consolas%2C%20%5C%22Liberation%20Mono%5C%22%2C%20%5C%22Courier%20New%5C%22%2C%20monospace%3B%5Cn%20%20%20%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40layer%20base%20%7B%5Cn%20%20*%2C%20%3Aafter%2C%20%3Abefore%2C%20%3A%3Abackdrop%20%7B%5Cn%20%20%20%20box-sizing%3A%20border-box%3B%5Cn%20%20%20%20border%3A%200%20solid%3B%5Cn%20%20%20%20margin%3A%200%3B%5Cn%20%20%20%20padding%3A%200%3B%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40layer%20utilities%20%7B%5Cn%20%20.text-2xl%20%7B%5Cn%20%20%20%20font-size%3A%20var(--text-2xl)%3B%5Cn%20%20%20%20line-height%3A%20var(--tw-leading%2C%20var(--text-2xl--line-height))%3B%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40property%20--tw-font-weight%20%7B%5Cn%20%20syntax%3A%20%5C%22*%5C%22%3B%5Cn%20%20inherits%3A%20false%5Cn%7D%22%2C%22visitorEnabled%22%3Afalse%2C%22visitor%22%3A%22%7B%5Cn%20%20Color(color)%20%7B%5Cn%20%20%20%20if%20(color.type%20%3D%3D%3D%20'rgb')%20%7B%5Cn%20%20%20%20%20%20color.g%20%3D%200%3B%5Cn%20%20%20%20%20%20return%20color%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%22%2C%22unusedSymbols%22%3A%5B%5D%2C%22version%22%3A%22local%22%7D)
Fixes: #17494
The padding code we had was incorrect as it would always pad on the
largest string representation. So for an input like this:
```
@source inline("z-{10..100..10}");
```
It would create the following candidates:
- `z-010`
- `z-020`
- `z-030`
- `z-040`
- `z-050`
- `z-060`
- `z-070`
- `z-060`
- `z-070`
- `z-100`
Instead of fixing the padding logic we realized that Tailwind utilities
don't need padding at all so this PR removes this feature
## Test plan
- Added the following to the Vite playground: `@source
inline("z-{10..100..10}");`
- Ensure it works:

This PR is an internal refactor of the codemods package structure that
will make a few follow-up PRs easier.
Essentially what happens is:
1. Moved `./src/template/` into `src/codemods/template/`
2. Moved `./src/codemods` into `./src/codemods/css` (because the CSS
related codemods already)
3. Moved the migration files for the JS config, PostCSS config and
Prettier config into `./src/codemods/config/`.
4. Made filenames with actual migrations consistent by prefixing them
with `migrate-`.
5. Made sure that all the migration functions also use `migrate…`
When looking at this PR, go commit by commit, it will be easier. In a
lot of cases, it's just moving files around but those commits also come
with changes to the code just to update the imports.
[ci-all]
This PR adds support for two things:
- New `drop-shadow-{color}` utilities which can be used to control the
filder drop shadow color
- New `drop-shadow-*/{alpha}` utilities which can be used to control the
intensity of the drop shadow
Note that drop-shadow-* utilities without a modifier use variables from
your theme.
If you opt into using drop shadow colors or drop-shadow "intensity"
utilities we will inline these values into your CSS when using a drop
shadow color or drop shadow intensity utility and as such can't be
changed by changing the theme variables on a per-element/tree basis.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR also adds a new `self-baseline-last` utility similar to
`items-baseline-last` added previously. We've also discussed adding a
`content-baseline-last` utility but since the browser support for this
[is very
limited](https://caniuse.com/mdn-css_properties_align-content_flex_context_last_baseline)
and we weren't able to get this to do anything even in the supported
browsers, we decided against it.
## Test plan
Updated the utility tests.
This PR improves the compatibility with Tailwind CSS v4 with unsupported
browsers with the goal to greatly improve compatibility with Safari 15.
To make this work, this PR makes the following changes to all code
- Change `oklab(…)` default theme values to use a percentage in the
first place (so instead of `--color-red-500: oklch(0.637 0.237 25.331);`
we now define it as `--color-red-500: oklch(63.7% 0.237 25.331);` since
this syntax has much broader support on Safari).
- Polyfill `@property` with a `@supports` query targeting older versions
of Safari and Firefox *
- Create fallbacks for the `color-mix(…)` function that use _inlined
color values from your theme_ so that they can be computed a compile
time by `lightningcss`. These fallbacks will convert to srgb to increase
compatibility.
- Create fallbacks for the _relative color_ feature used in the new
shadow utilities and using `color-mix(…)` in case _relative color_ is
applied on `currentcolor` (due to limited browser support)
- Create fallbacks for gradient interpolation methods (e.g. to support
`bg-linear-to-r/oklab`)
- Polyfill `@media` queries range syntax.
## A simplified example
Given this example CSS input:
```css
@import 'tailwindcss';
@source inline('from-cyan-500/50 bg-linear-45');
```
Here's the updated output CSS including the newly added polyfills and
updated `oklab` values:
```css
.bg-linear-45 {
--tw-gradient-position: 45deg;
background-image: linear-gradient(var(--tw-gradient-stops));
}
@supports (background-image: linear-gradient(in lab, red, red)) {
.bg-linear-45 {
--tw-gradient-position: 45deg in oklab;
}
}
.from-cyan-500\\/50 {
--tw-gradient-from: oklab(71.5% -.11682 -.08247 / .5);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
@supports (color: color-mix(in lab, red, red)) {
.from-cyan-500\\/50 {
--tw-gradient-from: color-mix(in oklab, var(--color-cyan-500) 50%, transparent);
}
}
:root, :host {
--color-cyan-500: oklch(71.5% .143 215.221);
}
@supports (((-webkit-hyphens: none)) and (not (margin-trim: 1lh))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
@layer base {
*, :before, :after, ::backdrop {
--tw-gradient-position: initial;
--tw-gradient-from: #0000;
--tw-gradient-via: #0000;
--tw-gradient-to: #0000;
--tw-gradient-stops: initial;
--tw-gradient-via-stops: initial;
--tw-gradient-from-position: 0%;
--tw-gradient-via-position: 50%;
--tw-gradient-to-position: 100%;
}
}
}
@property --tw-gradient-position {
syntax: "*";
inherits: false
}
@property --tw-gradient-from {
syntax: "<color>";
inherits: false;
initial-value: #0000;
}
@property --tw-gradient-via {
syntax: "<color>";
inherits: false;
initial-value: #0000;
}
@property --tw-gradient-to {
syntax: "<color>";
inherits: false;
initial-value: #0000;
}
@property --tw-gradient-stops {
syntax: "*";
inherits: false
}
@property --tw-gradient-via-stops {
syntax: "*";
inherits: false
}
@property --tw-gradient-from-position {
syntax: "<length-percentage>";
inherits: false;
initial-value: 0%;
}
@property --tw-gradient-via-position {
syntax: "<length-percentage>";
inherits: false;
initial-value: 50%;
}
@property --tw-gradient-to-position {
syntax: "<length-percentage>";
inherits: false;
initial-value: 100%;
}
```
## \* A note on `@property` polyfills and CSS modules
On Next.js, CSS module files are required to be _pure_, meaning that all
selectors must either be scoped to a class or an ID. Fortunatnyl for us,
this does not apply to `@property` rules which we've been using before
to initialize CSS variables.
However, since we're now bringing back the `@property` polyfills, that
would cause unexpected rules to be exported from the CSS file as this:
```css
@reference "tailwindcss";
.skew {
@apply skew-7;
}
```
Would turn to the following file:
```css
.skew {
/* … */
}
@supports (/*…*/) {
@layer base {
*, :before, :after, ::backdrop {
--tw-gradient-position: initial;
}
}
}
@property /* … */
```
Notice that this adds a `*` selector which is not considered pure.
Unfortunately there is no way for us to silence this warning or work
around it, as the dependency causing this errors
([`postcss-modules-local-by-default`](https://github.com/css-modules/postcss-modules-local-by-default))
is bundled into Next.js. To work around crashes, these polyfills will
not apply to CSS modules processed by the PostCSS extension for now.
## Testing on tailwindcss.com
To see the changes in effect, take a look at this screencast that
compares tailwindcss.com on iOS 15.5 with a version that has the patches
of this PR applied:
https://github.com/user-attachments/assets/1279d6f5-3c63-4f30-839c-198a789f4292
## Test plan
- Tested on tailwindcss.com via a preview build:
https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/
- Updated tests
- Ensure we also test on Chrome 111, Safari 16.4, Firefox 128 to
make sure we have no regressions. Also tested on Safari 16.4, 15.5, 18.0
This PR is a follow-up PR for:
https://github.com/tailwindlabs/tailwindcss/pull/17433
In the other PR we allow scanning CSS files for extracting usages of CSS
variables. This is important for `.module.css` files that reference
these variables but aren't in the same big AST of the main CSS file.
This PR also makes sure to watch for changes in those registered CSS
files and re-extract the variables when they change.
This PR took a bit longer than expected because I was trying to make
sure that writing to `./dist/out.css` works without infinite-looping
(e.g.: we had issues with this in Tailwind CSS v3 with webpack).
But I couldn't reproduce the issue at all. I did had some code that
tried to detect if the CSS file contained license headers and skip in
(because then it's very likely an output CSS file) but even without it
the tests were fine.
I setup integration tests with `@tailwindcss/cli` itself, and with tools
that use webpack. Added a test for Next.js, and a dedicated webpack test
as well.
Even without tests, locally, I couldn't reproduce an infinite loop due
to changes in an output CSS file...
Eventually dropped the code that tries to detect output CSS files.
One thing to keep in mind is that if you change any of your "main" CSS
files, then we will trigger a full rebuild anyway, so this change is
only required for unrelated CSS files (like CSS module files) that use
CSS variables.
## Test plan
1. Added integration tests for the CLI and Next.js
2. Added new dedicated test for webpack
This PR reworks a unit test that created a file in the project root and
then proceeded by scanning everything in the git root for candidates.
The issue specifically is that with the `.debug/` folder, our project
root can grow quite a bit which makes this test slower the more you work
on other tests...
To fix this we now simply create a tmp folder with only that one test
file. 🚀
Closes#17346
This PR fixes an issue that caused the `--theme(…)` function to behave
differently after a legacy JS plugin or config was configured. The issue
was that the compatibility layer would patch the theme value resolver to
always inline the value. This, however, is only expected to happen if
the path does not look like a CSS variable in which case this legacy
code path should not be run.
To fix this, I'm now keeping a reference to the regular theme resolution
function and call into it if the path starts with `--`.
## Test plan
- Tested with the repro in #17346 by adding pnpm overrides and confirmed
that this fixes the issue
- Added a unit test to the `--theme(…)` resolution tests
These match the new `mask-*` and updated `bg-*` utilities.
This is the same as #17378 but for `object-position`.
| Deprecated utility | New utility |
| --------------------- | --------------------- |
| `object-left-top` | `object-top-left` |
| `object-right-top` | `object-top-right` |
| `object-left-bottom` | `object-bottom-left` |
| `object-right-bottom` | `object-bottom-right` |
This PR adds support for utilities like:
- `text-shadow-lg/25`
It uses relative color syntax to replace the alpha value of the shadow
from your theme.
When combined with a colors:
- `text-shadow-lg/25 text-shadow-red-500`
- `text-shadow-lg/25 text-shadow-red-500/75`
The alpha values are **multiplied** resulting in a shadow with the color
specified in `--color-red-500` and alpha values of 25% and 18.75%
respectively.
This PR adds new `text-shadow-*` utilities and default values courtesy
of @danhollick's.
Usage is similar to the normal `shadow-*` utilities, for example:
```html
<h1 class="text-center text-7xl tracking-tight text-white text-shadow-xl">
Some fancy <br />
headline
</h1>
```
Since this PR also adds first-class support for the `--text-shadow`
theme namespace, it also means it resolves#17047.
## Test plan
- Copied @danhollick's [demo
playground](https://play.tailwindcss.com/lbA3Y6VY8u) to the `vite` setup
and ran a production build to ensure it looks correct:
https://tailwind-text-shadows-preview-2kdnjb32b.vercel.app/
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR adds `bg-{position,size}-*` utilities that support arbitrary
values. This exist as the new preferred version of something like this:
```
bg-[120px_120px]
```
Is it size or position (hint: it's the 2nd one).
To override it you'd have to provide a data type:
```
bg-[size:120px_120px]
```
Now you can be more explicit and have better intellisense by writing
these:
```
bg-position-[120px_120px]
bg-size-[120px_120px]
```
Fixes#17357
This affects the CDN and Play
Now candidates like these no longer parse and emit CSS:
- `[--foo:1rem;--bar:2rem]`
- `[&{color:red}]:flex`
- `data-[a]{color:red}foo[a]:flex`
This replaces the `scripting`, `scripting-none`, and `scripting-initial`
variants with one `noscript` variant that matches the name of the HTML
`<noscript>` element. The previous `scripting` can then be represented
as `not-noscript` e.g. `not-noscript:flex`.
We’re using this naming for `mask-*` and `mask-radial-at-*` position
utilities and we want to be consistent. The old ones for
`background-position` will stay but are deprecated
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR adds a new source detection feature: `@source not "…"`. It can
be used to exclude files specifically from your source configuration
without having to think about creating a rule that matches all but the
requested file:
```css
@import "tailwindcss";
@source not "../src/my-tailwind-js-plugin.js";
```
While working on this feature, we noticed that there are multiple places
with different heuristics we used to scan the file system. These are:
- Auto source detection (so the default configuration or an `@source
"./my-dir"`)
- Custom sources ( e.g. `@source "./**/*.bin"` — these contain file
extensions)
- The code to detect updates on the file system
Because of the different heuristics, we were able to construct failing
cases (e.g. when you create a new file into `my-dir` that would be
thrown out by auto-source detection, it'd would actually be scanned). We
were also leaving a lot of performance on the table as the file system
is traversed multiple times for certain problems.
To resolve these issues, we're now unifying all of these systems into
one `ignore` crate walker setup. We also implemented features like
auto-source-detection and the `not` flag as additional _gitignore_ rules
only, avoid the need for a lot of custom code needed to make decisions.
High level, this is what happens after the now:
- We collect all non-negative `@source` rules into a list of _roots_
(that is the source directory for this rule) and optional _globs_ (that
is the actual rules for files in this file). For custom sources (i.e
with a custom `glob`), we add an allowlist rule to the gitignore setup,
so that we can be sure these files are always included.
- For every negative `@source` rule, we create respective ignore rules.
- Furthermore we have a custom filter that ensures files are only read
if they have been changed since the last time they were read.
So, consider the following setup:
```css
/* packages/web/src/index.css */
@import "tailwindcss";
@source "../../lib/ui/**/*.bin";
@source not "../../lib/ui/expensive.bin";
```
This creates a git ignore file that (simplified) looks like this:
```gitignore
# Auto-source rules
*.{exe,node,bin,…}
*.{css,scss,sass,…}
{node_modules,git}/
# Custom sources can overwrite auto-source rules
!lib/ui/**/*.bin
# Negative rules
lib/ui/expensive.bin
```
We then use this information _on top of your existing `.gitignore`
setup_ to resolve files (i.e so if your `.gitignore` contains rules e.g.
`dist/` this line is going to be added _before_ any of the rules lined
out in the example above. This allows negative rules to allow-list your
`.gitignore` rules.
To implement this, we're rely on the `ignore` crate but we had to make
various changes, very specific, to it so we decided to fork the crate.
All changes are prefixed with a `// CHANGED:` block but here are the
most-important ones:
- We added a way to add custom ignore rules that _extend_ (rather than
overwrite) your existing `.gitignore` rules
- We updated the order in which files are resolved and made it so that
more-specific files can allow-list more generic ignore rules.
- We resolved various issues related to adding more than one base path
to the traversal and ensured it works consistent for Linux, macOS, and
Windows.
## Behavioral changes
1. Any custom glob defined via `@source` now wins over your `.gitignore`
file and the auto-content rules.
- Resolves#16920
3. The `node_modules` and `.git` folders as well as the `.gitignore`
file are now ignored by default (but can be overridden by an explicit
`@source` rule).
- Resolves#17318
- Resolves#15882
4. Source paths into ignored-by-default folders (like `node_modules`)
now also win over your `.gitignore` configuration and auto-content
rules.
- Resolves#16669
5. Introduced `@source not "…"` to negate any previous rules.
- Resolves#17058
6. Negative `content` rules in your legacy JavaScript configuration
(e.g. `content: ['!./src']`) now work with v4.
- Resolves#15943
7. The order of `@source` definitions matter now, because you can
technically include or negate previous rules. This is similar to your
`.gitingore` file.
9. Rebuilds in watch mode now take the `@source` configuration into
account
- Resolves#15684
## Combining with other features
Note that the `not` flag is also already compatible with [`@source
inline(…)`](https://github.com/tailwindlabs/tailwindcss/pull/17147)
added in an earlier commit:
```css
@import "tailwindcss";
@source not inline("container");
```
## Test plan
- We added a bunch of oxide unit tests to ensure that the right files
are scanned
- We updated the existing integration tests with new `@source not "…"`
specific examples and updated the existing tests to match the subtle
behavior changes
- We also added a new special tag `[ci-all]` that, when added to the
description of a PR, causes the PR to run unit and integration tests on
all operating systems.
[ci-all]
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Prepare the 4.0.16 release.
~~Also added a commit to mark the `--value('…')` and `--modifier('…')`
with literals strings as an experimental feature (aka not shipped in
this PR). But we can revert that commit if we still want to ship it in
4.0.16 instead of 4.1.~~
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
See #17360
This PR updates the `marker` variant to also target the marker present
in `<summary>` elements in WebKit browsers. Chromium uses `::marker` and
is therefore already covered.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Fixes#17332
This PR ensures that keyframes are emitted even when they are referenced
following a comma, e.g.:
```css
@theme {
--animate-test: 500ms both fade-in, 1000ms linear 500ms spin infinite;
/* ^ */
@keyframes fade-in {
from {
opacity: 0%;
}
to {
opacity: 100%;
}
}
}
```
## Test plan
Added a unit test to capture the issue from #17332
Throw an error if the input and output file for the CLI are identical.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Closes#17339
This PR fixes an issue that caused changes to `@import`-ed CSS files to
no longer rebuild the stylesheet after a change was made to a template
file.
The change in the template file causes a fast-path in the Vite plugin
now after changes in 4.0.8: _partial rebuilds_. For that branch we do
not need to re-evaluate your input CSS since we know only the candidate
list changed. However, we still need to emit all build dependencies as
via `addWatchFile(…)`, otherwise Vite will not correctly register
updates for these dependencies anymore.
## Test plan
- Updated the kitchen-sink Vite update tests to ensure that an
`@import`-ed CSS file can be updated even after a partial rebuild.
- Ensure this works in our Vite playground