I noticed a lot more backwards compatibility concerns had started
leaking into core, especially around the `theme` function, so did a bit
of work to try and pull that stuff out and into the compatibility layer.
Now the core version of `theme` only handles CSS variables (like
`--color-red-500`) and has no knowledge of the dot notation or how to
upgrade it. Instead, we unconditionally override that function in the
compatibility layer with a light version that _does_ know how to do the
dot notation upgrade, and override that again with the very heavy/slow
version that handles JS config objects only if plugins/JS configs are
actually used.
I've also renamed `registerPlugins` to `applyCompatibilityHooks` because
the name was definitely a bit out of date given how much work it's doing
now, and now call it unconditionally from core, leaving that function to
do any conditional optimizations itself internally.
Next steps I think would be to split up `plugin-api.ts` a bit and maybe
make `applyCompatibilityHooks` its own file, and move both of those
files into the `compat` folder so everything is truly isolated there.
My goal with this stuff is that if/when we ever decide to drop backwards
compatibility with these features in the future (maybe v5), that all we
have to do is delete the one line of code that calls
`applyCompatibilityHooks` in `index.ts`, and delete the `compat` folder
and we're done. I could be convinced that this isn't a worthwhile goal
if we feel it's making the codebase needlessly complex, so open to that
discussion as well.
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
The internal `registerPlugins()` API is used to enable backwards
compatibility with v3 plugins and configs and it is called on every
build even when no v3 plugins or configs are used. This function has a
non-trivial cost in that case — around 5ms.
So this PR does a few things:
## Implements a simpler, faster `theme(…)` function
We now have a much simpler `theme(…)` function that can be used when
backwards compatibility is not necessary. It still supports many of the
same features:
- The modern, v4 style CSS variable syntax `theme(--color-red-500)`
- The legacy, v3 path style `theme(colors.red.500)`
- And the v3-style alpha modifier `theme(colors.red.500 / 50%)`
- Path upgrades so things like `theme(accentColor.red.500)` pulls from
`--color-red-500` when no `--accent-color-red-500` theme key exists
When you do have plugins or configs the more advanced `theme(…)`
function is swapped in for more complete backwards compatibility.
## `registerPlugins` registers globs
Before `registerPlugins` passed the `ResolvedConfig` out so we could
register globs in `compile()`. Since that one function is really the
main driver for backwards compat we decided to move the content path
registration into `registerPlugins` itself when it comes to paths
provided by plugins and configs.
This is an internal implementation detail (well this entire PR is) but
it's worth mentioning. This method is used to resolve a theme value from
a theme key.
## `registerPlugins` is now only called when necessary
All of the above work made it so that `registerPlugins` can be called
only as needed. This means that when no v3 plugins or configs are used,
`registerPlugins` is never called thus elminating the performance impact
of config resolution.
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Right now the following does not work and instead produces a type error:
```
import { type Config } from 'tailwindcss'
export default {
// … config here
} satisfies Config
```
We were not exporting a `Config` type but thankfully this already exists
in the codebase so we just need to export it.
It does _not_ have all properties of an existing config as not all
features have been implemented (or in some cases necessary / relevant
for v4).
Notably missing are:
- `important`
- `prefix`
- `separator`
- `safelist`
- `blocklist`
- `future`
- `experimental`
- `corePlugins`
Also, explicit keys for theme are not currently specified but we should
probably bring this back even if just as an auto-complete aid.
Previously, given the following CSS and configuration:
```css
/* app.css */
@theme default {
--font-size-base: 1.25rem;
--font-size-base--line-height: 1.5rem;
}
@tailwind utilities;
@config "./config.js";
```
```js
// config.js
export default {
theme: {
fontSize: {
// …
base: ['1rem', { lineHeight: '1.75rem' }],
},
// …
},
};
```
When a config or a plugin asked for the value of `theme(fontSize.base)`
like so:
```js
// config.js
export default {
theme: {
// …
typography: ({ theme }) => ({
css: {
'[class~="lead"]': {
fontSize: theme('fontSize.base')[0],
...theme('fontSize.base')[1],
},
}
}),
},
};
```
We would instead pull the values from the CSS theme even through they're
marked with `@theme default`. This would cause the incorrect font size
and line height to be used resulting in something like this (in the case
of the typography plugin with custom styles):
```css
.prose [class~="lead"] {
font-size: 1.25rem;
line-height: 1.5rem;
}
```
After this change we'll now pull the values from the appropriate place
(the config in this case) and the correct font size and line height will
be used:
```css
.prose [class~="lead"] {
font-size: 1rem;
line-height: 1.75rem;
}
```
This will work even when some values are overridden in the CSS theme:
```css
/* app.css */
@theme default {
--font-size-base: 1.25rem;
--font-size-base--line-height: 1.5rem;
}
@theme {
--font-size-base: 2rem;
}
@tailwind utilities;
@config "./config.js";
```
which would result in the following CSS:
```css
.prose [class~="lead"] {
font-size: 2rem;
line-height: 1.75rem;
}
```
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR fixes an issue where theme function calls like
`theme('transitionTimingFunction.DEFAULT')` would incorrectly resolve to
an object when the set of defined CSS theme values looked like this:
```css
@theme {
--transition-timing-function-in: ease-in;
--transition-timing-function-out: ease-out;
--transition-timing-function-in-out: ease-out;
}
```
We were mistakenly retrieving the entire
`--transition-timing-function-*` namespace in this case and returning an
object, even though the user is explicitly asking for a single value by
including `.DEFAULT` in their call.
This ensures it resolves to null instead. Fixes an issue I ran into on
this live stream earlier today:
https://x.com/adamwathan/status/1831740214051799281
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
There are still instances in which CI is flaky after #14332. This PR
applies the same fix (that is, moving the file write into the retrying
block) to all `retryAssertion` callbacks.
When we observe that no new candidates were found, then we can return
early because nothing really changed. There is also no need to
re-optimize (use Lightning CSS) in this case.
But this had a side effect that when no new candidates were detected,
that you didn't see any output either. This feels like nothing is
working from a DX perspective.
Typically you are changing things, so it's not really a problem. But the
moment you use a class that already existed (e.g.: in another file) you
also don't get any output because we have a shared cache.
This PR solves that by always showing the output. But it still doesn't
write to disk if nothing changed.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR moves support for opacity modifies from the CSS `theme()`
function into the plugin `theme()` implementation, this will allow
plugins to use this, too:
```ts
let plugin = plugin(function ({ addUtilities, theme }) {
addUtilities({
'.percentage': {
color: theme('colors.red.500 / 50%'),
},
'.fraction': {
color: theme('colors.red.500 / 0.5'),
},
'.variable': {
color: theme('colors.red.500 / var(--opacity)'),
},
})
})
}
```
There's a small behavioral change for the CSS `theme()` function. Since
tuples are resolved by default for the CSS `theme()` function only,
these will no longer have opacity applied to their first values. This is
probably fine given the reduced complexity as I don't expect the first
values of tuples to be colors and the fix would mean we would have to
parse the modifier in different places.
This PR fixes an issue where variables like `--default-font-family`
wouldn't behave as expected when customizing `fontFamily.sans` or
`fontFamily.mono` in a JS config.
Because theme values added by JS config files are added as `reference`,
customizing `fontFamily.sans` means the `--font-family-sans` variable no
longer exists in the generated CSS.
The `--default-font-family` variable is set to `var(--font-family-sans)`
by default, so because that variable doesn't exist,
`--default-font-family` is effectively undefined and the browser default
font stack is used. This is unexpected because historically customizing
`fontFamily.sans` has updated your default font for your entire project.
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
This PR fixes a small bug where theme values could not be defined as
arrays in JS config files, for example:
```js
export default {
theme: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
},
}
```
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
This works similar to the Vue setup. The styles that Astro will receive
might still contain Tailwind CSS APIs but since it's not picky, we can
pass that through to the regular Vite `transform` handlers for now.
This, however, will have issues like
https://github.com/tailwindlabs/tailwindcss/issues/14205. We have to fix
this together with Vue and other similar extensions later. For now, it
will break when syntax is used that lightningcss rewrites (like `@apply
text-3xl/tight;`)
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
We removed named opacity modifier support in #14278 but we (read: me
lol) totally forgot about the suggestions in intellisense. So we need to
make sure that we don't suggest those either.
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>
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>
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 😅 ).
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.
Fixes#14205Fixes#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>
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.
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.
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>
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>
<!--
👋 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>
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>
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>
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.
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:

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>
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.
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;
}
```
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.
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`.
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">