Button
Displays a button or a component that looks like a button.
src/components/rahti_ui/button.rs
Installation
Install this component in your Rahti 0.0.58 application. Class conflicts are resolved with the existing frontend `twMerge`; no Rust class-merging dependency is needed.
Variants
Six of them, and `.default_variant()`, `.secondary()`, `.destructive()`, `.outline()`, `.ghost()` and `.link()` are shorthands for the same thing.
Sizes
`.xs()`, `.sm()`, the default, and `.lg()`.
Icon sizes
Square, no label padding. Give one an `aria-label` — there is no text to read out.
With icons
Icons are components, installed with `cargo rahti-icons add` rather than pasted in, and the base class list both sizes and spaces them. Two selectors do that and they are not equally forgiving: `[&_svg]` is a descendant selector and finds an icon wherever it sits, while `has-[>svg]:px-3` wants it as a direct child. These are built with `Html::concat` rather than an `html!` fragment so that it is one — see `icon_and_text`.
States
`.disabled(true)` writes the attribute; the base class list handles the rest. A spinner child is all a loading button is. `aria-invalid` is styled too.
As a link
`.href(...)` renders an `<a>`. A disabled one has no `disabled` attribute to set, so it gets `aria-disabled` and is made inert instead.
Attributes
`.attr()`, `.flag()`, `.submit()`, `.reset()` and `.plain()` land on the rendered element, and anything the caller sets wins over the component's own.
Class override
Rust composes the component and caller class lists. When utilities conflict, use the existing frontend `twMerge` in a compiled class binding.
Events, page scope
A handler that reads this page's state belongs on markup this page owns. `button_variants` returns the same class list the component renders, so a native `<button>` here is a Button in every way that shows.
Events and bindings, inline
`.render(children)` returns the element itself, with no boundary around it, so interpolating it into this block puts it in this block's scope. Handlers and bindings both work, and it is still the same component.
Reactive class strings
When the class list itself changes with state, the merge has to happen in the browser — and `main.js` publishes `twMerge` as a global for exactly that. `button_variants` is seeded into the script below, so the base is still this component's. Note the cost: the attribute is a binding, so it is literal text until PulsePoint mounts.
Events, component tag
Native event attributes, written on the tag and put on the `<button>` untouched — the component is UI and the handler is the caller's. The tag stamps a boundary, but an event expression is resolved when it fires, so these drive the same `count` this page declares. So does a `{…}` written between the tags: children read the block that wrote them, and the last one is here to show that.