5451 Commits

Author SHA1 Message Date
Adam Wathan
d6a67beb76
Add default option to @theme to support overriding default theme values from plugins/JS config files (#14327)
This PR adds a new `default` option to `@theme` to make it possible for
plugins/JS config files to override default theme values, and also
ensures that the final set of CSS variables we output takes into account
any theme values added by plugins/JS config files.

---

Previously, if you were using the default theme but also had a JS config
file that overrode any of those defaults like this:

```js
// ./tailwind.config.js
export default {
  theme: {
    extend: {
      colors: {
        red: {
          '500': 'tomato',
        },
      }
    }
  }
}
```

…then utilities like `text-red-500` would correctly use `color: tomato`,
but the `--color-red-500` CSS variable would still be set to the default
value:

```css
:root {
  --color-red-500: #ef4444;
}
```

This feels like a straight-up bug — if `#ef4444` is not part of your
design system because you've overridden it, it shouldn't show up in your
set of CSS variables anywhere.

So this PR fixes this issue by making sure we don't print the final set
of CSS variables until all of your plugins and config files have had a
chance to update the theme.

---

The second issue is that we realized people have different expectations
about how plugin/config theme values should interact with Tailwind's
_default_ theme vs. explicitly user-configured theme values.

Take this setup for instance:

```css
@import "tailwindcss";
@config "./tailwind.config.js";
```

If `tailwind.config.js` overrides `red-500` to be `tomato`, you'd expect
`text-red-500` to actually be `tomato`, not the default `#ef4444` color.

But in this setup:

```css
@import "tailwindcss";
@config "./tailwind.config.js";
@theme {
  --color-red-500: #f00;
}
```

…you'd expect `text-red-500` to be `#f00`. This is despite the fact that
currently in Tailwind there is no difference here — they are both just
`@theme` blocks, one just happens to be coming from an imported file
(`@import "tailwindcss"`).

So to resolve this ambiguity, I've added a `default` option to `@theme`
for explicitly registering theme values as "defaults" that are safe to
override with plugin/JS config theme values:

```css
@import "tailwindcss";
@config "./tailwind.config.js";
@theme default {
  --color-red-500: #f00;
}
```

Now `text-red-500` would be `tomato` here as per the config file.

This API is not something users are generally going to interact with —
they will almost never want to use `default` explicitly. But in this PR
I've updated the default theme we ship with to include `default` so that
it interacts in a more intuitive way with plugins and JS config files.

---

Finally, this PR makes sure all theme values registered by
plugins/configs are registered with `isReference: true` to make sure
they do not end up in the final CSS at all.

This is important to make sure that the super weird shit we used to do
in configs in v3 doesn't get translated into nonsense variables that
pollute your output (hello typography plugin I'm looking at you).

If we don't do this, you'll end up with CSS variables like this:

```css
:root {
  --typography-sm-css-blockquote-padding-inline-start: 1.25em;
}
```

Preventing theme values registered in plugins/configs from outputting
CSS values also serves the secondary purpose of nudging users to migrate
to the CSS config if they do want CSS variables for their theme values.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 11:49:50 -04:00
Adam Wathan
6d0371afb1
Evaluate theme functions in plugins (#14326)
This PR fixes a bug where CSS `theme()` functions were not evaluated
when present in rules added by plugins, using either `@plugin` or
registering a plugin in a JS config file.

For example, prior to this PR the `theme()` functions in this plugin
would make it into the final CSS without being evaluated:

```js
// ./my-plugin.js
export default plugin(({ addBase }) => {
  addBase({
    '.my-rule': {
      background: 'theme(colors.primary)',
      color: 'theme(colors.secondary)',
    },
  })
})
```

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 11:49:01 -04:00
Philipp Spiess
44dd6da4fe
Integration tests: Fix Windows flake for Vite watch mode (#14332)
I noticed the Windows integration tests for Vite's `watch` mode have
been flaky. Moving the file write into the retrying assertion callback
seems to fix it and allows us to get rid of the arbitrary timeout (I
don't remember that I ever added this in the first place 😅 ).
2024-09-04 17:09:33 +02:00
Philipp Spiess
719a5351c1
Fix Nuxt integration (#14319)
We noticed that Nuxt projects were not working with the tailwindcss
project. The issue was traced down to the fact that Nuxt starts multiple
Vite dev servers and calling the experimental `waitForRequestsIdle()` on
one of the test servers would never resolve.

This was fixed upstream and is part of the latest Vite/Nuxt release:
https://github.com/vitejs/vite/issues/17980.

We still need to handle the fact that Vite can spawn multiple dev
servers. This is necessary because when we invalidate all roots, we need
to find that module inside all of the spawned servers. If we only look
at the _last server_ (what we have done before), we would not find the
module and thus could not invalidate it.
2024-09-04 10:53:15 -04:00
Philipp Spiess
e7ca667f76 Remove leftover TODO in Vite plugin 2024-09-04 11:43:41 +02:00
Philipp Spiess
835922812b
Rework Vite plugin to support lightningcss pre processor and fast rebuilds (#14269)
Fixes #14205
Fixes #14106

This PR reworks the Vite extension in order to supprt `lightningcss` as
the pre-processor, enable faster rebuilds, and adds support for `vite
build --watch` mode. To make this change possible, we've done two major
changes to the extension that have caused the other changes.

## 1. Tailwind CSS is a preprocessor

We now run all of our modifications in `enforce: 'pre'`. This means that
Tailwind CSS now gets the untransformed CSS files rather than the one
already going through postcss or lightningcss. We do this because
Tailwind CSS _is_ a preprocessor at the same level as those tools and we
do sometimes use the language in ways that [creates problems when it's
the input for other
bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269).

The correct solution here is to make Tailwind not depend on any other
transformations. The main reason we were not using the `enforce: 'pre'`
phase in Vite before was becuase we relied on the `@import` flattening
of postcss so we now have to do this manually. `@import` flattening is
now a concern that every Tailwind V4 client has to deal with so this
might actually be something we want to inline into tailwindcss in the
future.

## 2. A Vite config can have multiple Tailwind roots 

This is something that we have not made very explicit in the previous
iteration of the Vite plugin but we have to support multiple Tailwind
roots in a single Vite workspace. A Tailwind root is a CSS file that is
used to configure Tailwind. Technically, any CSS file can be the input
for `tailwindcss` but you have to add certain rules (e.g. `@import
"tailwindcss";`) to make the compiler do something.

A workspace can have multiple of these rules (e.g. by having different
Tailwind configures for different sub-pages). With the addition of
[support for `@source`
rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS
config files](https://github.com/tailwindlabs/tailwindcss/pull/14239),
Tailwind roots become more complex and can have a custom list of
_dependencies_ (that is other JavaScript modules the compiler includes
as part of these new rules). In order to _only rebuild the roots we need
to_, we have to make this separation very clear.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-04 10:09:24 +02:00
Philipp Spiess
390e2d3e8d
Allow @plugin and @config to point to TS files (#14317)
Tailwind V3 used [jiti](https://github.com/unjs/jiti/) to allow
importing of TypeScript files for the config and plugins. This PR adds
the new Jiti V2 beta to our `@tailwindcss/node` and uses it if a native
`import()` fails. I added a new integration test to the CLI config
setup, to ensure it still works with our module cache cleanup.
2024-09-03 18:35:39 +02:00
Philipp Spiess
191c544c67
CSS theme(): Add unit test that combines CSS variable syntax with opacity modifier (#14323)
This PR adds a new test case to the branches that test the CSS `theme()`
function with the CSS variable syntax. The new case includes an opacity
modifier and ensures that the opacity is properly added.
2024-09-03 18:01:04 +02:00
Philipp Spiess
adc8dfb1a2
Ensure CSS theme() functions are evaluated in media query ranges with collapsed whitespace (#14321)
Fixes #14320

This PR adds `>`, `<`, and `=` as separators into the CSS value parser.
This is necessary because [`@media` range
context](https://www.w3.org/TR/mediaqueries-4/#mq-range-context) does
not require spaces around these operators so something like this is a
valid value for the range syntax:

```css
@media (40rem<width<=48rem) { 
  /* ... */
}
```

If you add our CSS `theme()` function to the mix, this rule look like
that:

```css
@media (theme(--breakpoint-sm)<width<=theme(--breakpoint-md)) {
  /* ... */
}
```

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-03 17:27:18 +02:00
Philipp Spiess
a1d56d8e24
Ensure content globs defined in @config files are relative to that file (#14314)
When you configure custom content globs inside an `@config` file, we
want to tread these globs as being relative to that config file and not
the CSS file that requires the content file. A config can be used by
multiple CSS configs.

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-03 16:54:08 +02:00
Adam Wathan
dcfaaac8f6
Prepare v4.0.0-alpha.21 (#14313)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create an issue to first
discuss any significant new features. This includes things like adding
new utilities, creating new at-rules, or adding new component examples
to the documentation.


https://github.com/tailwindcss/tailwindcss/blob/master/.github/CONTRIBUTING.md

-->

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-02 15:43:28 -04:00
Jordan Pittman
c65f20ace0
Support plugin options in CSS (#14264)
Builds on #14239 — that PR needs to be merged first.

This PR allows plugins defined with `plugin.withOptions` to receive
options in CSS when using `@plugin` as long as the options are simple
key/value pairs.

For example, the following is now valid and will include the forms
plugin with only the base styles enabled:

```css
@plugin "@tailwindcss/forms" {
  strategy: base;
}
```

We handle `null`, `true`, `false`, and numeric values as expected and
will convert them to their JavaScript equivalents. Comma separated
values are turned into arrays. All other values are converted to
strings.

For example, in the following plugin definition, the options that are
passed to the plugin will be the correct types:
- `debug` will be the boolean value `true`
- `threshold` will be the number `0.5`
- `message` will be the string `"Hello world"`
- `features` will be the array `["base", "responsive"]`

```css
@plugin "my-plugin" {
  debug: false;
  threshold: 0.5;
  message: Hello world;
  features: base, responsive;
}
```

If you need to pass a number or boolean value as a string, you can do so
by wrapping the value in quotes:

```css
@plugin "my-plugin" {
  debug: "false";
  threshold: "0.5";
  message: "Hello world";
}
```

When duplicate options are encountered the last value wins:

```css
@plugin "my-plugin" {
  message: Hello world;
  message: Hello plugin; /* this will be the value of `message` */
}
```

It's important to note that this feature is **only available for plugins
defined with `plugin.withOptions`**. If you try to pass options to a
plugin that doesn't support them, you'll get an error message when
building:

```css
@plugin "my-plugin" {
  debug: false;
  threshold: 0.5;
}

/* Error: The plugin "my-plugin" does not accept options */
```

Additionally, if you try to pass in more complex values like objects or
selectors you'll get an error message:

```css
@plugin "my-plugin" {
  color: { red: 100; green: 200; blue: 300 };
}

/* Error: Objects are not supported in `@plugin` options. */
```

```css
@plugin "my-plugin" {
  .some-selector > * {
    primary: "blue";
    secondary: "green";
  }
}

/* Error: `@plugin` can only contain declarations. */
```

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-02 12:49:09 -04:00
Jordan Pittman
52012d90d7
Support loading config files via @config (#14239)
In Tailwind v4 the CSS file is the main entry point to your project and
is generally configured via `@theme`. However, given that all v3
projects were configured via a `tailwind.config.js` file we definitely
need to support those. This PR adds support for loading existing
Tailwind config files by adding an `@config` directive to the CSS —
similar to how v3 supported multiple config files except that this is
now _required_ to use a config file.

You can load a config file like so:

```
@import "tailwindcss";
@config "./path/to/tailwind.config.js";
```

A few notes:
- Both CommonJS and ESM config files are supported (loaded directly via
`import()` in Node)
- This is not yet supported in Intellisense or Prettier — should
hopefully land next week
- TypeScript is **not yet** supported in the config file — this will be
handled in a future PR.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-09-02 18:03:16 +02:00
Philipp Spiess
de3c696f07 Fix changelog 2024-09-02 17:36:04 +02:00
Philipp Spiess
c6e4dabfb2
Remove named opacity support for color opacity modifiers (#14278)
Right now if you have a custom opacity theme value configured like this…

```css
@theme {
  --opacity-disabled: 50%;
}
```

…then you can use that named opacity value as a color opacity modifier
like this:

```html
<div class="bg-red-500/disabled">
```

We think this behavior is confusing. The color opacity modifier is not
setting opacity but the alpha value of a color. Opacity is for setting
the opacity of an element, so the custom names you'd come up with are
named after those situations, which makes them not seem appropriate for
color modifiers.
2024-09-02 16:41:38 +02:00
Philipp Spiess
d9e3fd613b
Add standalone CLI (#14270)
This PR adds a new standalone client: A single-binary file that you can
use to run Tailwind v4 without having a node setup. To make this work we
use Bun's single-binary build which can properly package up native
modules and the bun runtime for us so we do not have to rely on any
expand-into-tmp-folder-at-runtime workarounds.

When running locally, `pnpm build` will now standalone artifacts inside
`packages/@tailwindcss-standalone/dist`. Note that since we do not build
Oxide for other environments in the local setup, you won't be able to
use the standalone artifacts for other platforms in local dev mode.

Unfortunately Bun does not have support for Windows ARM builds yet but
we found that using the `bun-baseline` runtime for Windows x64 would
make the builds work fine in ARM emulation mode:

![Screenshot
windows](https://github.com/user-attachments/assets/5b39387f-ec50-4757-9469-19b98e43162d)


Some Bun related issues we faced and worked around:

- We found that the regular Windows x64 build of `bun` does not run on
Windows ARM via emulation. Instead, we have to use the `bun-baseline`
builds which emulate correctly.

- When we tried to bundle artifacts with [embed
directories](https://bun.sh/docs/bundler/executables#embed-directories),
node binary dependencies were no longer resolved correctly even though
they would still be bundled and accessible within the [`embeddedFiles`
list](https://bun.sh/docs/bundler/executables#listing-embedded-files).
We worked around this by using the `import * as from ... with { type:
"file" };` and patching the resolver we use in our CLI.
  
  
- If you have an import to a module that is used as a regular import
_and_ a `with { type: "file" }`, it will either return the module in
both cases _or_ the file path when we would expect only the `with {
type: "file" }` import to return the path. We do read the Tailwind CSS
version via the file system and `require.resolve()` in the CLI and via
`import * from './package.json'` in core and had to work around this by
patching the version resolution in our CLI.
 
  ```ts
  import packageJson from "./package.json"
  import packageJsonPath from "./package.json" with {type: "file"}
  
  // We do not expect these to be equal
  packageJson === packageJsonPath 
  ```
- We can not customize the app icon used for Windows `.exe` builds
without decompiling the binary. For now we will leave the default but
one workaround is to [use tools like
ResourceHacker](698d9c4bd1)
to decompile the binary first.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-09-02 15:23:46 +02:00
Philipp Spiess
ac6d4a6e8e
Spreading tailwindcss/defaultTheme exports keeps bare values (#14257)
In #14221 we added a new export to the `tailwindcss` package:
`tailwindcss/defaultTheme`. This is build on top of the full config from
V3 and will allow plugins to keep being compatible.

However, spreading in from this package has overwritten the bare value
callback handler. This PR fixes it by sharing the bare value callbacks
with the compat config.
2024-08-29 14:05:41 +02:00
Jordan Pittman
fa8253e42a
Fix support for declaration fallbacks in plugins (#14265)
This PR fixes support for "fallback" values for declarations in plugins.

A plugin using `addUtilities`, `matchUtilities`, `addComponents`, etc…
should be able to specify "fallback" values for declarations by passing
an array as the value of a declaration however this does not currently
work in v4 (but it does in v3):

```js
export default {
  plugins: [
    function ({ addUtilities }) {
      addUtilities({
        '.outlined': {
          outline: ['1px solid ButtonText', '1px auto -webkit-focus-ring-color'],
        },
      })
    },
  ],
};
```

After this PR the candidate `outlined` will now produce the following
CSS — like it does in v3:

```css
.outlined {
  outline: 1px solid ButtonText;
  outline: 1px auto -webkit-focus-ring-color;
}
```
2024-08-27 12:18:53 +02:00
Philipp Spiess
f5499aac95
Fix issues with the CSS theme() function (#14262)
While working on #14257, we noticed two issues with the CSS `theme()`
function:

1. In V3 it's possible to set arrays inside the theme object. An example
for this is the default font families as defined here:
https://github.com/tailwindlabs/tailwindcss/blob/main/stubs/config.full.js#L303-L311.
We now properly join these arrays that are not tuples.
2. We noticed that in the case where there are no modifiers, the
fallback values for the CSS `theme()` function had the first word
removed. A regression test for this was added.
2024-08-26 18:55:27 +02:00
Philipp Spiess
35398e0ffb Add .gitattributes and define a merge strategy for changelog files 2024-08-26 16:27:41 +02:00
Philipp Spiess
0796653527
Correctly merge tuple values when using the plugin API (#14260)
We noticed that when the `defaultTheme` (change for this is coming in
#14257) defines a tuple that is also defined in the CSS theme, the
values are incorrectly merged as objects instead of overwritten.
However, CSS theme values should take precedence, even when they use
tuple syntax.

Proper coverage of this will come once `#14257` is merged when calling
`theme(fontSize.xs[1].lineHeight)` will also have a default value passed
in from the `defaultTheme`.
2024-08-26 16:23:52 +02:00
Philipp Spiess
ab8972749c
Postcss: Bring back proper type exports (#14256)
Closes #14253

Since we changed the export strategy for the postcss client in #14132,
we accidentally no longer generated type exports for this package.

This PR adds a type export back. We now use a similar pattern to the
`./colors` and `./defaultTheme` exports in the tailwindcss package where
we have a separate cjs entrypoint.

The changes were validated manually in a playground project that were
installing the updated dependencies from tarballs.

Here is one example of it working as expected:
 
<img width="750" alt="Screenshot 2024-08-26 at 14 10 07"
src="https://github.com/user-attachments/assets/83de15f2-1543-4805-9231-9b8df1636c5e">
2024-08-26 15:54:07 +02:00
Philipp Spiess
d5f563b746
Prepare v4.0.0-alpha.20 (#14244)
Prepare next `4.0.0-alpha.20` release
2024-08-23 15:51:53 +02:00
Philipp Spiess
a84e52a46c
Move value parser into tailwindcss root (#14236)
This PR is moving content from
`packages/tailwindcss/src/value-parser/*.ts` into
`packages/tailwindcss/src/value-parser.ts` to simplify the file
structure.
2024-08-22 12:32:49 -04:00
Philipp Spiess
fcc07826f7
Remove unused resolveNamespace method (#14235)
Removing a leftover API from the recent changes in
https://github.com/tailwindlabs/tailwindcss/pull/14177
2024-08-22 11:21:14 -04:00
Jordan Pittman
cc228fbfc3
Add support for matching multiple utility definitions for one candidate (#14231)
Currently if a plugin adds a utility called `duration` it will take
precedence over the built-in utilities — or any utilities with the same
name in previously included plugins. However, in v3, we emitted matches
from _all_ plugins where possible.

Take this plugin for example which adds utilities for
`animation-duration` via the `duration-*` class:

```ts
import plugin from 'tailwindcss/plugin'

export default plugin(
  function ({ matchUtilities, theme }) {
    matchUtilities(
      { duration: (value) => ({ animationDuration: value }) },
      { values: theme("animationDuration") },
    )
  },
  {
    theme: {
      extend: {
        animationDuration: ({ theme }) => ({
          ...theme("transitionDuration"),
        }),
      }
    },
  }
)
```

Before this PR this plugin's `duration` utility would override the
built-in `duration` utility so you'd get this for a class like
`duration-3500`:
```css
.duration-3000 {
  animation-duration: 3500ms;
}
```

Now, after this PR, we'll emit rules for `transition-duration`
(Tailwind's built-in `duration-*` utility) and `animation-duration`
(from the above plugin) and you'll get this instead:
```css
.duration-3000 {
  transition-duration: 3500ms;
}

.duration-3000 {
  animation-duration: 3500ms;
}
```

These are output as separate rules to ensure that they can all be sorted
appropriately against other utilities.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-08-22 16:22:12 +02:00
Philipp Spiess
bc88958855
Add support for the CSS theme() function (#14177)
This PR adds the CSS `theme()` function to Tailwind V4. It's intended
use is to get the raw value of a theme variable. This can be handy when
trying to reference theme values in places where `var()` is not
supported, for example:

```css
@media (min-width: theme(--breakpoint-md)) {
  /*...*/
}
```

The CSS `theme()` function is backward compatible with Tailwind V3 which
means that it can also be used with the old key path syntax, like:
`theme(colors.red.500)`. The lookup for this is shared with the plugin
`theme()` function and this PR adds a bunch of edge cases to validate
the backward compatibility. Here are a few interesting cases that we
found to be valid in Tailwind V3 and are now also valid in Tailwind V4:

```js
// First argument can be inside quotes
theme('colors.red.500') 
// Square brackets are valid separators in V3, even when chained with dots
theme(color[red].500)
// Slashes can be used for adding opacity to colors. This can also be inside quotes
theme('colors.red.500 / 75%')
// Oh yeah and there's also the tuple syntax for accessing v3 scoped variables
theme(fontSize.xs[1].lineHeight)
// themes can also define fallback values which could be theme calls again...
theme(colors.red.unknown / 75%, theme(colors.red.500 / 25%))
// ... or list of values:
theme(fontFamily.sans, 'Helvetica Neue', Helvetica, sans-serif)
// Theme function can also be used in candidate class names...
sm:[--color:theme(colors.red[500])
// ... and of course @media queries
@media (min-width: theme(breakpoint.md)) and (max-width: theme(--breakpoint-lg))
```

The way this is implemented right now is by adding a separate walk that
scans all declaration values. If these values look like they could have
a `theme()` function call, we will parse these values using a new
`ValueParser` into a small AST that can be used to substitute function
calls.
2024-08-22 15:46:44 +02:00
Jordan Pittman
30bbe51a38
Improve compatibility with @tailwindcss/typography and @tailwindcss/forms (#14221)
This PR enables compatibility for the `@tailwindcss/typography` and
`@tailwindcss/forms` plugins. This required the addition of new Plugin
APIs and new package exports.

## New Plugin APIs and compatibility improvements

We added support for `addComponents`, `matchComponents`, and `prefix`.
The component APIs are an alias for the utilities APIs because the
sorting in V4 is different and emitting components in a custom `@layer`
is not necessary. Since `prefix` is not supported in V4, the `prefix()`
API is currently an identity function.

```js
 addComponents({
  '.btn': {
    padding: '.5rem 1rem',
    borderRadius: '.25rem',
    fontWeight: '600',
  },
  '.btn-blue': {
    backgroundColor: '#3490dc',
    color: '#fff',
    '&:hover': {
      backgroundColor: '#2779bd',
    },
  },
  '.btn-red': {
    backgroundColor: '#e3342f',
    color: '#fff',
    '&:hover': {
      backgroundColor: '#cc1f1a',
    },
  },
})
```

The behavioral changes effect the `addUtilities` and `matchUtilities`
functions, we now:

- Allow arrays of CSS property objects to be emitted:
  ```js
  addUtilities({
    '.text-trim': [
      {'text-box-trim': 'both'},
      {'text-box-edge': 'cap alphabetic'},
    ],
  })
  ```
- Allow arrays of utilities
  ```js
  addUtilities([
    {
      '.text-trim':{
        'text-box-trim': 'both',
        'text-box-edge': 'cap alphabetic',
      },
    }
  ])
  ```
- Allow more complicated selector names
  ```js
  addUtilities({
    '.form-input, .form-select, .form-radio': {
      /* styles here */
    },
    '.form-input::placeholder': {
      /* styles here */
    },
    '.form-checkbox:indeterminate:checked': {
      /* styles here */
    }
  })
  ```

## New `tailwindcss/color` and `tailwindcss/defaultTheme` export

To be compatible to v3, we're adding two new exports to the tailwindcss
package. These match the default theme values as defined in v3:

```ts
import colors from 'tailwindcss/colors'

console.log(colors.red[600])
```

```ts
import theme from 'tailwindcss/defaultTheme'

console.log(theme.spacing[4])
```

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-08-22 08:06:21 -04:00
Philipp Spiess
84ebe19da2
Vite: Retain candidates between input CSS updates (#14228)
This PR fixes an issue introduced with the changed candidate cache
behavior in #14187.

Prior to #14187, candidates were cached globally within an instance of
Oxide. This meant that once a candidate was discovered, it would not
reset until you either manually cleared the cache or restarted the Oxide
process. With the changes in #14187 however, the cache was scoped to the
instance of the `Scanner` class with the intention of making the caching
behavior more easy to understand and to avoid a global cache.

This, however, had an unforeseen side-effect in our Vite extension.
Vite, in dev mode, discovers files _lazily_. So when a developer goes to
`/index.html` the first time, we will scan the `/index.html` file for
Tailwind candidates and then build a CSS file with those candidate. When
they go to `/about.html` later, we will _append_ the candidates from the
new file and so forth.

The problem now arises when the dev server detects changes to the input
CSS file. This requires us to do a re-scan of that CSS file which, after
#14187, caused the candidate cache to be gone. This is usually fine
since we would just scan files again for the changed candidate list but
in the Vite case we would only get the input CSS file change _but no
subsequent change events for all other files, including those currently
rendered in the browser_). This caused updates to the CSS file to remove
all candidates from the CSS file again.

Ideally, we can separate between two concepts: The candidate cache and
the CSS input file scan. An instance of the `Scanner` could re-parse the
input CSS file without having to throw away previous candidates. This,
however, would have another issue with the current Vite extension where
we do not properly retain instances of the `Scanner` class anyways. To
properly improve the cache behavior, we will have to fix the Vite
`Scanner` retaining behavior first. Unfortunately this means that for
the short term, we have to add some manual bookkeeping to the Vite
client and retain the candidate cache between builds ourselves.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-21 12:54:42 +02:00
Adam Wathan
45fb21e753
Add support for the theme() function in the plugin API (#14207)
This PR adds support for the [`theme()`
function](https://tailwindcss.com/docs/plugins#dynamic-utilities) from
the v3 plugin API, used for configuring which values functional
utilities support:

```js
plugin(function({ matchUtilities, theme }) {
  matchUtilities(
    {
      tab: (value) => ({
        tabSize: value
      }),
    },
    { values: theme('tabSize') }
  )
})
```

Things this handles:

- "Upgrading" theme keys to their v4 names, so if you do
`theme('colors')` that will correctly retrieve all the colors from the
`--color-*` namespace with the new CSS variable based configuration
- Polyfilling dependent keys, so `theme('backgroundColor')` will still
pull everything in `--color-*` even though there is no values in the
`backgroundColor` namespace in v4 by default
- Polyfilling theme values that are now handled by "bare values"
internally, so even though there is no `flexShrink` theme values in v4,
`theme('flexShrink')` will still configure your plugin to properly
support any value that the built-in `shrink-*` utilities support

Things that aren't handled:

- Theme values that have been replaced by static utilities can't be
retrieved yet, so for example `theme('cursor')` returns nothing right
now because there are no values for the `cursor-*` utilities in the
theme anymore, they are all just baked in to the framework.

This will be handled in a future PR.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-20 11:05:08 -04:00
Adam Wathan
3df38a7458
Don't wrap relative selectors in arbitrary variants with :is(…) (#14203)
Prior to this PR, we weren't accounting for the fact that `:has(…)`
supports [relative
selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selector_structure#relative_selector),
which are sort of like partial selectors that can start with a
combinator like `>`, `+`, or `~`.

Before, a class like `has-[>img]:flex` would generate this:

```css
.has-\[\>img\]\:flex:has(*:is(> img)) {
  display: flex;
}
```

This was incorrect because `*:is(> img)` isn't even valid CSS at all, so
the rule would do nothing.

After this change, we generate this instead:

```css
.has-\[\>img\]\:flex:has(> img) {
  display: flex;
}
```

This PR also ensures that relative selectors are recognized as invalid
in places where they are not supported, so classes like
`group-[>img]:flex` for example will produce nothing now instead of
invalid CSS.

This is mostly a simple change but it did involve storing some
additional information in the variant AST.

Fixes #14202.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-16 13:42:04 -04:00
Philipp Spiess
e10b786437
Add Next.js integration test (#14163)
Using the [new integration test
setup](https://github.com/tailwindlabs/tailwindcss/pull/14089), this PR
adds a test for a V4 Next.js setup using the Postcss plugin. It's
testing both a full build and the dev mode (non-turbo for now).

Because of webpack, tests are quite slow which is worrisome since we
probably need to add many more integrations in the future. One idea I
have is that we separate tests in two buckets: _essential_ tests that
run often and are fast and advanced suites that we only run on CI via
custom, non-blocking, jobs.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-16 15:45:52 +02:00
Robin Malfait
a902128640
Improve Oxide scanner API (#14187)
This PR updates the API for interacting with the Oxide API. Until now,
we used the name `scanDir(…)` which is fine, but we do way more work
right now.

We now have features such as:

1. Auto source detection (can be turned off, e.g.: `@tailwindcss/vite`
doesn't need it)
2. Scan based on `@source`s found in CSS files
3. Do "incremental" rebuilds (which means that the `scanDir(…)` result
was stateful).

To solve these issues, this PR introduces a new `Scanner` class where
you can pass in the `detectSources` and `sources` options. E.g.:

```ts
let scanner = new Scanner({
  // Optional, omitting `detectSources` field disables automatic source detection
  detectSources: { base: __dirname }, 

  // List of glob entries to scan. These come from `@source` directives in CSS.
  sources: [
    { base: __dirname, pattern: "src/**/*.css" },
    // …
  ],
});
```

The scanner object has the following API:

```ts
export interface ChangedContent {
  /** File path to the changed file */
  file?: string
  /** Contents of the changed file */
  content?: string
  /** File extension */
  extension: string
}
export interface DetectSources {
  /** Base path to start scanning from */
  base: string
}
export interface GlobEntry {
  /** Base path of the glob */
  base: string
  /** Glob pattern */
  pattern: string
}
export interface ScannerOptions {
  /** Automatically detect sources in the base path */
  detectSources?: DetectSources
  /** Glob sources */
  sources?: Array<GlobEntry>
}
export declare class Scanner {
  constructor(opts: ScannerOptions)
  scan(): Array<string>
  scanFiles(input: Array<ChangedContent>): Array<string>
  get files(): Array<string>
  get globs(): Array<GlobEntry>
}
```

The `scanFiles(…)` method is used for incremental rebuilds. It takes the
`ChangedContent` array for all the new/changes files. It returns whether
we scanned any new candidates or not.

Note that the `scanner` object is stateful, this means that we don't
have to track candidates in a `Set` anymore. We can just call
`getCandidates()` when we need it.

This PR also removed some unused code that we had in the `scanDir(…)`
function to allow for sequential or parallel `IO`, and sequential or
parallel `Parsing`. We only used the same `IO` and `Parsing` strategies
for all files, so I just got rid of it.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-16 15:05:42 +02:00
Martin Feckie
cc4689deef
Add Glimmer template extensions to Oxide content detection (#14199)
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-15 09:34:19 -04:00
Robin Malfait
5035c106fb
Bump GitHub actions to latest version (#14182)
This PR bumps the versions of common GitHub workflows to the latest
version.
2024-08-13 18:12:19 +02:00
Jordan Pittman
e299ea381f
Add support for the tailwindcss/plugin export (#14173)
This PR adds support for the `tailwindcss/plugin` import which has
historically been used to define custom plugins:

```js
import plugin from "tailwindcss/plugin";

export default plugin(function ({ addBase }) {
  addBase({
    // ...
  });
});
```

This also adds support for `plugin.withOptions` which was used to define
plugins that took optional initilization options when they were
registered in your `tailwind.config.js` file:

```js
import plugin from "tailwindcss/plugin";

export default plugin.withOptions((options = {}) => {
  return function ({ addBase }) {
    addBase({
      // ...
    });
  };
});
```

We've stubbed out support for the `config` argument but we're not
actually doing anything with it at the time of this PR. The scope of
this PR is just to allow people to create plugins that currently work
using the raw function syntax but using the `plugin` and
`plugin.withOptions` APIs. Support for `config` will land separately.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-08-13 10:25:29 -04:00
Jordan Pittman
9ab47329b8
Add support for addBase in plugins (#14172)
This PR adds support for `addBase` in JS plugins which adds styles into
the CSS base layer using `@layer base`. This exists for backwards
compatibility with v3 but is not something we will encourage people to
use going forward — in v4 it's better to just write these styles in a
CSS file.

In v3, `@layer base` was something we compiled away and was only used
for determining where to add some styles in the final CSS, but in v4 we
are using native CSS layers. This means that `addBase` in v4 expects you
to have a _real_ `@layer base` in your final CSS, which you will have as
long as you are using `@import "tailwindcss"` to add Tailwind to your
project.

Now something like this works:
```js
function ({ addBase }) {
  addBase({
    'h1': { fontSize: '2em' },
    'h2': { fontSize: '1.5em' },
  })
}
```

Which will emit the following CSS:
```css
@layer base {
  h1 {
    font-size: 2em;
  }

  h2 {
    font-size: 1.5em;
  }
}
```

The only limitation compared to v3 is that there is no way for you to
wrap these styles in another custom layer.

In v3 you could do this:

```css
@layer my-base {
  @tailwind base;
}
```

…and then anything you added with `addBase` would end up exactly where
`@tailwind base` was in your source CSS.

But in v4 there is no `@tailwind base`, so there's no way to wrap these
styles in `@layer my-base` like in the example above. All base styles
added by plugins are simply appended to the end of the stylesheet but
wrapped in `@layer base` so they behave as if they are co-located with
other base styles.

Odds of this impacting anyone are extremely low, but if it proves to be
an actual issue I think we could output these styles at the location of
an optional `@tailwind base` rule if we detect it exists.
2024-08-12 14:44:44 -04:00
Philipp Spiess
8cace0d9b5
Retry UI test assertions (#14165)
We noticed that some of the tests depending on the `hover:` variant were
flaky. After some investigation, we found that injected elements had the
`:hover` state without us explicitly hovering over the element.

To avoid this, we now set up an explicit placeholder element to move the
mouse to before running the tests.
2024-08-12 12:44:26 +02:00
Philipp Spiess
fbf877aa0a
Fix Rust build by passing through RUSTUP_HOME variable (#14171)
While rebasing on the latest changes on `next`, especially #14160, I
noticed that my local `pnpm build` step was no longer working and erring
with the following:

```
│ > @tailwindcss/oxide@4.0.0-alpha.19 build /Users/philipp/dev/tailwindcss/crates/node
│ > npx napi build --platform --release --no-const-enum
│
│ Type Error: Could not parse the Cargo.toml: Error: Command failed: cargo metadata --format-version 1 --manifest-path "/Users/philipp/dev/
│ tailwindcss/crates/node/Cargo.toml"
│ error: rustup could not choose a version of cargo to run, because one wasn't specified explicitly, and no default is configured.
│ help: run 'rustup default stable' to download the latest stable release of Rust and set it as your default toolchain.
│
│ error: rustup could not choose a version of cargo to run, because one wasn't specified explicitly, and no default is configured.
│ help: run 'rustup default stable' to download the latest stable release of Rust and set it as your default toolchain.
```

It turns out that with the changes in turbo v2, env variables no longer
propagate to the individual tasks automatically but since I installed
rustup outside of the default `~/.rustup` directory, the task was no
longer able to find it.

To fix this, we now define `RUSTUP_HOME` as a global env to always pass
through.
2024-08-12 11:37:45 +02:00
Robin Malfait
558dcd568b
Fix v4 release workflow (#14167)
This PR fixes the release workflow

- We added a postbuild step so that any arguments/flags passed to the
`pnpm run build` command are forwarded to the underlying command.
- We made sure that we run any `pnpm` specific flags before the actual
command
- Cleaned up the CI workflow a bit
2024-08-09 19:57:16 +02:00
Robin Malfait
f5f91ce9de
Prepare v4.0.0-alpha.19 (#14162)
Prepare next `4.0.0-alpha.19` release
2024-08-09 17:58:30 +02:00
Jordan Pittman
759c1a37a9
Unify implementations of compile and __unstable__loadDesignSystem (#14150)
Right now there's some minor duplication and a ton of missing stuff in
`__unstable__loadDesignSystem` — this PR makes sure the functionality is
unified between it and `compile()`
2024-08-09 11:43:33 -04:00
Jordan Pittman
f06472eb95
Add matchUtilities and addUtilities APIs (#14114)
This PR introduces support for the v3-like `addUtilities` and
`matchUtilities` APIs in v4. We anticipate designing a new API that
feels more native to the way v4 works before shipping v4.0 stable, but
we're continuing to support these APIs for backwards compatibility.

We've tried to make the behavior as identical as possible, but because
of fundamental differences between the v3 and v4 engines there are a few
things that work differently:

## Only simple single-class selectors are supported

In v3 you could pass a complex CSS selector to `addUtilities` and we
would generate a utility for every class in the selector. In v4 we only
allow you to use a simple, single-class selector.

You should use nesting if you need a more complex selector, or need to
include at-rules like `@media` or `@supports`.

```js
// v3
function ({ addUtilities }) {
  addUtilities({
    '.scrollbar-none::-webkit-scrollbar': {
      display: 'none',
    },
  })
}

// v4
function ({ addUtilities }) {
  addUtilities({
    '.scrollbar-none': {
      '&::-webkit-scrollbar': {
        display: 'none',
      },
    },
  })
}
```

If you were adding custom utilities that included two classes and were
depending on both of those classes behaving like utilities (they could
each be used with variants), those custom utilities will need to be
rewritten as two separate utilities that each use nesting:

```js
// v3
function ({ addUtilities }) {
  addUtilities({
    '.section > .row': {
      color: 'red',
    },
  })
}

// v4
function ({ addUtilities }) {
  addUtilities({
    '.section': {
      '& > .row': {
        color: 'red',
      },
    },

    '.row': {
      'section > &': {
        color: 'red',
      },
    },
  })
}
```

We may introduce support for this in the future if this limitation turns
out to be a huge pain in the ass, but crossing our fingers that people
were mostly doing simple stuff here.

## Opacity modifiers support bare values

To be consistent with how built-in utilities work in v4, custom
utilities that specify `type: "color"` now get "bare value" support for
opacity modifiers. This means that a utility like `foo-black/33` will
work out of the box without having to either add `--opacity-33` to your
theme nor would you need to add it to the `modifiers` option.

## The `preferOnConflict` type option is gone

In v3 we introduced an internal API called `preferOnConflict` for types.
This was used as a way to disambiguate between two utilities with the
same "root" but which produced different properties which used the same
CSS data types. This was only applicable to arbitrary values and was
only used for disambiguating between `background-position` and
`background-size`.

In v4, both of these properties are handled by a single plugin meaning
this feature is no longer necessary. No one should've really been using
this option anyway as it was never documented so we're dropping the
feature.

## The options `respectPrefix` and `respectImportant` are not yet
supported

Neither the `prefix` nor `important` features exist in any form in v4 at
this time. Therefore, they are not currently supported by this PR. We
will look into supporting them if/when those features return.

## The `theme(…)` function is not currently supported

Custom utilities defined using `matchUtilities` often use the `theme(…)`
function to define their default values, but we haven't implemented
support for `theme(…)` yet in v4.

This means that as of this PR, default values for custom utilities must
be hardcoded:

```js
function ({ matchUtilities }) {
  matchUtilities({
    'tab': (value) => {
      return {
        'tab-size': value,
      }
    },
  }, {
    values: {
      2: '2',
      4: '4',
      8: '8',
    },
  })
}
```

Getting `theme(…)` working is a big project so we're going to tackle it
in a separate PR.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-09 11:34:27 -04:00
Robin Malfait
d223112162
Bump dependencies (#14160)
This PR bumps dependencies

We also make some dependencies `catalog:` dependencies, which allows us
to keep
the version in sync. E.g.: `lightningcss` and `@types/node`.

Bumped `turbo` to the latest version + enabled the new UI

Fixed a bug in the tests now that `lightningcss` outputs the correct
value.
2024-08-09 16:12:24 +02:00
Robin Malfait
7d73e51359
Ensure @apply works inside @utility (#14144)
This PR fixes an issue where `@apply` was not handled if it was used
inside of `@utility`.

You should now be able to do something like this:
```css
@utility btn {
  @apply flex flex-col bg-white p-4 rounded-lg shadow-md;
}
```

If you then use `btn` as a class, the following CSS will be generated:
```css
.btn {
  border-radius: var(--radius-lg, .5rem);
  background-color: var(--color-white, #fff);
  padding: var(--spacing-4, 1rem);
  --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
  --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  flex-direction: column;
  display: flex;
}
```

This PR also makes sure that you can use custom `@utility` inside other
`@utility` via `@apply`. E.g.:
```css
@utility foo {
  color: red;
}

@utility bar {
  color: red;
  @apply hover:foo;
}
```

If we detect a circular dependency, then we will throw an error since
circular dependencies are not allowed. E.g.:
```css
@utility foo {
  @apply hover:bar;
}

@utility bar {
  @apply focus:baz;
}

@utility baz {
  @apply dark:foo;
}
```
Regardless of which utility you use, eventually it will apply itself.

Fixes: #14143
2024-08-09 16:09:25 +02:00
Philipp Spiess
b01ff53f2a
Vite: Support Tailwind in Vue <style> blocks (#14158)
This PR adds support to transforming `<style>` blocks emitted by Vue
components with tailwindcss when the `@tailwindcss/vite` is used.

Example:

```vue
<style>
  @import 'tailwindcss/utilities';
  @import 'tailwindcss/theme' theme(reference);
  .foo {
    @apply text-red-500;
  }
</style>
<template>
 <div class="underline foo">Hello Vue!</div>
</template>
```

Additionally, this PR also adds an integration test.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-09 12:41:58 +02:00
Jordan Pittman
1fdf67989f Remove errant console log calls 2024-08-08 15:29:47 -04:00
Philipp Spiess
921b4b673b
Use import to load plugins (#14132)
Alternative to #14110

This PR changes the way how we load plugins to be compatible with ES6
async `import`s. This allows us to load plugins even inside the browser
but it comes at a downside: We now have to change the `compile` API to
return a `Promise`...

So most of this PR is rewriting all of the call sites of `compile` to
expect a promise instead of the object.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-08 11:49:06 -04:00
Adam Wathan
d55852a17c
Update CHANGELOG.md 2024-08-08 10:05:38 -04:00
Adam Wathan
8a01fabb6d
Update CHANGELOG.md 2024-08-08 10:05:15 -04:00