Skip to content

Conversation

@Yeom-JinHo
Copy link

Description

This PR focuses on fixing incorrect arbitrary utility conversions introduced during the Tailwind v4 migration.
Some utilities using CSS variables like gap-[var(--gap)] were mistakenly transformed into gap-(--gap) by the upgrade CLI, causing runtime and style mismatches.
This update restores proper handling for CSS variable–based arbitrary values to ensure accurate conversion and consistent styling.


Changes

  • Early Guard for Complex Arbitrary Values

    • In arbitraryUtilities, when the candidate is functional + arbitrary, conversion is skipped if the raw value contains var(), calc(), spaces, commas, or slashes.
    • Prevents invalid rewrites such as gap-[var(--gap)]gap-(--gap).
  • String Normalization

    • Normalize the raw value to valueStr = String(value) for consistent parsing and formatting.
  • Complex Value Handling

    • If isComplex is true, do not attempt bare-value replacements; only emit the bracketed arbitrary form (${root}-[${valueStr}]).
  • Modifier Serialization

    • Use printModifier() when emitting classes with modifiers to ensure correct formatting.
  • Storage/Signature Access (No Behavior Change)

    • Read precomputed utilities and signatures via designSystem.storage[PRE_COMPUTED_UTILITIES_KEY | UTILITY_SIGNATURE_KEY].get(options.signatureOptions).
    • Read spacing map via designSystem.storage[SPACING_KEY].

Motivation

The Tailwind v4 migration tool (@tailwindcss/upgrade) introduced incorrect transformations for arbitrary utilities using CSS variables, leading to broken layouts.
This fix ensures the migration process safely ignores complex or variable-based values and preserves valid classnames for reliable build output.


Breaking Changes

None.
This patch only restores correct handling for arbitrary utility parsing during the migration process.


Before/After

Before
gap-[var(--gap)]gap-(--gap) (invalid, broken animation/layout)

After
gap-[var(--gap)] remains intact and functional across builds

@Yeom-JinHo Yeom-JinHo requested a review from a team as a code owner October 23, 2025 14:51
@RobinMalfait RobinMalfait self-assigned this Oct 23, 2025
@RobinMalfait
Copy link
Member

Hey! Can you share an example on how gap-(--gap) resulted in invalid / broken code?

gap-(--gap) is a shorthand for gap-[var(--gap)] and compiles to the same output:

.gap-\(--gap\) {
  gap: var(--gap);          /* This is exactly the same as */
}
.gap-\[var\(--gap\)\] {
  gap: var(--gap);          /* this value here */
}

So the only way I can see it break your code is if you have custom CSS that requires this exact selector .gap-\[var\(--gap\)\]. This is one of the trade-offs where any upgrade can result in a breaking change if you rely on the "internal" selectors.

Since it compiles to the same declarations, it's not something that should be fixed in Tailwind CSS, but instead I would recommend that you either don't rely on the exact class selectors directly in your own CSS, or that you revert these specific upgrades.

Not going to close the PR yet, but it doesn't look like a Tailwind CSS bug and looks like something you are relying on in your own code. I am interested in the actual bug you are seeing, so if you can share some more information that would be awesome!

@Yeom-JinHo
Copy link
Author

@RobinMalfait
Hi! Thank you for the detailed explanation.

The issue occurred on this page: https://www.ui-layouts.com/components/marquee

The animation isn’t working there.

It happened after the commit chore: migrate all components to Tailwind CSS v4,
which was done through the CLI, so I initially thought it was a Tailwind-related issue.

Based on your explanation, it now seems like a scope-related problem.
I haven’t found a fix yet, but I’ll keep investigating.

You can go ahead and close this PR — thank you for your time and clarification! 🙏

cc: @naymurdev

@Yeom-JinHo
Copy link
Author

Additionally, you can check the migrated v4 CSS file at the link below!
https://github.com/ui-layouts/uilayouts/blob/main/apps/ui-layout/app/globals.css

@wongjn
Copy link
Collaborator

wongjn commented Oct 24, 2025

@Yeom-JinHo For your custom animation classes, you're relying on a CSS variable that don't exist on :root, i.e: marquee var(--duration) linear infinite. You should use @theme inline for these.

@wongjn wongjn closed this Oct 24, 2025
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.

3 participants