Add @reference "…" (#15565)

This PR adds a new `@reference "…"` API as an replacement for the
previously added [`@import "…"
reference`](https://github.com/tailwindlabs/tailwindcss/pull/15228). The
motivation for a distinct at rule is that `@import` is already handled
outside of Tailwind in some scenarios (e.g. when using in combination
with postcss-import, other pre-processors, or frameworks like Svelte).
While our implementation of hijacking the `media` attribute _works in
this cases_, it can cause annoying linter issues because tooling build
around `@import` does not know about our behavior.

To fix this, we've decided to move this mode into a separate at rule
that is passed-through in the other tooling. Here's an example of how
this would look like in Svelte:

```svelte
<h1>Hello world!</h1>

<style>
  @reference './theme.css';
  h1 {
    color: var(--theme-color);
  }
</style>
```

With this change, the Svelte linter would not be detecting unused CSS
from the `theme.css` file as it would if we'd rely on `@import`.
This commit is contained in:
Philipp Spiess 2025-01-07 16:41:43 +01:00 committed by GitHub
parent 02cfc45057
commit d6c4e72351
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 1 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add `@tailwindcss/browser` package to run Tailwind CSS in the browser ([#15558](https://github.com/tailwindlabs/tailwindcss/pull/15558))
- Add `@reference "…"` API as a replacement for the previous `@import "…" reference` option ([#15565](https://github.com/tailwindlabs/tailwindcss/pull/15565))
### Fixed

View File

@ -553,3 +553,36 @@ test('it crashes when inside a cycle', async () => {
`[Error: Exceeded maximum recursion depth while resolving \`foo.css\` in \`/root\`)]`,
)
})
test('resolves @reference as `@import "…" reference`', async () => {
let loadStylesheet = async (id: string, base: string) => {
expect(base).toBe('/root')
expect(id).toBe('./foo/bar.css')
return {
content: css`
@theme {
--color-red-500: red;
}
.foo {
color: red;
}
`,
base: '/root/foo',
}
}
await expect(
run(
css`
@reference './foo/bar.css';
@tailwind utilities;
`,
{ loadStylesheet, candidates: ['text-red-500'] },
),
).resolves.toMatchInlineSnapshot(`
".text-red-500 {
color: var(--color-red-500);
}
"
`)
})

View File

@ -15,9 +15,12 @@ export async function substituteAtImports(
let promises: Promise<void>[] = []
walk(ast, (node, { replaceWith }) => {
if (node.kind === 'at-rule' && node.name === '@import') {
if (node.kind === 'at-rule' && (node.name === '@import' || node.name === '@reference')) {
let parsed = parseImportParams(ValueParser.parse(node.params))
if (parsed === null) return
if (node.name === '@reference') {
parsed.media = 'reference'
}
features |= Features.AtImport