25 april 2025


Ostrowski Arnaud

Technical Product Owner


FrontendDesign

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

As we looked to support more frameworks beyond Blazor, Web Components seemed like a promising path. We built a parallel library using Lit and Shoelace, wrapped it for React, and hoped it could scale across the JavaScript ecosystem.

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.

 
And fundamentally, having a second, parallel library meant we were now maintaining two disconnected worlds. APIs drifted. Behaviors diverged. There was no single source of truth. We hadn’t solved fragmentation, we had just given it a new face.


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:

  1. A technology foundation that could scale across frameworks
  2. We needed a compiler that could generate components for multiple frontend stacks from a single source—without compromising behavior, accessibility, or styling.

  3. A seamless connection between design and code
  4. 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.

  5. A system that teams could trust and extend
  6. 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: 

  1. Extract values from Figma via its API 
  2. Convert them into a structured token format 
  3. Export those tokens into a custom Tailwind configuration 
  4. 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

We’d just wrapped months of work on the Nova Tycho beta. It was just before Christmas, and spirits were high. We published the package, announced it internally, and told teams they could start exploring the new system.

Then the messages started rolling in:

“Hey… it’s not working.” 
“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

Our token system depends on extracting Figma variables and converting them into W3C-compliant tokens. Initially, we relied on a third-party plugin to do the heavy lifting.
 
That plugin worked great… until it didn’t.

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.

It was a frustrating failure—but it left us with a pipeline we now fully understand and control.
 

The One with Blazor’s Two-Way Binding

Blazor introduced its own set of complexities—especially around two-way data binding in forms. Unlike React or Vue, where input state can be controlled more explicitly, Blazor relies on a different set of conventions and patterns. Getting our components to behave correctly in Blazor, without breaking the unified API structure, took real digging and experimentation.

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

Stencil was new to most of us. So was the idea of writing components in a way that could be compiled into multiple frameworks. There was a learning curve—not just in terms of syntax, but in terms of mindset.

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.

But that process built alignment. It made us a stronger team. And ultimately, it raised the quality bar for every component we shipped.
We don’t regret any of these bumps.
 
Each one helped us sharpen the product, the process, and ourselves.
 
 
What It Feels Like Now

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.

The API is consistent across frameworks. Developers don’t need to relearn usage patterns when switching stacks. Designers don’t have to chase teams for feedback on fidelity or behavior. The handover process isn’t really a handover anymore—it’s a continuation of the same system.

We’ve replaced uncertainty with predictability.

We’ve replaced duplication with reuse.

And most importantly, we’ve replaced parallel effort with shared momentum.
Designers feel empowered. They can trust the system to represent their decisions accurately—and spend their energy solving real problems, not debating colors or spacing. Developers feel unblocked. They can move quickly, focus on logic and architecture, and know that the UI layer will hold up under pressure.
 
Even conversations across teams have changed. We speak the same language now—because the system enforces it.

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.

And that’s exactly what we set out to create.

Looking Ahead: Opening the Gates

Rebuilding Nova was never just about solving our current problems. It was about unlocking what comes next.

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

We know that our ecosystem isn’t static. New technologies emerge, stacks evolve, and product teams have their own constraints. With Nova Tycho’s new architecture, we’re ready to adapt.
 
We’re exploring deeper support for alternate render targets, more flexible theming strategies, and optional presets tailored to different types of digital products. The groundwork is there. Now it’s about growing it in the right direction.
 

And Yes… Open Sourcing Nova

This is the question we hear the most when we present Nova:

“Is it open source?”

Not yet.

But we want it to be.

We believe that the work we’ve done—especially around framework-agnostic architecture, design token pipelines, and multi-platform documentation—is valuable beyond our organization. And we know the community could benefit from it, challenge it, and help us improve it even further.

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.

We’re not there yet. But the door is open. And we’re walking toward it.

Nova started as an internal solution. Today, it’s something more.

Tomorrow, it might be yours too.

Discover the design system:Nova

Thanks and Acknowledgements

Rewriting Nova wasn’t just a technical project. It was a team effort—one that pushed us, challenged us, and ultimately brought out the best in us.
 
To my team: thank you.

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.

Starting over is a risky pitch. Choosing to rebuild from scratch meant time, cost, and uncertainty. But you backed the decision—and gave us the space to do it right. That trust made all the difference.

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.

This version of Nova was built by people who care.
 
Not just about components—but about the people who use them.
 

This website uses cookies to provide you with an optimal browsing experience. Some cookies are strictly necessary for the operation of this website and cannot be rejected, while others are used for analytical/functional/targeting purposes and can be rejected. For more information, please consult ourCookie Policy . You can manage/change your cookie preferences at any time. If you do not manage your preferences, only the cookies which are strictly necessary will be accepted.