Skip to content

Support prefixed custom CSS imports#20274

Open
saadeghi wants to merge 5 commits into
tailwindlabs:mainfrom
saadeghi:feat/import-prefix
Open

Support prefixed custom CSS imports#20274
saadeghi wants to merge 5 commits into
tailwindlabs:mainfrom
saadeghi:feat/import-prefix

Conversation

@saadeghi

@saadeghi saadeghi commented Jun 24, 2026

Copy link
Copy Markdown

Summary

This PR makes prefix(...) work for custom CSS imports that define @utility rules.

Why?

Developing libraries on top of Tailwind CSS may requires the users to use prefixes to avoid conflicts.
If 2 different imported libraries have the same class name, it should be possible to prefix one of them to avoid conflict.

@import "tailwindcss";
@import "./lib1";
@import "./lib2" prefix(icons);

in above code, if lib1 and lib2 have same utility class names, and both are 3rd party libraries, we need a way to avoid this conflict.

Example:

For example, a custom import like:

@import "./components.css" prefix(ui);

where components.css contains:

@utility card {
  color: red;
}

can now generate the utility using the prefixed candidate:

<div class="ui:card"></div>

The prefix is scoped to utilities imported from that file, so it does not prefix core utilities or expose unrelated utilities that happen to use the same internal name prefix.

This also preserves the existing behavior where @import "tailwindcss" prefix(tw); configures the global Tailwind prefix.

Test plan

All tests passing

pnpm test

Checked formatting:

node node_modules/prettier/bin/prettier.cjs --check packages/tailwindcss/src/index.ts packages/tailwindcss/src/prefix.test.ts

Example:

Imported file:

button.css

@utility button {
  background-color: blue;
  color: white;
  padding: 0.5rem 1rem;
}

Before this PR:

style.css

@import "tailwindcss";
@import "./button";

Result:
button

style.css

@import "tailwindcss" prefix(tw);
@import "./button";

Result:
tw:button

style.css

@import "tailwindcss" prefix(tw);
@import "./button" prefix(ui);

Result:
tw:button

After this PR:

style.css

@import "tailwindcss";
@import "./button";

✅ Same result:
button

style.css

@import "tailwindcss" prefix(tw);
@import "./button";

✅ Same result:
tw:button

style.css

@import "tailwindcss" prefix(tw);
@import "./button" prefix(ui);

🆕 Result:
Now the custom imported file can have its own prefix.
ui:button

I assume this won't be a breaking change since it does not change existing behavior, and @import "./button" prefix(ui); was not doing anything and was never documented.

Please let me know if anything needs to be changed.

Allow `prefix(...)` on imported custom CSS files to scope `@utility` definitions from that import.

Custom utilities from prefixed imports now use the public `prefix:utility` candidate format without making that prefix global for core utilities. Imported utility roots are tracked per prefix to avoid exposing unrelated utilities that happen to share the same internal name prefix.

Adds coverage for:
- custom imports inheriting the Tailwind import prefix
- custom imports using their own prefix
- prefixed custom imports not prefixing core utilities
- avoiding collisions with unrelated `prefix-*` utilities
@saadeghi saadeghi requested a review from a team as a code owner June 24, 2026 19:13
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f75b1d25-5045-4ec2-832d-80694454ed16

📥 Commits

Reviewing files that changed from the base of the PR and between b192f17 and e13d610.

📒 Files selected for processing (1)
  • packages/tailwindcss/src/prefix.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/tailwindcss/src/prefix.test.ts

Walkthrough

The changes add import-prefix tracking for utilities, record imported utility roots during AST walking, and use that information to remap and filter parsed candidates. @media prefix(...) handling now validates prefixes, sets theme.prefix only when the block contains @tailwind utilities, and propagates importPrefix context. The tests add cases for prefixed and unprefixed Tailwind imports with imported custom utilities.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding prefix support for custom CSS imports.
Description check ✅ Passed The description matches the changeset and explains the new prefixed custom import behavior and examples.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/tailwindcss/src/index.ts`:
- Around line 636-639: The imported-utility candidate override in
designSystem.parseCandidate still lets the internal hyphenated form bypass the
prefix remap and reach parseCandidate(candidate), so ui-card or tw:ui-card can
resolve under the internal name. Update the fallback path in the parseCandidate
wrapper to filter out imported utility roots that were registered via
importedUtilityRootsByPrefix, and ensure the same check is applied in the later
matching branch near the second parseCandidate handling so only the public
prefix form is accepted.
- Around line 536-538: The nested `@theme` handling in index.ts is incorrectly
propagating prefix(...) from imported custom theme blocks into the global
theme.prefix, which can make core utilities unexpectedly require a custom
prefix. Update the logic around the `@theme` and `@utility` walk/processing so
prefix propagation only happens for imports that actually contain Tailwind
utilities (for example, guarded by containsTailwindUtilities), and ensure the
later `@theme` handler only sets theme.prefix for those Tailwind utility imports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d21dc37a-cb76-4ff5-85b7-be1f4ba2cbe9

📥 Commits

Reviewing files that changed from the base of the PR and between d5ca0ae and c4fe362.

📒 Files selected for processing (2)
  • packages/tailwindcss/src/index.ts
  • packages/tailwindcss/src/prefix.test.ts

Comment thread packages/tailwindcss/src/index.ts
Comment thread packages/tailwindcss/src/index.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge. The change is well-scoped, the previously flagged variant-shadowing path now has a correct fallback and a dedicated test, and all standard prefix scenarios are covered by the new test suite.

The core translation logic (external prefix:name → internal prefix-name) is correct throughout. The parseCandidate wrapper correctly falls back when the import-prefix route produces no results, preventing breakpoint names used as prefixes from silently swallowing responsive utilities. The only finding is a minor inefficiency where createCssUtility is called once for the original utility name and then immediately discarded when an import prefix is active — this has no impact on correctness or output.

No files require special attention. The logic in index.ts around parseCandidateWithoutImportedRoots and the importedUtilityRootsByPrefix map is the most complex new path, but it is well-exercised by the new tests.

Reviews (2): Last reviewed commit: "add test: variant order for prefixed imp..." | Re-trigger Greptile

Comment thread packages/tailwindcss/src/index.ts
Comment thread packages/tailwindcss/src/index.ts Outdated
Comment thread packages/tailwindcss/src/prefix.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant