Commit graph

26 commits

Author SHA1 Message Date
Adam Wathan
28e46badf7
Rename --font-size-* variables to --text-* (#14909)
This PR updates all of the `--font-size-*` variables to `--text-*`
instead to closer match the utility names.

```diff
  @theme {
-   --font-size-xs: 0.75rem;
-   --font-size-xs--line-height: 1rem;
-   --font-size-sm: 0.875rem;
-   --font-size-sm--line-height: 1.25rem;
-   --font-size-base: 1rem;
-   --font-size-base--line-height: 1.5rem;
-   --font-size-lg: 1.125rem;
-   --font-size-lg--line-height: 1.75rem;
-   --font-size-xl: 1.25rem;
-   --font-size-xl--line-height: 1.75rem;

    /* ... */
  
+   --text-xs: 0.75rem;
+   --text-xs--line-height: 1rem;
+   --text-sm: 0.875rem;
+   --text-sm--line-height: 1.25rem;
+   --text-base: 1rem;
+   --text-base--line-height: 1.5rem;
+   --text-lg: 1.125rem;
+   --text-lg--line-height: 1.75rem;
+   --text-xl: 1.25rem;
+   --text-xl--line-height: 1.75rem;

    /* ... */
  }
```

This is part of a bigger set of changes where we're renaming other theme
variables as well with the same goals, since many existing theme
variables like `--shadow-*` and `--radius-*` are already not using the
explicit CSS property name.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-11-07 14:08:06 -05:00
Adam Wathan
ca4e4aebd8
Adjust shadow, radius, and blur scales to ensure all utilities have a value suffix (#14849)
This PR reworks the default `--shadow-*` and `--inset-shadow-*` scales
to remove the bare `shadow` and `inset-shadow` utilities, and ensure
every shadow has an explicit size as part of the utility name.

Here's a complete list of changes:

| v3                | v4 Alpha          | Proposed           |
| ----------------- | ----------------- | ------------------ |
| _N/A_             | `shadow-xs`       | `shadow-2xs`       |
| `shadow-sm`       | `shadow-sm`       | `shadow-xs`        |
| `shadow`          | `shadow`          | `shadow-sm`        |
| `shadow-md`       | `shadow-md`       | `shadow-md`        |
| `shadow-lg`       | `shadow-lg`       | `shadow-lg`        |
| `shadow-xl`       | `shadow-xl`       | `shadow-xl`        |
| _N/A_ | `inset-shadow-xs` | `inset-shadow-2xs` |
| _N/A_ | `inset-shadow-sm` | `inset-shadow-xs`  |
| `shadow-inner`    | `inset-shadow`    | `inset-shadow-sm`     |

The motivation for this change is just to make the scale more
predictable — it's never been intuitive to me that `shadow` sits in
between `shadow-sm` and `shadow-md`.

This PR doesn't remove the ability to create classes like `shadow` and
`inset-shadow` by adding bare `--shadow` and `--inset-shadow` theme
variables, but does remove them from the default theme.

## Impact

We'll include a codemod for this in our upgrade tool to automate this
change for people upgrading from v3 to v4, but this is still sort of an
annoying breaking change admittedly and will make lots of educational
resources, example components, and LLM tools out of date for v4 😕 At the
same time I don't want to feel like we can never correct regrettable
legacy decisions just to preserve backward compatibility.

We made a similar change like this when we went from the v0.x color
palette to the v1.x color palette changing names like `bg-red` to
`bg-red-500` and that proved to definitely be the right decision long
term, so want to rip the band-aid off here too if we can.

Planning to make the same change for `rounded`, `drop-shadow`, and
`blur` as well — maybe in separate PRs but maybe just all in this one as
well since I don't think we want to do one and not all.

_Update_: I've also made the same changes to the `--radius-*`,
`--drop-shadow-*`, and `--blur-*` scales now, effectively removed the
`rounded`, `drop-shadow`, and `blur` classes by default, and changing
the meaning `rounded-sm`, `drop-shadow-sm`, and `blur-sm`.

We'll put together a codemod to handle this stuff in a separate PR.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-11-04 14:04:03 -05:00
Robin Malfait
894bf9f5ef
Support migrating projects with multiple config files (#14863)
When migrating a project from Tailwind CSS v3 to Tailwind CSS v4, then
we started the migration process in the following order:

1. Migrate the JS/TS config file
2. Migrate the source files (found via the `content` option)
3. Migrate the CSS files

However, if you have a setup where you have multiple CSS root files
(e.g.: `frontend` and `admin` are separated), then that typically means
that you have an `@config` directive in your CSS files. These point to
the Tailwind CSS config file.

This PR changes the migration order to do the following:

1. Build a tree of all the CSS files
2. For each `@config` directive, migrate the JS/TS config file
3. For each JS/TS config file, migrate the source files

If a CSS file does not contain any `@config` directives, then we start
by filling in the `@config` directive with the default Tailwind CSS
config file (if found, or the one passed in). If no default config file
or passed in config file can be found, then we will error out (just like
we do now)

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-04 16:52:11 +00:00
Adam Wathan
1c5bb39d60
Use 0 instead of none in OKLCH values (#14741)
This PR updates all of our OKCLH colors to use `0` instead of `none` due
to weird behavior in Chrome where using `color-mix` with colors using
`none` produces unexpected results:

<img width="1110" alt="image"
src="https://github.com/user-attachments/assets/2272e494-500b-4f75-b5c1-d41c714f0339">

Both `none` and `0` behave as expected in Safari and Firefox so
suspecting this is a bug in Chrome rather than spec'd behavior.

Fixes #14740

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-21 15:54:27 -04:00
Adam Wathan
edb066e0aa
Interpolate gradients using OKLCH by default (#14708)
This PR updates all of our gradient utilities to interpolate using OKLCH
by default instead of sRGB. This results in a smoother transition
between colors that preserves saturation throughout the gradient, rather
than hitting the dreaded dull gray zone in between your color stops.

Here are a few examples comparing sRGB (top) to OKLCH (bottom):

<img width="736" alt="image"
src="https://github.com/user-attachments/assets/57a158b6-a3a2-4eda-813e-1b596c7d4b3a">

We only apply a default interpolation mode when _not_ using arbitrary
values with the gradient utility.

Simplified but clear:

```css
.bg-linear-to-r {
  background-image: linear-gradient(to right in oklch, var(--gradient-color-stops));
}

.bg-linear-[to_right] {
  background-image: linear-gradient(to right, var(--gradient-color-stops));
}

.bg-linear-[to_right_in_hsl] {
  background-image: linear-gradient(to right in hsl, var(--gradient-color-stops));
}
```

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-17 16:16:14 -04:00
Adam Wathan
72c30d422a
Support linear gradient angles as bare values (#14707)
This PR adds support for linear gradient angles as bare values, like
this:

```
bg-linear-45 => linear-gradient(45deg, …)
```

We already support this for [conic
gradients](https://github.com/tailwindlabs/tailwindcss/pull/14467), so
this makes these utilities more consistent.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-17 14:29:32 -04:00
Adam Wathan
2d45f5aa9a
Add first draft of new wide-gamut color palette (#14693)
This PR updates all of the colors in our default theme to use OKLCH
instead of RGB and increases the overall chroma to take advantage of the
wider color gamut.

Just a first draft based on @danhollick's initial work — expecting these
will be further refined before a stable release as we continue to test
them.

<img width="628" alt="image"
src="https://github.com/user-attachments/assets/2de1bfca-fddd-47f9-b609-39f26abdee41">

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-16 15:24:53 -04:00
Adam Wathan
6d43a8be99
Preserve explicit transition duration and timing function when overriding transition property (#14490)
This PR changes the behavior of the `transition-{property}` utilities to
respect any explicit timing function or duration set by the user using
the `ease-*` and `duration-*` utilities.

Say you have this HTML:

```html
<div class="transition-colors duration-500 ease-out lg:transition-all">
```

Currently, the `transition-duration` and `transition-timing-functions`
will be reset to their default values at the `lg` breakpoint even though
you've provided explicit values for them.

After this PR is merged, those values will be preserved at the `lg`
breakpoint.

This PR also adds `duration-initial` and `ease-initial` utilities to
"unset" explicit duration/timing-function values so that the defaults
from classes like `transition-all` will kick in, without having to
specify their explicit values.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-23 10:43:28 -04:00
Adam Wathan
eb3bb4422a
Add shadow-initial and inset-shadow-initial utilities (#14468)
This PR complements #14458 by adding new `shadow-initial` and
`inset-shadow-initial` utilities that make it possible to "undo" a
custom shadow color and revert to the default shadow color for the
current shadow size.

For example, in this example the shadow will be red on hover even though
the default color for the `shadow` utility is `rgb(0 0 0 / 5%)`:

```html
<div class="shadow-sm shadow-red-500 hover:shadow">
  <!-- … -->
</div>
```

This is usually what you want, but if you actually want `hover:shadow`
to apply its default color, you need to know what the color is and add
it yourself:

```html
<div class="shadow-sm shadow-red-500 hover:shadow hover:shadow-black/5">
  <!-- … -->
</div>
```

Using `shadow-initial`, you can achieve the same thing without having to
know what the original color was:

```html
<div class="shadow-sm shadow-red-500 hover:shadow hover:shadow-initial">
  <!-- … -->
</div>
```

The `inset-shadow-initial` utility does the same thing for the
`inset-shadow-*` utilities.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-20 10:36:22 -04:00
Adam Wathan
bf115bf1bd
Add radial and conic gradient utilities (#14467)
This PR adds new `bg-radial-*` and `bg-conic-*` utilities for radial and
conic gradients. It also adds support for "arbitrary gradients", where
gradient utilities like `bg-linear-*`, `bg-radial-*`, and `bg-conic-*`
can now accept a complete gradient definition as their arbitrary value.

## Radial gradients

Radial gradients are created with the `bg-radial` utility, or the
`bg-radial-[…]` utility, combined with the existing `from-*`, `via-*`,
and `to-*` utilities.

The simple `bg-radial` utility just creates a radial gradient with no
position information, which defaults to `center`:

```
radial-gradient({from}, {via}, {to});
```

If you use the arbitrary value format, whatever you provide as the
arbitrary value is inserted into the first position:

```
radial-gradient({arbitrary value}, {from}, {via}, {to});
```

So a utility like `bg-radial-[at_top_left]` would produce this:

```
radial-gradient(at top left, {from}, {via}, {to});
```

This makes it possible to use some of the `radial-gradient(…)` features
that this PR doesn't add first class support for, like using values like
`circle at center` or providing a specific interpolation color space
like `in hsl longer hue`. We may add explicit APIs for these in the
future, but I'm proposing this PR first since those changes would be
purely additive and none of the decisions here would create any conflict
with those APIs.

## Conic gradients

Conic gradients are created with the `bg-conic`,
`bg-conic-{bareNumber}`, and `bg-conic-[…]` utilities, combined with the
existing `from-*`, `via-*`, and `to-*` utilities.

The `bg-conic` utility creates a conic gradient with no angle, which
defaults to `0deg`:

```
conic-gradient({from}, {via}, {to});
```

The `bg-conic-{bareNumber}` utilities create conic gradients using the
bare number as the angle:

```
conic-gradient(from {bareNumber}deg, {from}, {via}, {to});
```

The `bg-conic-[…]` arbitrary value utilities insert whatever you provide
as the arbitrary value into the first position verbatim:

```
conic-gradient({arbitraryValue}, {from}, {via}, {to});
```

So a utility like `bg-conic-[from_45deg_in_hsl]` would produce this:

```
conic-gradient(from 45deg in hsl, {from}, {via}, {to});
```

Note that the `from` keyword needs to be provided by the user when using
arbitrary values, but not when using bare values.

This makes it possible to use some of the `conic-gradient(…)` features
that this PR doesn't add first class support for, like using values like
`at 0 0` or providing a specific interpolation color space like `in hsl
longer hue`. We may add explicit APIs for these in the future, but I'm
proposing this PR first since those changes would be purely additive and
none of the decisions here would create any conflict with those APIs.

## Arbitrary gradients

Prior to this PR, utilities like `bg-linear-[…]` could only accept
positional information as their arbitrary value, like
`bg-linear-[to_top_right]`. All of the color stops could only be
provided using the `from-*`, `via-*`, and `to-*` utilities.

If you wanted to provide the complete gradient in one class, you needed
to use `bg-[…]` and write out the gradient function yourself:


```html
<div class="bg-[linear-gradient(to_right,var(--color-red-500),var(--color-yellow-400))]">
```

This PR refactors some things internally to make it possible to provide
the entire gradient as the arbitrary value to each background gradient
utility, like this:

```html
<div class="bg-linear-[to_right,var(--color-red-500),var(--color-yellow-400)]">
```

This is nice if you're doing something very custom and you want to be
able to look at the whole value together, while still avoiding some of
the boilerplate you'd have if you had to write out the entire gradient
function yourself.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-20 10:34:54 -04:00
Adam Wathan
8d00534823
Preserve shadow color when overriding shadow size (#14458)
This PR changes how shadow color and shadow size utilities interact when
used with variants.

Take this HTML:

```html
<div class="shadow-lg shadow-red-500 hover:shadow-xl">
  <!-- … -->
</div>
```

Currently this shadow would be red by default, but revert to the default
semi-transparent black color on hover.

This PR changes this behavior such that the shadow remains red on hover,
and only the shadow size changes.

We deliberately didn't do this originally because making things behave
this way makes it very difficult to get the default shadow color back
once you've changed it. The default color for `shadow-xl` for instance
is `rgb(0 0 0 / 0.1)`, and the only way to get that color back after
changing it is to know that value and explicitly bring it back:

```html
<div class="shadow-lg shadow-red-500 hover:shadow-xl hover:shadow-black/10">
  <!-- … -->
</div>
```

To make things more difficult, the default shadow color is not the same
across shadow sizes. For `shadow-sm` it's `black/5`, and for
`shadow-2xl` it's `black/25`.

In practice though you basically never need to bring back the default
shadow color, so I'm reconsidering this trade-off in v4, and think I
prefer this new behavior where the color is preserved but you have to
bring back the default color if you actually need it.

A simple workaround if you don't know the color is to reset the
`--tw-shadow-color` variable like this:

```html
<div class="shadow-lg shadow-red-500 hover:shadow-xl hover:[--tw-shadow-color:initial]">
  <!-- … -->
</div>
```

This relies on semi-private internals though, so perhaps we can
introduce a utility for this, like `shadow-default` or `shadow-initial`
that just unsets the shadow color.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-09-19 09:43:48 -04:00
Adam Wathan
a51b63a05d
Don't override explicit leading-*, tracking-*, or font-{weight} utilities with font-size utility defaults (#14403)
This PR improves how the `text-{size}` utilities interact with the
`leading-*`, `tracking-*`, and `font-{weight}` utilities, ensuring that
if the user explicitly uses any of those utilities that those values are
not squashed by any defaults baked into the `text-{size}` utilities.

Prior to this PR, if you wrote something like this:

```html
<div class="text-lg leading-none md:text-2xl">
```

…the `leading-none` class would be overridden by the default line-height
value baked into the `text-2xl` utility at the `md` breakpoint. This has
been a point of confusion and frustration for people [in the
past](https://github.com/tailwindlabs/tailwindcss/issues/6504) who are
annoyed they have to keep repeating their custom `leading-*` value like
this:

```html
<div class="text-lg leading-none md:text-2xl md:leading-none lg:text-4xl lg:leading-none">
```

This PR lets you write this HTML instead but get the same behavior as
above:

```html
<div class="text-lg leading-none md:text-2xl lg:text-4xl">
```

It's important to note that this change _only_ applies to line-height
values set explicitly with a `leading-*` utility, and does not apply to
the line-height modifier.

In this example, the line-height set by `text-sm/6` does _not_ override
the default line-height included in the `md:text-lg` utility:

```html
<div class="text-sm/6 md:text-lg">
```

That means these two code snippets behave differently:

```html
<div class="text-sm/6 md:text-lg">…</div>
<div class="text-sm leading-6 md:text-lg">…</div>
```

In the top one, the line-height `md:text-lg` overrides the line-height
set by `text-sm/6`, but in the bottom one, the explicit `leading-6`
utility takes precedence.

This PR applies the same improvements to `tracking-*` and
`font-{weight}` as well, since all font size utilities can also
optionally specify default `letter-spacing` and `font-weight` values.

We achieve this using new semi-private CSS variables like we do for
things like transforms, shadows, etc., which are set by the `leading-*`,
`tracking-*`, and `font-{weight}` utilities respectively. The
`text-{size}` utilities always use these values first if they are
defined, and the default values become fallbacks for those variables if
they aren't present.

We use `@property` to make sure these variables are reset to `initial`
on a per element basis so that they are never inherited, like with every
other variable we define.

This PR does slightly increase the amount of CSS generated, because now
utilities like `leading-5` look like this:

```diff
  .leading-5 {
+   --tw-leading: 1.25rem;
    line-height: 1.25rem;
  }
```

…and utilites like `text-sm` include a `var(…)` lookup that they didn't
include before:

```diff
  .text-sm {
    font-size: 0.875rem;
-   line-height: var(--font-size-sm--line-height, 1.25rem);
+   line-height: var(--tw-leading, var(--font-size-sm--line-height, 1.25rem));
  }
```

If this extra CSS doesn't feel worth it for the small improvement in
behavior, we may consider just closing this PR and keeping things as
they are.

This PR is also a breaking change for anyone who was depending on the
old behavior, and expected the line-height baked into the `md:text-lg`
class to take precedence over the explicit `leading-6` class:

```html
<div class="text-sm leading-6 md:text-lg">…</div>
```

Personally I am comfortable with this because of the fact that you can
still get the old behavior by preferring a line-height modifier:

```html
<div class="text-sm/6 md:text-lg">…</div>
```

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-09-18 09:20:04 -04: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
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
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
62de02a379
Support combining arbitrary shadows without a color with shadow color utilities (#13876)
* Support combining arbitrary shadows without a color with shadow color utilities

* Update changelog

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-06-21 17:36:59 -04:00
Adam Wathan
818692fe17
Don't accommodate hidden elements in space/divide (#13459)
* Don't accommodate hidden elements in space/divide

* Update tests

* Update changelog

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-04-09 12:59:08 -04:00
Adam Wathan
cc6094e84f
Don't read variables for shadow sizes (#13449)
* Don't read variables for shadow sizes

* Add UI test

* Handle key suffix in get function instead of ThemeKey

* Remove duplicate theme keys

* Format test in a less insane way

* Revert playground changes

* Update changelog

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-04-04 18:01:37 -04:00
Adam Wathan
b5df5e9202
Use variables with fallbacks for utility classes (#13177)
* Use variables with fallbacks for utility classes

* Update playwright test

* rename `resolveBare` to `resolveValue`

* make private methods really private

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-03-21 18:25:43 +01:00
Robin Malfait
1933d8eb76
Fix typos (#13257)
* fix typos

* update test, due to typo in property order list
2024-03-15 18:13:02 +01:00
Robin Malfait
0ee3508179
Move optimizeCss to the packages where it's used (#13230)
* add `@tailwindcss/optimize` as a separate package

* remove lightningcss from `tailwindcss`

* import `optimizeCss` from `@tailwindcss/optimize`

* ensure we use `src/` files in development

* move `devDependencies` after `dependencies`

Just for consistency

* inline `optimizeCss` in leaf packages

Instead of introducing a custom `@tailwindcss/optimize` package

* update changelog

* fix changelog
2024-03-13 17:25:16 +01:00
Robin Malfait
d230f2e13b
Improve incremental builds (#13168)
* ensure we don't crash on deleted files

* change return type of `compile` to include a `rebuild()` function

This will allow us in the future to perform incremental rebuilds after
the initial rebuild. This is purely the API change so that we can
prepare all the call sites to use this new API.

* set `@tailwind utilities` nodes

Instead of replacing the node that represents the `@tailwind utilities`
with the generated AST nodes from the rawCandidates, we will set the
nodes of the `@tailwind utilities` rule to the AST nodes instead.

This way we dont' have to remove and replace the `@tailwind utilities`
rule with `n` new nodes. This will later allow us to track the
`@tailwindcss utilities` rule itself and update its `nodes` for
incremental rebuilds.

This also requires a small change to the printer where we now need to
print the children of the `@tailwind utilities` rule. Note: we keep the
same `depth` as-if the `@tailwindcss utilities` rule was not there.
Otherwise additional indentation would be present.

* move sorting to the `ast.sort()` call

This will allow us to keep sorting AST nodes in a single spot.

* move parser functions to the `DesignSystem`

This allows us to put all the parsers in the `DesignSystem`, this allows
us to scope the parsers to the current design system (the current theme,
current utility values and variants).

The implementation of these parsers are also using a `DefaultMap`
implementation. This allows us to make use of caching and only parse a
candidate, parse a variant or compile AST nodes for a given raw
candidate once if we've already done this work in the past.

Again, this is scoped to the `DesignSystem` itself. This means that if
the corresponding theme changes, then we will create a new
`DesignSystem` entirely and the caches will be garbage collected. This
is important because a candidate like `bg-primary` can be invalid in
`DesignSystem` A, but can be valid in `DesignSystem` B and vice versa.

* ensure we sort variants alphabetically by root

For incremental rebuilds we don't know all the used variants upfront,
which means that we can't sort them upfront either (what we used to do).

This code now allows us to sort the variants deterministically when
sorting the variants themselves instead of relying on the fact that they
used to be sorted before.

The sort itself could change slightly compared to the previous
implementation (especially when you used stacked variants in your
candidates), but it will still be deterministic.

* replace `localeCompare` comparisons

Use cheaper comparisons than `localeCompare` when comparing 2 strings.
We currently don't care if it is 100% correctly sorted, but we just want
consistent sorting. This is currently faster compared to
`localeCompare`.

Another benefit is that `localeCompare` could result in
non-deterministic results if the CSS was generated on 2 separate
computers where the `locale` is different.

We could solve that by adding a dedicated locale, but it would still be
slower compared to this.

* track invalid candidates

When an incoming raw candidates doesn't produce any output, then we can
mark it as an invalid candidate. This will allow us to reduce the amount
of candidates to handle in incremental rebuilds.

* add initial incremental rebuild implementation

This includes a number of steps:

1. Track the `@tailwind utilities` rule, so that we can adjust its nodes
   later without re-parsing the full incoming CSS.
2. Add the new incoming raw candidates to the existing set of
   candidates.
3. Parse the merged set to `compileCandidates` (this can accept any
   `Iterable<string>`, which means `string[]`, `Set<string>`, ...)
4. Get the new AST nodes, update the `@tailwind utilities` rule's nodes
   and re-print the AST to CSS.

* improvement 1: ignore known invalid candidates

This will reduce the amount of candidates to handle. They would
eventually be skipped anyway, but now we don't even have to re-parse
(and hit a cache) at all.

* improvement 2: skip work, when generated AST is the same

Currently incremental rebuilds are additive, which means that we are not
keeping track if we should remove CSS again in development.

We can exploit this information, because now we can quickly check the
amoutn of generated AST nodes.

- If they are the same then nothing new is generated — this means that
  we can re-use the previous compiled CSS. We don't even have to
  re-print the AST because we already did do that work in the past.
- If there are more AST nodes, something new is generated — this means
  that we should update the `@tailwind utilities` rule and re-print the
  CSS. We can store the result for future incremental rebuilds.

* improvement 3: skip work if no new candidates are detected

- We already know a set of candidates from previous runs.
- We also already know a set of candidates that are invalid and don't
  produce anything.

This means that an incremental rebuild could give us a new set of
candidates that either already exist or are invalid.

If nothing changes, then we can re-use the compiled CSS.

This actually happens more often than you think, and the bigger your
project is the better this optimization will be.

For example:

```
// Imagine file A exists:
<div class="flex items-center justify-center"></div>
<button class="text-red-500">Delete</button>
```

```
// Now you add a second file B:
<div class="text-red-500 flex"></div>
```

You just created a brand new file with a bunch of HTML elements and
classes, yet all of the candidates in file B already exist in file A, so
nothing changes to the actual generated CSS.

Now imagine the other hundreds of files that already contain hundreds of
classes.

The beauty of this optimization is two-fold:

- On small projects, compiling is very fast even without this check.
  This means it is performant.
- On bigger projects, we will be able to re-use existing candidates.
  This means it stays performant.

* remove `getAstNodeSize`

We can move this up the tree and move it to the `rebuild` function
instead.

* remove invalid candidate tracking from `DesignSystem`

This isn't used anywhere but only in the `rebuild` of the compile step.
This allows us to remove it entirely from core logic, and move it up the
chain where it is needed.

* replace `throwOnInvalidCandidate` with `onInvalidCanidate`

This was only needed for working with `@apply`, now this logic _only_
exists in the code path where we are handling `@apply`.

* update `compile` API signature

* update callsite of `compile()` function

* fix typo
2024-03-11 14:24:49 -04:00
Adam Wathan
d863de7abf
Prevent content-none from being overridden when conditionally styling ::before/::after (#13187)
* Prevent content-none from being overridden when conditionally styling before/after

* Update changelog

* Fix changelog entry

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-03-11 14:09:50 -04:00
Adam Wathan
5c5037d9b5
Ensure scale utilities support percentages (#13182)
* Ensure scale utilities support percentages

* Update changelog

* Fix changelog entry

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: wongjn <11310624+wongjn@users.noreply.github.com>
2024-03-11 13:14:10 -04:00
Adam Wathan
fcaff7c613
[v4] Only set border-style for appropriate border side (#13124)
* Only set border-style for appropriate border side

Fixes #13121.

Something to consider is that this implementation will still have issues if we ever implement support for utilities like `border-r-dashed`. 6am and I just woke up and am going to the gym now but leaving this as a note to consider when I get back before merging :)

* Fix typos in test names

* Revert playground change

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-03-07 09:11:28 -05:00
Robin Malfait
a68de1df27
introduce v4 codebase 2024-03-05 14:29:15 +01:00