Skip to content

refactor(web): bring HeroUI usage in line with v3 (variant codemod + small fixes)#9

Merged
dingyi merged 5 commits into
mainfrom
feature/heroui-v3-cleanup
Apr 27, 2026
Merged

refactor(web): bring HeroUI usage in line with v3 (variant codemod + small fixes)#9
dingyi merged 5 commits into
mainfrom
feature/heroui-v3-cleanup

Conversation

@dingyi

@dingyi dingyi commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Upgrade @heroui-pro/react 1.0.0-beta.1 → 1.0.0-beta.2 and tighten @heroui/react / @heroui/styles ranges from >=3.0.0 to ^3.0.3.
  • Auto-rewrite ~325 v2 visual variants (solid|bordered|flat|light|faded|shadow) across 102 component files into v3 semantic variants (primary|secondary|tertiary|danger|danger-soft|ghost|outline). Without this remap HeroUI silently fell back to the default primary variant on every Button, losing all visual hierarchy across the app.
  • Drop the redundant color prop from any Button that already carries a variant — v3 Button has no color prop at all.
  • Collapse color='danger' variant='flat'variant='danger-soft' and color='danger' variant='solid'variant='danger'.
  • Remove all radius='full' props (v3 dropped radius from Button / Tabs / Chip / Sidebar.Trigger). Replaced with Tailwind rounded-full on className.
  • Rename 3 × <Button isLoading>isPending in SubscriptionPurchaseModal (Stripe / Creem / Epay pay buttons). Without the rename the Stripe and Creem buttons stayed clickable while a payment was already in flight.
  • Add web/scripts/heroui-v3-variant-codemod.mjs, an idempotent codemod so future regressions can be cleaned up by re-running bun scripts/heroui-v3-variant-codemod.mjs.

After this PR rg "variant=['\"](solid|bordered|flat|light|faded|shadow)['\"]" web/src/ and rg "radius=" web/src/ both come up empty.

Out of scope (tracked in web/TODO.md for follow-up PRs)

  • Migrate the last 3 files still importing the Semi-style HeroCompat wrapper (ToolPriceSettings, TieredPricingEditor ~1700 lines, DynamicPricingBreakdown).
  • 8 <Button color='success' / 'warning'> sites that need a hand-tuned Tailwind override (the codemod prints them all).
  • startContent / endContentchildren on Button & Chip (~50 files; needs human review for gap-*).
  • Single classNames={...} left in auth/AuthLayout.jsx.

Test plan

  • bun run dev and click through /console (Channels, Tokens, Users, Logs, Pricing, Subscriptions, Settings) — verify Button hierarchy reads right (primary CTA stands out, secondary/tertiary recede, danger looks danger-soft).
  • Mobile drawer toggle and console PageLayout sidebar trigger stay round.
  • HeaderLogo "自用模式" / "演示站点" Chip stays pill-shaped.
  • Subscription purchase modal — Stripe and Creem buttons disable while paying.
  • Re-run bun scripts/heroui-v3-variant-codemod.mjs on a clean tree → reports 0 edits.

This PR was generated automatically by Cursor.

Made with Cursor

dingyi added 5 commits April 27, 2026 23:21
- Bump @heroui-pro/react from 1.0.0-beta.1 to 1.0.0-beta.2 (latest beta on npm).
- Tighten @heroui/react and @heroui/styles ranges from >=3.0.0 to ^3.0.3
  so future v4 cannot silently slip in via npm install.
- Add web/TODO.md tracking the remaining HeroUI v3 compliance work
  (HeroCompat removal, variant codemod, isLoading/radius cleanup).

Made-with: Cursor
HeroUI v3 Button renamed the v2 isLoading prop to isPending.
Without the rename the prop is silently ignored — buttons stayed
clickable while a payment was already in flight.

Updates the 3 Stripe / Creem / Epay pay buttons in SubscriptionPurchaseModal.

Made-with: Cursor
HeroUI v3 removed the radius prop from Button, Tabs, Chip; when passed it
is silently ignored, leaving the components with their default border
radius instead of the rounded pill we wanted.

- MobileMenuButton, ErrorBoundary refresh, Home copy/CTA buttons
  → rounded-full on className.
- HeaderLogo self-use/demo Chip → rounded-full (overrides Chip's default
  rounded-2xl).
- NoticeModal Tabs → drop radius (Tabs has no radius prop in v3); the
  pill look will be picked up via the upcoming variant cleanup.

Sidebar.Trigger in PageLayout has the same issue but is intentionally
deferred so it can be cleaned up alongside its variant='light' migration.

Made-with: Cursor
HeroUI v3 dropped Button's color prop and renamed every visual variant
(solid|bordered|flat|light|faded|shadow) to a semantic one. Without the
remap the props were silently ignored and ~100 buttons across the app
fell back to the default 'primary' variant — losing every visual cue
about hierarchy (secondary, dismiss, danger, …).

This is an automated rewrite via web/scripts/heroui-v3-variant-codemod.mjs:

  solid    -> primary
  bordered -> secondary
  flat     -> tertiary
  light    -> tertiary
  faded    -> secondary
  shadow   -> primary

Plus the danger combos:

  color='danger' variant='flat'  -> variant='danger-soft'   (drops color)
  color='danger' variant='solid' -> variant='danger'        (drops color)

And `color='primary'|'secondary'|'default'` are removed from any Button
that already carries a variant — v3 Button has no color prop, so it was
just dead weight.

`color='success'|'warning'` are intentionally left in place; v3 has no
matching built-in Button variant, so a Tailwind override needs to be
chosen by hand. The codemod prints the call sites so they can be
hand-tuned separately:
  - settings/personal/cards/CheckinCalendar  ('补签 / Gift' button)
  - table/channels/modals/ModelTestModal     ('测试中' indicator)
  - table/channels/modals/MultiKeyManageModal('警告' badge)
  - table/tokens/index                       ('警告' label)
  - table/users/UsersColumnDefs              ('风控' label)
  - table/users/modals/UserSubscriptionsModal('警告')
  - Setting/Operation/SettingsChannelAffinity('警告')
  - Setting/Performance/SettingsPerformance  ('清理缓存')

Also fixes the lone surviving `<Chip variant='flat'>` inside the Semi
compatibility shim (components/ui/semi.js) since v3 Chip uses 'tertiary'
in place of v2's 'flat'.

102 component files updated, ~325 prop edits.

Made-with: Cursor
Phase 0–3 are complete (deps upgrade, isLoading rename, radius removal,
variant codemod). Phase 1 (HeroCompat migration), 4 (success/warning
Buttons), 5 (startContent/endContent), 6 (classNames) tracked for
follow-up PRs.

Made-with: Cursor
@dingyi
dingyi merged commit a6fa2e6 into main Apr 27, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5bb02a37d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +83 to +88
/color=(['"])danger\1\s+variant=(['"])flat\2/g,
`variant='danger-soft'`,
);
src = src.replace(
/variant=(['"])flat\1\s+color=(['"])danger\2/g,
`variant='danger-soft'`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match danger buttons when attrs are not adjacent

The danger-collapse pass only matches color='danger' when it sits immediately next to variant='flat'/'solid', so tags with intervening props are missed. Those missed cases still get the later global rewrite (flattertiary), leaving combinations like color='danger' variant='tertiary'; in v3 Button, color is ignored, so destructive actions render as neutral tertiary instead of danger styling. Please broaden this pass to detect both attributes anywhere within the same <Button ...> tag before remapping variants.

Useful? React with 👍 / 👎.

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