When I first started evaluating a frontend stack for a design-system pilot, I kept seeing the same group of names: Ant Design, Arco Design, Tailwind CSS, shadcn/ui, Radix UI, GSAP, Framer Motion, and Magic UI.
They all appeared in conversations about interface development, but they were not solving the same problem. Without separating their responsibilities first, it was easy to compare tools that were never direct alternatives in the first place.
The useful questions were more basic:
Claude Code helped me organize the ecosystem into those layers. I then mapped the structure back to the Figma component workflow I already understood and used that model to make the actual stack decision.
The ecosystem becomes much easier to understand when each tool is placed by responsibility.
| Layer | Main responsibility | Examples |
|---|---|---|
| Styling foundation | Atomic styling utilities used to compose visual rules | Tailwind CSS |
| Complete UI components | Ready-made buttons, inputs, dialogs, tables, and other interface components | Ant Design, Arco Design, shadcn/ui |
| Unstyled behavior primitives | Keyboard interaction, focus, accessibility, and component behavior without prescribing appearance | Radix UI, Base UI |
| Motion | Transitions, movement, timelines, and animation behavior | GSAP, Framer Motion |
| Visual-effect components | More presentation-oriented components for marketing and showcase interfaces | Magic UI |
The Figma analogy helped me keep the layers straight:
Button / Primary / Large component;These layers can be used together. They are not one large category of competing UI tools.
This was the first distinction I needed to correct.
Tailwind does not give you a finished Button. It gives you atomic classes that can be combined into the color, spacing, radius, typography, and states of a button.
A component library packages those decisions into a reusable unit.
That is very similar to building a component in Figma: start with layout, fill, radius, and spacing, then save the result as a reusable component.
shadcn/ui makes this relationship especially visible. It provides components such as <Button>, but their visual styling is still composed from Tailwind classes.
So the more useful model is: Tailwind provides styling materials; a component library packages those materials into reusable components.
Once that distinction is clear, a question such as "Ant Design or Tailwind?" is no longer a meaningful comparison because the two tools operate at different layers.
The most important property of shadcn/ui for this project was not its default visual style. It was its ownership model.
Traditional component libraries such as Ant Design or Arco are typically installed as dependencies. Teams customize them mainly through APIs and theme systems.
shadcn/ui takes a different approach: its CLI copies the component source code directly into the project repository. Once generated, those files can be read, modified, removed, or gradually adapted like any other project code.
That matters when the goal is to turn a company-specific Figma design system into a company-specific code component library.
I was not looking for a third-party design language that happened to look close enough. I needed a component foundation with mature interaction structure that could still be visually rebuilt around our own tokens and component rules.
In that sense, shadcn was a starting point rather than the design system itself.
Looking inside more complex shadcn components also made another layer clearer: many of them rely on unstyled primitives such as Radix UI.
That layer handles things such as:
It does not decide the final radius, color, or typography.
This separation is useful for design-system work. The company can define its own visual system without rebuilding every accessibility and interaction behavior from scratch.
Changing an underlying primitive such as Radix or Base UI may affect implementation details, but it should not determine the final visual language by itself.
Ant Design and Arco Design are strong choices when the main goal is to build dense enterprise interfaces quickly. They already include mature tables, forms, filters, and administration-oriented components.
For a product that can accept their design language and theme model, that level of completeness is a major advantage.
The trade-off is different when the goal is a highly independent company design system. Their component structures and default design decisions are more opinionated, so making the result feel completely unrelated to the original library can require more overriding.
That is why I did not choose Ant Design for this pilot. It was not a judgment that the library was worse; it was optimized for a different goal.
Arco was still useful as a reference because it maintains both Figma design resources and corresponding code components, which makes it a good example of design-system synchronization across design and development.
GSAP and Framer Motion often appear in the same frontend discussions, but they do not replace a component or styling system.
They define how interface elements move.
Framer Motion is well suited to common UI transitions and state changes. GSAP is stronger when the interface needs complex sequencing or precise animation timelines.
A project can use shadcn/ui and Framer Motion together, or introduce GSAP only for more complex motion. The choice of component foundation does not remove the need for a motion layer.
Once the ecosystem was separated into layers, the relationship between a Figma design system and a code component library became clearer as well.
Figma defines things such as:
Code components additionally need to handle:
The goal is therefore not to translate Figma into code one-to-one. It is to let design and code share the same design rules while allowing the implementation layer to use mature engineering primitives.
That is why the pilot ultimately used React + Tailwind CSS + shadcn/ui:
One practical issue appeared during implementation: shadcn expects its own semantic CSS variables, such as:
--primary
--muted
--accentThe tokens extracted from the Figma design system used company-specific names, for example:
--color-text-brand-primaryInstead of renaming the company tokens or modifying every component, the implementation added a thin mapping layer between the two naming systems.
For example, shadcn's --primary can point to the corresponding company token.
This keeps several responsibilities separate:
The mapping also avoids treating a third-party component library's semantic names as if they were the company's design-token architecture.
Pilot stack: React + Tailwind CSS + shadcn/ui.
shadcn component source is copied into the repository rather than treated only as an opaque third-party package.
Company design tokens keep their own naming system and are mapped to shadcn semantic CSS variables through a thin bridge layer.
Complex component behavior can continue to use unstyled primitives such as Radix UI / Base UI, while the final appearance is defined by company tokens and component styles.
The most useful role Claude Code played here was translating an unfamiliar frontend ecosystem into the Figma component model I already understood.
I still did not treat that translation as the final source of truth. After hearing that shadcn components were essentially normal component code styled with Tailwind utilities, I opened the generated Button source myself and verified that the structure was actually readable and editable.
That workflow has been useful for me: use AI to map an unfamiliar domain onto an existing mental model, then validate that model against the real code or product once before making decisions with it. The first step lowers the learning cost; the second makes the understanding reliable enough to use.