Migrate from Mantine to Tailwind CSS v4 + shadcn/ui#730
Migrate from Mantine to Tailwind CSS v4 + shadcn/ui#730
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
dac8adf to
1b0a9b3
Compare
…obile-optimization
…/feat/mobile-optimization
…yEngine/feat/mobile-optimization
…m PolicyEngine/feat/mobile-optimization
Resolve 53 ESLint errors (unused imports, duplicate imports, missing button type attributes, a11y keyboard handlers) and 3 stylelint hex color warnings across 37 files. Run Prettier on all changed files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI was failing because the lockfile format changed between bun 1.2.0 and 1.2.21 (configVersion field removed). Update packageManager to match the version used to generate the lockfile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unsupported `variant` prop from Container/Title components
- Change "flex-start" to "start" for Stack/Group align prop
- Replace pixel string gap values with token keys (sm/md/lg)
- Replace `h` prop with style={{ height }} on Stack
- Remove `size` prop from Progress component
- Resolve duplicate Label identifier (Recharts vs shadcn)
- Fix step prop type on VariableInput
- Export SheetPortal/SheetOverlay from sheet.tsx
- Fix USPlaceSelector type mismatch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace @mantine/dates (YearPickerInput, DatePickerInput) with native HTML inputs in 3 value setter components - Remove MantineProvider from WebsiteApp, CalculatorApp, test fixtures, test utils, and Storybook config - Delete Mantine theme files (theme.ts, styles/colors.ts, styles/components.ts) - Remove @mantine/core, @mantine/dates, @mantine/hooks, eslint-config-mantine, postcss-preset-mantine from package.json - Inline ESLint rules previously provided by eslint-config-mantine - Update postcss config to remove mantine-breakpoint variables Zero @Mantine imports remain. All tests pass (2999), typecheck clean, build succeeds, lint clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code simplification from code-simplifier review: - Fix nested ternary in MultiButtonFooter - Simplify redundant style declarations in SidebarNavItem - Convert arrow function components to function declarations - Remove unnecessary fragment wrappers - Simplify PathwayView containerContent variable - Simplify IngredientSubmissionView badge rendering - Clean up HomeHeader and HeaderBar inline styles - Add displayName to React.memo components - Format with Prettier Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use const with ternary and explicit string type annotation to avoid TypeScript literal type narrowing conflict. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tailwind v4 with prefix(tw) requires the prefix before the variant (tw:lg:flex), not after (lg:tw:flex). The wrong ordering caused all responsive breakpoints and state variants to silently fail, breaking the header nav links, blog grid layout, footer layout, and hover effects across the site. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shadcn Button default variant uses bg-primary which requires a base --color-primary CSS variable. Added explicit teal styling to the footer subscribe button to match the production appearance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
We should talk about this PR. I don't fully understand the impetus for these changes, especially since Tailwind exhibits real drawbacks against Meantime and other front-end frameworks, which is why we ranked it low in our framework evaluation exercise last year. Also, this would introduce significant merge conflicts for important projects. |
|
Thanks for the feedback Anthony. I'd like to understand the concerns better so we can have a productive discussion. On the framework evaluationYou mentioned that Tailwind "ranked low in our framework evaluation exercise last year." I searched for this evaluation across GitHub issues/PRs (both app and app-v2), Slack (#app-redesign, #app-v2, #frontend, #design, #tech), Gmail, Granola meeting notes, and Google Drive — and couldn't find a written record of it. The closest I found was:
Could you share more details on this evaluation — when it happened, who participated, and where the results are documented? I want to make sure I'm not missing something. On Tailwind's drawbacksWhat specific drawbacks are you concerned about? The common ones I'm aware of:
Happy to discuss tradeoffs, but worth noting the popularity gap is significant: Tailwind has ~48M weekly npm downloads vs Mantine's ~1.4M (~35x), and 93k vs 31k GitHub stars. shadcn/ui (Tailwind's component layer) has 84k stars despite being much newer. This matters for ecosystem support, community resources, and AI code generation quality. On merge conflictsThis is a fair concern. This PR was specifically scoped to avoid conflict with your open PRs — it only touches static/presentational pages (home, blog, footer, header) and shared components. The migration plan (issue #729) is phased to defer areas that overlap with #556 until it merges. That said, a full migration would eventually touch your PRs. Here's the scope I've estimated:
The plan is: your PRs merge first on their current Mantine code, then a follow-up migration PR converts those files to Tailwind + shadcn. This is additive work but doesn't block or conflict with your current development. Broader motivation: aligning our toolsThe goal isn't just to swap Mantine for Tailwind in app-v2 — it's to converge all PolicyEngine frontend projects on a single styling framework. Right now we have:
This fragmentation means design tokens, component patterns, and developer knowledge don't transfer across projects. I first tried vanilla-extract (#662/#665) as a way to improve styling while keeping Mantine components, but VE interfered with Plotly.js module loading and caused choropleth maps to render blank. Tailwind compiles to static CSS via a Vite plugin with no runtime, so it doesn't have that problem. Tailwind is the natural convergence point since most of our tools already use it, and pairing it with shadcn/ui gives us the component library layer. Beyond that, Mantine's breaking upgrade cycle (v6→v7→v8) has been costly each time, while with shadcn you own the component source code and never face library-driven breaking changes. |
- Fix About dropdown: use onSelect with navigate() instead of asChild+Link (Radix DropdownMenuItem prevents default on Link clicks) - Country picker: add border, country code label, hover state - Dropdown menus: add explicit tw:bg-white background - Footer: tighten spacing, softer link colors, smaller social icons - FooterSubscribe: remove left padding, constrain input width, zero margins Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the detailed response, Max. Let me address each section. On the framework evaluationThe evaluation document is here: https://docs.google.com/document/d/1efbBqJncYTWydD7t08w69GaTXwo3LUpHN_Jd1IMv43E/edit?usp=sharing The document didn't evaluate Tailwind as a standalone option, but rather as part of HeroUI, a component framework built on top of it. I conducted the evaluation and then met with Mike, Sakshi, and Nikhil to discuss the findings. You're right that the February 2025 comment predated this — the evaluation happened afterward. On Tailwind's drawbacksOne of the primary goals of the evaluation was to find a framework that was feature-rich and would ensure internal styling consistency. The evaluation found that while Tailwind is highly customizable, it requires significant hand-building for most components. It also requires direct CSS knowledge and provides limited abstraction compared to component-level frameworks. The concern was that these characteristics would likely lead to less internal design cohesion and longer development time compared to a more opinionated component library. On the popularity comparison: npm download counts and GitHub stars are useful signals for ecosystem health, but they don't map directly to suitability for a given project. It's also worth noting that shadcn/ui has significantly fewer npm downloads than Mantine, yet it was selected here alongside Tailwind. That suggests the decision was driven by factors beyond popularity metrics, which I think is the right approach — but it cuts both ways when evaluating Mantine as well. On merge conflictsThe merge conflict analysis in the comment is useful, but it focuses on open PRs against the current base. The more significant migration concern is what happens when we eventually merge the On tool alignmentThe tool alignment argument is the strongest case here, and worth taking seriously. One thing I'd want to understand better is how Tailwind addresses visual consistency across projects. Tailwind provides low-level utility classes, which gives developers significant flexibility in how they style components. The PR mentions shared design tokens, which is an important piece, but the question is whether that's sufficient on its own to ensure front ends share a cohesive visual design. A component library like Mantine enforces a degree of visual consistency by default, since developers work within the constraints of the library's design system. This may be an open question — shared visual alignment across projects may or may not be a priority — but it would be good to clarify that as part of this discussion. On Mantine's breaking upgrade cycleWorth noting that Mantine follows semantic versioning, so major-version breaking changes are expected and by design. This is standard across the ecosystem — any actively maintained library with major-version bumps will introduce breaking changes. The migration cost is real, but it's not unique to Mantine. On feature completenessThe PR description notes 6 wrapper components and 3 hook shims that replicate Mantine functionality — 9 compatibility layers in total, and this is only a partial migration. This suggests that the Tailwind + shadcn/ui combination requires additional implementation work to reach parity with what Mantine provides out of the box. As the migration continues into more complex areas of the codebase, it's worth considering whether the number of custom shims will grow and what the maintenance implications of that would be. An open questionI'd like to better understand the full case for Tailwind beyond tool alignment. For example: are there specific technical benefits — performance characteristics, styling guardrails, developer experience improvements — that Tailwind provides over Mantine for this project? And for the other PolicyEngine projects that already use Tailwind, was that an intentional technical choice, or more of a default that Claude selected? Understanding that original rationale would help in evaluating whether Tailwind is the right convergence point or whether the existing usage is more incidental than intentional. |
|
Thanks for the thoughtful response, Anthony. A few replies: On the evaluationI read the doc — thanks for sharing. It's a solid comparison of component libraries, but it evaluated Tailwind only as a dependency of HeroUI, not as Tailwind + shadcn/ui (which is what this PR proposes). The shortlist was Chakra, Mantine, and HeroUI — this option was never compared. So I don't think we can say it "ranked low" when it wasn't evaluated in its current form. On the "Claude default" questionYou asked whether Tailwind in other PE projects was intentional or Claude's default. Honest answer: mostly Claude's default. But I think that's signal, not a problem. When I start a new project, I ask Claude "if we were building this from scratch, what would you use?" — and it consistently picks Tailwind because of the 40x larger ecosystem, more training data, and better generation quality. In an AI-first workflow, aligning with what the AI is best at is a feature, not a bug. We should be letting the AIs cook, not fighting their defaults. On visual consistencyDesign tokens in Tailwind (which we define in On the shimsFair point. The 9 compatibility wrappers exist because this is a phased migration — they get removed as each area converts. They're scaffolding, not permanent debt. The alternative (big-bang migration) would be far riskier. On Mantine's semverTrue that breaking changes on major versions are expected. The difference is that with shadcn, we own the component source code — there are no library-driven breaking changes ever. We upgrade on our timeline, not Mantine's. On the migration branch conflictGood call — I'll add a conflict assessment for the api-v2 migration branch to the PR description. On the broader motivationThe question I keep coming back to: if we were starting app-v2 from scratch today, what would we pick? I'm confident the answer is Tailwind + shadcn. Given that, migrating now while the codebase is still young is cheaper than doing it later. And it converges us with the state legislative tracker, OBBBA tools, slides framework, and every standalone calculator we've built. |
Summary
Complete migration from Mantine UI to Tailwind CSS v4 + shadcn/ui (radix primitives). Zero
@mantine/*imports remain. All Mantine packages removed from dependencies.Tracking issue: #729
What changed
Foundation:
tw:prefixcn()utility (clsx + tailwind-merge)Wrapper components (Mantine API compatibility):
Text,Stack,Group,Title,Container,SpinneruseMediaQuery,useViewportSize,useDisclosureComponent migration (~190 files):
@mantine/dates(YearPickerInput, DatePickerInput) with native HTML inputsRemoved:
@mantine/core,@mantine/dates,@mantine/hooksdependencieseslint-config-mantine,postcss-preset-mantinedev dependenciesMantineProviderfrom app shells, test fixtures, Storybooktheme.ts,styles/colors.ts,styles/components.ts)Code quality:
Test plan
bunx vitest run— 2999 tests pass (248 files)bun run typecheck— 0 errorsbun run lint— 0 errors (ESLint + Stylelint)bun run build— succeeds (website + calculator)🤖 Generated with Claude Code