NebulaKit is shaped by years of working with code across many projects and by the lessons learned from the problems met along the way. Its purpose is to solve some of those problems. Each section below describes one of them and the approach NebulaKit takes.
Starting a new project often means assembling a lot of UI-related libraries before the first page. Each of them has to be set up, made to work with the others, and kept up to date with its own releases and breaking changes. On top of that, the libraries only provide pieces: every actual component still has to be built from them, and each one becomes more code for your team to look after.
When everything an interface needs comes from one system, choosing those libraries, building components out of them, and keeping their versions compatible is no longer the developers' job. A system built and released as a whole has a single version to track, and its components look and behave as one because they were designed together.
When you piece the UI together yourself, the component library usually needs to be configured before the first page can be built and looks the way you want, often through a large theme object passed down through a provider. Over time it grows into hundreds of lines that someone has to own, and every upgrade becomes a chance for it to break.
When a UI library comes with its design decisions already made, the setup shrinks to a handful of optional, app-wide settings. An app can run without any configuration at all, and the settings it does use take a few lines instead of hundreds, so there is no large configuration for anyone to own.
Not every UI library comes with a look at all. Some hand over the behavior alone and leave all of the styling to you. That can sound like flexibility, but it comes at a cost once the real work begins. Every visual detail becomes your team's job to design and build, and then to keep aligned as the app grows.
When the design is built into the components, those decisions are already made. A component looks finished the moment it renders, and your team can build the product instead of the design.
Beyond how an app looks, the way its CSS is produced affects performance. A popular approach generates it in JavaScript at runtime: the rules are created while the page renders and injected into the document. It is convenient, but that work adds to every render, and server rendering needs additional setup just to extract the CSS.
When everything is compiled at build time into one static stylesheet, none of that work happens in the browser. Components only describe their appearance and the stylesheet resolves the rest, so the browser can cache it like any other file, and a server-rendered page simply links to it.
An app also has to work for everyone, and accessibility is a promise almost every UI library makes. Read further, though, and the promise often comes with a list of things left for you to do. Those steps are documented, but under deadline pressure they are the first to slip.
Accessibility holds up only when it is built into the components rather than left to the app. When every component follows the WAI-ARIA patterns on its own, there is far less risk of forgetting something along the way, even when time is short.
Working with any UI library means reading its documentation, which should answer a question quickly, yet too often does the opposite. The page of a single component tends to mix very different kinds of content together, which leaves it long, hard to read, and harder to understand.
Documentation is easier to read when every topic has its own place. With each component documented across dedicated pages, the answer is a click away, and with examples built with props, the code you read is the code you write.
Once an app is live, the UI library it is built on keeps evolving, and how its updates get into the app matters. Some libraries are not installed as packages at all. Instead, the source code of each component is copied into your project. It feels like freedom, and for a while it is. Then you change it. From that moment the original moves on with fixes and new features, your copy stays where it was, and every component you own becomes one more you maintain alone.
Updates stay available only as long as the components remain part of the library rather than your codebase. When a UI library is installed as a versioned package instead of being copied into the project, fixes, improvements, and new components arrive with a version bump, and a changelog shows what each release brings.
As an app lives on, it grows: new features land, requirements change, and the code passes from one developer to the next. A year later, UI code and business logic are mixed in every component, and it is hard to tell where one ends and the other begins. Nobody wants to touch it anymore, and every new feature makes it a little worse.
The solution is to hand UI code and UI decisions over to the library. When the UI is described with props and the library takes care of everything behind them, what stays in your app is the code your product is actually about.
Apps lose their visual coherence one small decision after another, when every value is open and every developer picks their own. In the end, the same things look slightly different on every page, and nobody can say which version is right.
Consistency lasts when it is part of the system instead of enforced in code review. If all components share the same foundation and work within the same constraints, every view matches the rest, no matter who writes it and when.
Today a lot of UI code is not written by people at all. AI produces it in large volumes, and given free-form styling, it fills components with class names and one-off values. An agent does it far faster than people do, so inconsistency builds up much sooner.
An agent writes reliable code when the API leaves little room to improvise. When the look of every component is set through props with defined values, an agent chooses rather than invents, and its code stays uniform from one view to the next. Docs written for AI agents and matched to the installed version help it write cleaner and more predictable code.
No system is perfect. Each one is shaped by the problems it sets out to solve and by the trade-offs it has to make along the way, and NebulaKit is no exception.