refactor(web): bring HeroUI usage in line with v3 (variant codemod + small fixes)#9
Conversation
- 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
There was a problem hiding this comment.
💡 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".
| /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'`, |
There was a problem hiding this comment.
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 (flat → tertiary), 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 👍 / 👎.
Summary
@heroui-pro/react1.0.0-beta.1 → 1.0.0-beta.2 and tighten@heroui/react/@heroui/stylesranges from>=3.0.0to^3.0.3.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 defaultprimaryvariant on every Button, losing all visual hierarchy across the app.colorprop from any Button that already carries a variant — v3 Button has nocolorprop at all.color='danger' variant='flat'→variant='danger-soft'andcolor='danger' variant='solid'→variant='danger'.radius='full'props (v3 droppedradiusfrom Button / Tabs / Chip / Sidebar.Trigger). Replaced with Tailwindrounded-fullonclassName.<Button isLoading>→isPendinginSubscriptionPurchaseModal(Stripe / Creem / Epay pay buttons). Without the rename the Stripe and Creem buttons stayed clickable while a payment was already in flight.web/scripts/heroui-v3-variant-codemod.mjs, an idempotent codemod so future regressions can be cleaned up by re-runningbun scripts/heroui-v3-variant-codemod.mjs.After this PR
rg "variant=['\"](solid|bordered|flat|light|faded|shadow)['\"]" web/src/andrg "radius=" web/src/both come up empty.Out of scope (tracked in
web/TODO.mdfor follow-up PRs)HeroCompatwrapper (ToolPriceSettings,TieredPricingEditor~1700 lines,DynamicPricingBreakdown).<Button color='success' / 'warning'>sites that need a hand-tuned Tailwind override (the codemod prints them all).startContent/endContent→childrenon Button & Chip (~50 files; needs human review forgap-*).classNames={...}left inauth/AuthLayout.jsx.Test plan
bun run devand 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).bun scripts/heroui-v3-variant-codemod.mjson a clean tree → reports 0 edits.This PR was generated automatically by Cursor.
Made with Cursor