Building a design system from scratch is one of those decisions that sounds either visionary or irresponsible depending on when you ask the question.
When I joined PickYourTrail and started looking at the web codebase seriously, the situation was clear enough: there was no strong, consistent UI foundation. Components had been built screen by screen, styling decisions had accumulated without a shared vocabulary, and the gap between what the web app looked like and what we wanted it to look like had no clear path to close. We could keep composing screens out of ad hoc component decisions, or we could invest in something that gave the whole team a shared system to build from.
We chose to build. The result was Atom — a design system built in layers, with Radix UI as the accessibility primitive foundation and Stitches for token-based theming. This is what that decision looked like in practice and what I learned from it.
Why we did not use Material UI, Ant Design, or Chakra
The question was not whether those libraries were good. They are good, and each reflects serious investment in both component coverage and accessibility. The question was whether they matched what we actually wanted to own.
What I did not want: a full visual language we would spend the next year fighting. Heavy UI library adoption often produces a situation where the library’s design opinions dominate the product’s UI decisions, and every time design wants something slightly different from the library’s defaults, the engineering path is either a workaround or a themed override that creates the appearance of customization without the reality. That surface grows over time.
What I wanted instead: the behavioral primitives done well by someone else, the product design layer owned entirely by us. That specific separation — trustworthy interaction behavior and accessibility underneath, our own design system on top — was the thing that made building feel worth the investment. We were not trying to reinvent keyboard navigation for dialogs or focus management for dropdown menus. We were trying to build a visual and compositional layer that matched PickYourTrail’s product identity specifically.
The layered architecture
The architecture that made sense to me was a vertical stack of packages with clear responsibility at each level.
@atom-web/core holds tokens — theming foundations, spacing scale, typography, color definitions, and the variables that flow through everything else. This is the layer that makes the system a system rather than just a collection of components.
@atom-web/micros holds primitives — the smallest useful components, built on Radix UI where behavior and accessibility are non-trivial, styled with our token layer. Buttons, text, icons, inputs at their most essential form.
@atom-web/macros holds more composed building blocks — things made from multiple primitives but still generic enough to be reusable across different product contexts.
@atom-web/widgets holds higher-level assembled components that carry more product opinion — cards, section layouts with expected visual structure.
That layering solved two problems. First, it made reasoning about the system cleaner — not everything belongs at the same level of abstraction, and forcing that clarity early prevents components from accumulating at whatever level was convenient when they were built. Second, it gave better dependency boundaries to consumers — a product team that only needs primitives can take just micros without pulling in the heavier opinionated layers.
Stitches as the styling foundation
At the time Atom was built, Stitches was the right choice for the styling layer.
The appeal was specific: token-based theming baked into the API, a good developer experience for building reusable styled components, zero-runtime positioning that mattered for performance, and enough flexibility to express our own design vocabulary rather than inheriting one. For a design system that other applications would consume as packages, having a styling engine where tokens are first-class rather than an afterthought was important.
We knew at the time that the CSS-in-JS landscape was moving and that this bet might need to be revisited. That eventually happened. But Stitches gave us the right foundation for the period when Atom was being built, and the architecture decisions we made around it — the token layer in core, the composition model — survived the later migration better than the styling engine itself did.
Radix UI for accessibility primitives
If there is one architectural decision from Atom that I would defend most strongly in retrospect, it is the choice to build on Radix UI for behavioral primitives rather than building them from scratch.
Accessible interactive components are genuinely hard to build correctly. Dialog accessibility, dropdown keyboard navigation, combobox behavior, tooltip timing and positioning — these take significant effort to get right across browsers and assistive technologies, and they require ongoing maintenance as browser behavior changes. Building them from scratch would have been a significant investment in exactly the part of the system that is most constrained by external standards rather than by product design opinion.
Radix gave us that primitive layer as a stable, well-maintained foundation. The unstyled Radix components handle the behavior and the accessibility contract. Our design system handles the visual layer on top. That separation meant we could change styling decisions without touching accessibility behavior, and trust that the interaction primitives would keep working correctly without us owning that maintenance burden.
The pattern is the right abstraction: own the things that are specific to your product, depend on well-maintained libraries for the things that are standard.
Icon system and cross-platform scope
The icon packages were one of the clearest signals that Atom was intended to be a real internal platform rather than a collection of components in a shared folder.
We ended up with @atom-web/icons-16 and @atom-web/icons-24 for web, sized for different UI contexts, plus a React Native variant. 650+ icons across sizes and platforms. Treating icons as a package-backed system rather than an unmanaged collection of SVG files made a visible difference in UI consistency — once every consumer of the design system is using the same icon set from the same source, coherence is automatic rather than enforced by discipline.
It also reflected a commitment that Atom was not just a web design system. React Native was in scope from early on, which shaped both the token design and the component architecture. A token system that only works on web is not much of a platform. Getting that cross-platform foundation established early, rather than retrofitting it after the fact, was one of the decisions that held up well.
Package infrastructure: Turborepo, Yarn workspaces, Changesets
A design system with multiple published packages is not just a component library. It is a monorepo product. That requires taking the build and release infrastructure seriously.
Turborepo handled the build pipeline across the package graph, making local development across packages coherent and CI efficient. Yarn workspaces handled the dependency linking between packages during development. Changesets managed the versioning and changelog process — important when you have multiple packages that need independent versioning while still being part of a coherent release.
This was the point where Atom started feeling like infrastructure rather than just a UI folder. Once multiple product surfaces depend on the design system, packaging and release discipline matters as much as component quality. A poorly managed release process in a shared design system creates ripple effects across every consuming product simultaneously.
What the investment actually produced
The system gave us a shared UI vocabulary the team could actually use, a package structure that scaled as more products consumed it, stronger visual consistency across surfaces, and the cross-platform foundation we needed for the mobile work that followed.
The most valuable part of a design system is not the styling engine. It is the clarity of the primitive layer, the package boundaries, and the decisions about what your team truly wants to own versus what it should depend on externally. Those structural decisions survived styling technology changes. The Stitches bet eventually had to be revisited, but the Radix primitive layer, the token architecture, and the layered package structure were all worth keeping.
Building from scratch is not automatically the right call for every team. But when you know exactly what you want to own and what you want to delegate, and when the products that will consume the system have specific design requirements that large UI library adoption would fight rather than support, building gives you the foundation that adoption cannot.