Skip to content

fix(react-email): preserve source order when inlining duplicate Tailwind class rules - #3688

Draft
bukinoshita wants to merge 2 commits into
canaryfrom
fix/tailwind-inliner-rule-order-3dbe
Draft

fix(react-email): preserve source order when inlining duplicate Tailwind class rules#3688
bukinoshita wants to merge 2 commits into
canaryfrom
fix/tailwind-inliner-rule-order-3dbe

Conversation

@bukinoshita

@bukinoshita bukinoshita commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary by cubic

  • Bug Fixes
    • Track a global source-order index per rule in extractRulesPerClass() and sort with sort-rules-by-order before inlining or emitting non-inlinable rules.
    • Apply sorting in inlineStyles(), <Tailwind> (non-inlinable <style>), and the clone path.
    • Add a regression test for interleaved duplicate classes; snapshots only change declaration order.
  • Refactors
    • Add resolve-variable-functions to inline var() from local declarations or custom property initial values.
    • Supports nested var() with a cycle guard; resolution no longer depends on processing order.

Written for commit b924d99. Summary will update on new commits.

Review in cubic

cursoragent and others added 2 commits July 29, 2026 16:34
…r-independent

Resolve var() references per declaration against cloned variable values with
recursive nested resolution, instead of relying on shared in-place node
mutation whose result depended on the order rules were processed.

Co-authored-by: Bu Kinoshita <bukinoshita@users.noreply.github.com>
…ind class rules

extractRulesPerClass groups rules per class, so flattening the grouped map
values dropped the original stylesheet order across classes. With interleaved
duplicate class definitions (e.g. .box, .other, .box) that conflict on the same
property, this let an unrelated class clobber a later override. Rules now carry
a global source-order index and every consumer sorts by it before generating
styles, restoring correct cascade precedence for inlined styles and emitted
non-inlinable <style> rules.

Co-authored-by: Bu Kinoshita <bukinoshita@users.noreply.github.com>
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-email Ready Ready Preview, Comment Jul 29, 2026 4:37pm
react-email-demo Ready Ready Preview, Comment Jul 29, 2026 4:37pm

@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b924d99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
react-email Patch
@react-email/editor Patch
@react-email/ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/react-email@3688

commit: b924d99

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 14 files

Confidence score: 3/5

  • In packages/react-email/src/components/tailwind/utils/css/resolve-variable-functions.ts, cyclic local variable resolution can strip the caller’s fallback (e.g., var(--loop, red)), which may produce incorrect final CSS values instead of safe defaults; preserve the outer var() when recursion hits seen (or propagate the failure) so fallback behavior remains intact.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/react-email/src/components/tailwind/utils/css/resolve-variable-functions.ts">

<violation number="1" location="packages/react-email/src/components/tailwind/utils/css/resolve-variable-functions.ts:55">
P2: Cyclic local variables lose the caller’s `var()` fallback. Avoid replacing an outer reference when its recursive expansion hits `seen` (or propagate that failure), so `var(--loop, red)` remains available after custom-property declarations are removed.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

new Set([...seen, variableName]),
);

funcParentListItem.data = unwrapValue(clonedValue);

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.

P2: Cyclic local variables lose the caller’s var() fallback. Avoid replacing an outer reference when its recursive expansion hits seen (or propagate that failure), so var(--loop, red) remains available after custom-property declarations are removed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-email/src/components/tailwind/utils/css/resolve-variable-functions.ts, line 55:

<comment>Cyclic local variables lose the caller’s `var()` fallback. Avoid replacing an outer reference when its recursive expansion hits `seen` (or propagate that failure), so `var(--loop, red)` remains available after custom-property declarations are removed.</comment>

<file context>
@@ -0,0 +1,58 @@
+        new Set([...seen, variableName]),
+      );
+
+      funcParentListItem.data = unwrapValue(clonedValue);
+    },
+  });
</file context>

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.

2 participants