The library gave me three buttons for free. The bottle I had to earn by hand.
Three buttons and one bottle
FocusBottle is structurally a very small app: three timer lengths, one start button, and a bottle that fills with light while you work. No account, no settings screen, no onboarding. You open it, pick 25, 50, or 90 minutes, and the light begins to move.
Which is exactly why I did not write its button from scratch. I have done that before — lost most of a week to a Select, of all things, and nearly all of it to keyboard navigation and focus rings I could not see but a user could. Never again. The three buttons took an afternoon because somebody else had already solved them.
The bottle took much longer. That asymmetry is the whole lesson.
What "default" means now
A component library used to be a pile of CSS you installed and then spent a month overriding. The newer ones are assembled differently: the interaction and accessibility layer comes from React Aria, the styling and design system come from Tailwind v4, and the library binds the two. What lands in your project is not a pretty button. It is a button that already handles the keyboard, the screen reader, and dark mode, behind a typed API.
Which is why the pitch quietly changed. It used to be about variety — how many components are in the box. Now it is about default: the claim that you no longer have to decide what a modal should feel like, or how a select should announce itself.
That is a better thing to sell, because deciding is the expensive part.
Teaching an agent your components
There is a newer problem, and I hit it constantly. Point an AI coding agent at a page and it will invent a Button from memory — usually a plausible one, occasionally a component that does not exist in your library at all. It writes confidently, and you find out at runtime.
Several libraries now ship context for exactly that: a machine-readable docs file, an MCP server, an installable skill so the coding agent reads the real component API instead of hallucinating a familiar-sounding one. The improvement in a first draft is not subtle. Plausible code becomes correct code, and you stop reviewing syntax long enough to review intent.
It is a strange thing to call a feature. But if your library is what the agent reaches for, the agent has to be able to read it.
The part you still write yourself
None of this is free. A library that saves you the button also asks you to accept some of its opinions about how a button should look and behave. If your product has a visual identity you actually care about, that is a real bill, and it is worth reading before you commit.
So the split I landed on is this: take the library for everything that is infrastructure — modals, selects, focus management, dark mode — and write the part that is the product. In FocusBottle that part is the glass. Particles drift upward faster as your minutes accumulate, and if you reach for your phone at minute nineteen, the light visibly dims. Small, fussy, hand-built. No library ships it, and none should.
The joke is that FocusBottle is three numbers and a bottle, and the library did most of the work. But the reason to use it was never that it made the app look good. It was that it bought me the hours to fight with the light instead of with the button that starts it.