

Technical Product Owner
Rebuilding Nova: How We Scaled Our Design System for a Complex Tech Landscape
From mismatched libraries to a framework-agnostic platform: lessons from the rewrite of Nova Tycho.
Starting Over, On Purpose
We didn’t set out to rebuild Nova from scratch.
Like many teams with a design system in production, we thought we were on the right track: we had a designer-facing library in Figma, a component library for Blazor, and an ambitious plan to support more JavaScript frameworks: wrapping webcomponents for React and Angular. We made it work—kind of.
But as Nova started to grow across the organization, the cracks widened. The design and development layers weren’t aligned. APIs drifted apart. The same component looked different depending on where you used it. The maintenance overhead was multiplying, and worst of all, trust in the system was starting to erode.
So we made a decision that most teams avoid: We scrapped it. And we started again.
This is the story of how we rewrote Nova—from its architecture to its tokens to its tooling and philosophy. It’s a story about technical decisions, yes—but also about the human challenges behind scaling a system that’s meant to unify disciplines, not just serve them separately.
Our goal wasn’t just to make Nova better. It was to make it right. For designers and developers. For different frameworks. For the future.
Where We Came From
Nova started with the right intentions. We wanted to build a design system that would bring consistency, speed, and alignment across a growing digital landscape. At the time, our primary frontend stack was Blazor, so our initial implementation — built in collaboration with an external agency — focused on delivering a component library tailored to that framework.
We developed a Figma- and a Blazor-based library. The agency worked on one side, we provided feedback on the other. And this worked fine. Even after they delivered the final output, we took over and adopted it.
But as demand for Nova grew and new technologies emerged inside the company, so did the need for broader framework support. We responded by creating a parallel JavaScript implementation based on Shoelace, a Web Component library built on Lit. We added a wrapper to make it compatible with React and Angular. Over time, we juggled two separate codebases: one for Blazor, one for the JavaScript ecosystem.
On paper, it gave us coverage. In practice, it gave us divergence.
The Blazor and JS components had different behaviors. Their APIs weren’t consistent. Their visual fidelity didn’t always match the designs—or each other. Even internally, the team had to juggle between Nova version depending on their stack. And when something changed in the design library, syncing that change downstream to both implementations was fragile at best.
Behind the scenes, we were also struggling to define a proper design-to-dev process. Design decisions weren’t consistently reflected in code. Developers often had to guess at intent or build from scratch. The very idea of Nova as a unified system started to slip away.
By then, it was clear: we weren’t scaling Nova. We were duplicating it.
That realization hurt. But it gave us clarity. We didn’t need to keep adding patches: we needed a new foundation.
Lessons in Complexity
Scaling Nova wasn’t just a technical challenge, it was a reality check.
As more teams tried to adopt the system, we found ourselves answering the same questions over and over again. Why does this component from the design library look off once it’s implemented? Why are the APIs different depending on which framework you use? Why does customizing a component feel like reverse-engineering the system?
Here’s what we learned the hard way.
Lesson 1: Design and Development Can’t Be Split
A design system isn’t “a Figma library for designers and a component library for developers.” That separation is what leads to mismatches, misinterpretations, and wasted effort. The system has to be one thing—designed and built together, with shared APIs, shared behavior, and shared ownership.
If a component exists in design but doesn’t match in dev, it’s not usable.
If the developer has to guess what a designer meant, the system has failed.
So, we stopped thinking of Nova as two libraries. From this point forward, it would be one product—with one pipeline, one foundation, and one goal: consistency across the full workflow.
Lesson 2: Not All Web Components Are Created Equal
But the promise of reuse didn’t quite deliver.
We quickly discovered that Shoelace, while powerful, came with trade-offs. One major blocker was Shadow DOM. While encapsulation is useful for some use cases, it proved to be a major problem in ours. Styling Web Components became extremely difficult, and for teams with strong customization needs (which we have many of), Shadow DOM was more obstacle than benefit.
Lesson 3: You Can’t Scale Without Maintainability
At some point, we had two codebases, multiple wrappers, design–dev gaps, and growing frustration. The more we tried to patch and adapt, the more fragile everything became. Maintenance overhead was unsustainable. The developer experience degraded. And every new component took disproportionate effort to ship.
It became clear: scaling isn’t just about supporting more frameworks—it’s about doing it in a way that’s sustainable.
We didn’t just need a better technical solution. We needed a better philosophy.
That’s when we made the call to stop patching and start rebuilding.
Our North Star: Framework-Agnostic, Truly Unified
Once we recognized that Nova was unsustainable, the team stepped back and asked a hard but freeing question:
If we could rebuild the system from scratch, what would it need to be?
We didn’t want another patch or a slightly better fork—we wanted a foundation that could support any frontend team at our organization, regardless of the technology stack they used. That meant giving every team access to the same high-quality components, the same APIs, and the same design fidelity—no matter if they were working in React, Angular, Vue, or Blazor.
At the same time, we wanted to close the loop between design and development. Nova couldn’t be a design system that lived in parallel workflows: it had to be a shared tool, with the same vocabulary and structure on both sides.
So we set ourselves a goal that was ambitious, maybe even a little naïve at the time:
Build one component library, framework-agnostic by design, with consistent APIs and predictable styling, deeply connected to its design source.
This vision became our North Star.
To get there, we knew we needed three things:
- A technology foundation that could scale across frameworks
- A seamless connection between design and code
- A system that teams could trust and extend
We needed a compiler that could generate components for multiple frontend stacks from a single source—without compromising behavior, accessibility, or styling.
Design decisions needed to be codified, not reinterpreted. If something existed in Figma, it should be reflected in development. And vice versa. No guessing, no second-guessing.
Customization had to be first-class, not an afterthought. Teams needed to adapt Nova to their product realities without hacking around limitations.
These principles guided every decision that followed. They pushed us to rethink our tooling, our process, and the way we structured our code and tokens. Eventually, they led us to the right stack—and the architecture that made Nova Tycho possible.
How We Rebuilt Nova
Architecture: One Core, Many Outputs
The first major challenge was choosing the right technology to support our multi-framework ambitions—without compromising developer experience or maintainability.
After experimenting with Lit and Shoelace, and discovering their limitations, we landed on StencilJS.
Stencil gave us exactly what we needed:
- A framework-agnostic compiler
- Native support for React, Angular, and Vue wrappers
- A light DOM approach that avoided the customization issues we had with Shadow DOM
- A clean abstraction layer that allowed us to build a single codebase and distribute it across multiple frameworks
It also didn’t provide opinionated styling out of the box—which was a blessing. We could define our own design tokens, theming logic, and interaction behaviors without constantly fighting the framework.
The only thing missing was Blazor support.
So, we built it ourselves.
We created a custom wrapper that allowed Stencil-generated components to run inside Blazor using JavaScript interop. It wasn’t simple, and it certainly wasn’t stress-free, but it worked—and it gave us the final piece of the puzzle: a single library that worked everywhere our product teams did.
Tokens, Tailwind, and the Figma Bridge
To unify design and development, we needed to start with a shared language. For us, that meant tokens—and lots of them.
We rethought our entire token architecture from scratch:
- We aligned on W3C-compliant token naming conventions
- We restructured how tokens were grouped, themed, and referenced across modes
- We used Figma variables as the single source of truth
From there, we built a pipeline that would:
- Extract values from Figma via its API
- Convert them into a structured token format
- Export those tokens into a custom Tailwind configuration
- Enable developers to use Nova’s styles without ever writing custom CSS
This gave teams the flexibility they needed:
They could build apps using Tailwind with full access to Nova’s design language—without needing to override components or style files. Everything was mapped, predictable, and customizable through configuration alone.
Was it easy to set up? Not even close.
We rewrote the pipeline several times. At one point, a plugin we relied on completely broke, and we had to build our own tooling to take its place.
But the result was worth it. We now had a token system that truly bridged design and code.
Docs, Testing, and the Delivery Pipeline
Documentation had been a pain point in the past. So in Nova 3, we automated everything we could.
Thanks to Stencil’s built-in support for JSDoc-style comments and metadata extraction, we generated technical documentation directly from the component source code. No more manual syncing. No more stale docs. Every prop, slot, method, and event was documented where it belonged: right next to the code.
For JavaScript frameworks, we used Storybook to provide live, interactive playgrounds.
For Blazor, we extended an open-source viewer to offer a similar experience.
Testing was also a first-class concern. We built a full end-to-end test suite that validated:
- Component rendering across frameworks
- Accessibility compliance
- Styling consistency
- Behavior in real-world usage scenarios
And yes—we learned a few hard lessons along the way. Like the time we confidently released a “working” beta package… only to discover it was empty. Turns out, even the best rewrite needs a solid release checklist.
Each of these pillars contributed to something bigger than just a better system.
They gave us the foundation for a unified design-to-code workflow that works across stacks, teams, and disciplines—while being flexible enough to evolve with us.
What Went Wrong (and What We Fixed)
Rewriting a design system isn’t glamorous work. It’s long, messy, and full of unknowns. And while Nova 3 stands today on solid foundations, the road getting there was anything but smooth.
Here are a few moments that didn’t go as planned—and what we learned from them.
The One with the Empty Package
Then the messages started rolling in:
“I’m importing the package, but nothing shows up.”
“Is this… empty?”
It was. Completely empty.
We had misconfigured the build pipeline and shipped an installable package with zero content. A funny story in hindsight—but in the moment, it was stressful. That experience led us to double down on release checks, automated CI validations, and proper error handling in the build step. It’s not just about what you build, it’s how you package and ship it.
The One Where the Token Pipeline Broke
At some point, it broke without warning. We couldn’t extract tokens anymore, and the pipeline ground to a halt. That forced us to re-engineer the process ourselves. We built a custom solution, tailored to our structure, that parsed Figma values and transformed them into usable, structured design tokens.
The One with Blazor’s Two-Way Binding
We had to develop deep knowledge of Blazor’s data flow model, find clean patterns for interop with Stencil components, and test edge cases thoroughly. It slowed us down, but it strengthened the quality and clarity of our Blazor wrapper in the long run.
The One with the New Stack Overload
We had to rethink how we structured components. How we handled events. How we avoided assumptions that would break in one framework but not another. It took time for the team to find a groove, and we made our share of missteps along the way.
With Nova 3 now live and in the hands of our teams, everything feels different—not just because the code is better, but because the experience is.
We no longer worry about discrepancies between design and implementation. The button you see in Figma is the button you get in React, Angular, and Blazor. Teams don’t have to ask which component version is the “real one.” There’s only one.
We’ve replaced uncertainty with predictability.
We’ve replaced duplication with reuse.
There’s still work ahead, of course. But we’re no longer pushing against the system. Nova is no longer a liability to work around. It’s a tool we can build with.
Looking Ahead: Opening the Gates
Now that we’ve established a truly unified, framework-agnostic foundation, we’re in a position to do things we couldn’t even consider before—things that shift Nova from being “just” a design system to becoming a real product platform.
Here’s what’s on the horizon.
Better Support for More Teams, More Stacks
And Yes… Open Sourcing Nova
“Is it open source?”
But we want it to be.
We’re actively exploring how to make this happen. It’s not just about licensing or GitHub visibility—it’s about creating the right structure for contribution, governance, and long-term maintenance.
Nova started as an internal solution. Today, it’s something more.
Discover the design system:Nova
Thanks and Acknowledgements
You showed resilience when things broke, patience when things stalled, and creativity when the path forward wasn’t obvious. The system we have today exists because of your commitment—not just to the code, but to the craft. You made Nova 3 possible.
To our leadership: thank you for trusting us.
And to the engineers and designers across Elia Group who gave feedback, filed bugs, tested early versions, and asked hard questions: thank you. Nova is better because of your input. Please keep it coming.
No results found