A design system should leave the code with you
What building Oceania taught me about registry-first components, token ownership, and why design systems should install as editable source.

I used to think the important part of a design system was the component itself: the button, the input, the select, the tooltip.
That is still true, but it is incomplete. The more I build interfaces, the more I think the install path is part of the design system too.
If a component arrives in your app as a black box, the system has already made a decision for you. It has decided where the code lives, how much you are allowed to change, what counts as customization, and how much friction sits between a product need and a component update.
Oceania was my attempt to design that relationship differently.
The install path is part of the product
Oceania is a small React component registry for Next.js and TypeScript. It ships components through shadcn-compatible registry files, which means you install them with a CLI command and the source lands directly in your app.
That sounds like implementation detail. It is not.
The install path decides whether a team treats a design system as infrastructure they own or as a dependency they work around. If the component code lives inside the app, the team can read it, tune it, delete parts, add states, and make tradeoffs without waiting for the design-system package to expose the perfect prop.
That does not make the system less opinionated. It makes the opinion inspectable.
Packages protect the library author. Registries help the app team.
Package-first component libraries are tidy for maintainers. You version the package, publish it, and consumers upgrade when they are ready. That model is right for many things.
But product UI is awkward. A component library author can only predict so much. Real apps have weird constraints: a dense admin page, a brand state, a form error that needs a slightly different layout, a keyboard interaction that only appears after real users touch it.
With a package, those needs turn into wrappers and overrides. With a registry, those needs turn into edits.
The difference is psychological as much as technical. Editing a local components/ui/select.tsx file feels like working on your product. Fighting a third-party Select API feels like negotiating with someone else's product.
Tokens are a portability problem
A copied component is not portable if its tokens are missing.
That was one of the first practical lessons from Oceania. The Button file is not enough. It depends on radii, font choices, border states, primary colors, disabled colors, focus rings, and spacing variables. If those tokens do not travel with the component, the component installs but the system does not.
So Oceania treats tokens as a registry item. The CLI can pull tokens.json, which writes app/oceania-tokens.css into the app. Components then read variables like --oc-primary, --oc-text-900, and --oc-button-radius.
This keeps the contract honest. Oceania gives you source and the visual language it depends on. You can accept the defaults, or change the variables and make the system yours.
The boring files matter
The most important parts of a design system are often not glamorous.
For Oceania, the boring files were the registry builder, the generated JSON, the fresh-install notes, the browser QA checklist, and the verification script. They are not what you show first in a portfolio screenshot, but they decide whether the system survives contact with another project.
The registry builder turns component source into shadcn-compatible JSON. It adds dependencies, registry dependencies, docs notes, and target paths. That means button.json knows it needs utilities and tokens. select.json knows it depends on Radix Select. The install command is only simple because the registry file carries the boring details.
This is the part of design systems I respect more now: polish is not only visual. Polish is also when the command does what the docs promised.
Behavior belongs to primitives
I did not want Oceania to hand-roll behavior for controls that already have excellent primitive libraries. Select, Checkbox, Switch, Slider, Tabs, Radio Group, and Tooltip use Radix primitives so keyboard interaction and accessibility semantics are not invented from scratch.
That lets the design system focus on what it should own: the visual grammar, composition patterns, state treatments, token contract, and install ergonomics.
It also creates a useful boundary. Radix handles the behavior. Oceania handles how that behavior feels inside an interface.
What I would improve next
Oceania is a first pass. It covers core primitives, not complex product surfaces. The next layer would be components that feel closer to application work: data tables, command menus, empty states, upload controls, notification patterns, and form sections.
I would also like stronger visual documentation around token changes. Right now, tokens are installable and inspectable, but the registry could show how changing a few variables transforms multiple components at once. That would make the system feel less like a catalog and more like a living material.
The larger lesson is already clear though: a design system is not finished when the component renders correctly.
It is finished when another team can install it, understand it, change it, and still feel like they are building inside their own product.
That is the kind of system I want to keep making.
Explore the full project here: Oceania case study, live registry, and source code.