Nitro Kit 2.0

Good questions

Nitro Kit does a few things differently, so here are the reasons, in plain terms.

Why Phlex?

Because components are Ruby, and Ruby is good at this. A Phlex component is a plain class with a constructor and a view_template. You compose it like any other object. No template registry, no helper soup, no wondering where a partial's locals come from.

It also lets the components check their own inputs. Every option is an explicit keyword with a closed set of values, and a wrong one raises with the accepted vocabulary in the message. That beats rendering something slightly off and finding out in production.

Why not HTML?

You still get HTML. Phlex renders ordinary HTML, and it stays native: real buttons, real forms, real details/summary. The question is what you author against.

Raw HTML is a wide open API. Any attribute, any structure, any typo. That's fine for a page you own, but a component kit needs a narrower contract so it can promise the markup, accessibility, and styling all agree. Ruby constructors give you that contract. Named slots and blocks still accept ordinary Phlex content.

Why is there no class: prop?

Because it's a second styling API in disguise. The moment components accept arbitrary classes, every internal class name becomes something your app can depend on, and every Nitro Kit upgrade can quietly break your styling. We'd rather not do that to you.

Instead, components render classless, self-describing markup and customization flows through CSS custom properties. If an external script or widget genuinely needs a class hook, there's desperately_need_a_class:. It works, marks itself in the markup, and is named that way so you'll think twice.

How do I change stuff?

Start with the configurator. Pick accent, neutral, radius, density, font, and shell layout, see the system update live, then copy the deterministic CSS and AppShell composition into your app.

Past that, theming is the documented --nk-* custom properties. Override them globally or scope them to part of your app. For structural changes, wrap and compose Nitro components in your own, or subclass one when you need a small, fixed vocabulary such as a SaveButton that's always primary.

Why not Tailwind?

Nitro Kit ships static, plain CSS. There is no Tailwind build requirement or runtime dependency, and the components work the same in every app.

Your app can absolutely still use Tailwind. An optional Tailwind v4 adapter maps Nitro Kit's tokens into Tailwind theme variables, so utilities and components can share the same values. What you won't need is utility classes inside the components themselves. That's the point of the components.

What does Nitro Kit own, and what do I own?

Nitro Kit owns the reusable parts: component markup, CSS, and a little behavior where the browser needs help. Your app owns everything that makes it your app: data, routes, authorization, business rules, and how the pieces compose into product.

The line matters in practice. AppShell gives you responsive chrome, but you decide the navigation. SortableTable renders headers and aria-sort, but you supply the URLs and sort policy. Nitro Kit doesn't want your product decisions.

How much JavaScript is in it?

As little as we could get away with. Native HTML and CSS come first: accordions are details/summary, dialogs use native commands, dropdowns use Popover, and tooltips use CSS hover and focus.

What remains is a handful of small Stimulus controllers for the gaps, such as menu keyboard navigation and Active Storage uploads. They're progressive, clean up after themselves, and behave through Turbo Drive, Frames, Streams, and morphs.

Does it work with Rails forms and Turbo?

Yes, that's the home turf. Use form_with with NitroKit::FormBuilder and keep everything Rails gives you: naming, IDs, model values, CSRF, multipart forms, and real ActiveModel errors wired to the controls.

Turbo Frames and Streams use the normal Rails helpers from Phlex. Active Storage direct uploads work through Dropzone, with plain form submission as the no-JavaScript fallback. Nitro Kit doesn't wrap Rails. It stays out of the way where Rails is already good.