Adam Wathan e000caa0bd
Add support for inline option when defining @theme values (#14095)
This PR adds support for a new `inline` option when defining a `@theme`
block that tells Tailwind to use raw theme values for utilities instead
of referencing the corresponding generated CSS variable.

```css
/* Input */
@theme inline {
  --color-red-500: #ef4444;
  /* ... */
}


/* Example output */
:root {
  --color-red-500: #ef4444;
}

.text-red-500 {
  color: #ef4444;
}
```

This can be composed with the existing `reference` option in case you
want to define a `@theme` block as both `reference` (so the variables
aren't generated) and `inline`:

```css
/* Input */
@theme inline reference {
  --color-red-500: #ef4444;
  /* ... */
}


/* Example output */
.text-red-500 {
  color: #ef4444;
}
```

Since you can have multiple `@theme` blocks, you can even define some
values normally and some as inline based on how you're using them. For
example you might want to use `inline` for defining literal tokens like
`--color-red-500`, but include the variable for tokens that you want to
be able to theme like `--color-primary`:

```css
/* Input */
@theme inline {
  --color-red-500: #ef4444;
  /* ... */
}

@theme {
  --color-primary: var(--color-red-500);
}


/* Example output */
:root {
  --color-red-500: #ef4444;
  --color-primary: var(--color-red-500);
}

.text-red-500 {
  color: #ef4444;
}

.text-primary {
  color: var(--color-primary, var(--color-red-500));
}
```

## Breaking changes

Prior to this PR, you could `@import` a stylesheet that contained
`@theme` blocks as reference by adding the `reference` keyword to your
import:

```css
@import "./my-theme.css" reference;
```

Now that `reference` isn't the only possible option when declaring your
`@theme`, this syntax has changed to a new `theme(…)` function that
accepts `reference` and `inline` as potential space-separated values:

```css
@import "./my-theme.css";
@import "./my-theme.css" theme(reference);
@import "./my-theme.css" theme(inline);
@import "./my-theme.css" theme(reference inline);
```

If you are using the `@import … reference` option with an earlier alpha
release, you'll need to update your code to `@import … theme(reference)`
once this PR lands in a release.

## Motivation

This PR is designed to solve an issue pointed out in #14091.

Prior to this PR, generated utilities would always reference variables
directly, with the raw value as a fallback:

```css
/* Input */
@theme {
  --color-red-500: #ef4444;
  /* ... */
}


/* Example output */
:root {
  --color-red-500: #ef4444;
}

.text-red-500 {
  color: var(--color-red-500, #ef4444);
}
```

But this can create issues with variables resolving to an unexpected
value when a theme value is referencing another variable defined on
`:root`.

For example, say you have a CSS file like this:

```css
:root, .light {
  --text-fg: #000; 
}

.dark {
  --text-fg: #fff;
}

@theme {
  --color-fg: var(--text-fg);
}
```

Without `@theme inline`, we'd generate this output if you used the
`text-fg` utility:

```css
:root, .light {
  --text-fg: #000; 
}

.dark {
  --text-fg: #fff;
}

:root {
  --color-fg: var(--text-fg);
}

.text-fg {
  color: var(--color-fg, var(--text-fg));
}
```

Now if you wrote this HTML, you're probably expecting your text to be
the dark mode color:

```html
<div class="dark">
  <h1 class="text-fg">Hello world</h1>
</div>
```

But you'd actually get the light mode color because of this rule:

```css
:root {
  --color-fg: var(--text-fg);
}

.text-fg {
  color: var(--color-fg, var(--text-fg));
}
```

The browser will try to resolve the `--color-fg` variable, which is
defined on `:root`. When it tries to resolve the value, _it uses the
value of `var(--text-fg)` as it would resolve at `:root`_, not what it
would resolve to based on the element that has the `text-fg` class.

So `var(--color-fg)` resolves to `#000` because `var(--text-fg)`
resolved to `#000` at the point in the tree where the browser resolved
the value of `var(--color-fg)`.

By using `@theme inline`, the `.text-fg` class looks like this:

```css
.text-fg {
  color: var(--text-fg);
}
```

With this definition, the browser doesn't try to resolve `--color-fg` at
all and instead resolves `--text-fg` directly which correctly resolves
to `#fff` as expected.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-08-02 09:37:30 -04:00
..