Avatar
A round picture, and something to show while it is not there.
src/components/rahti_ui/avatar.rs
Basic
A picture that loads, one that does not, and one that was never given.
CN
CNThree avatars: one whose picture loads, one whose picture cannot, and one with no picture at all. Only the first shows a face, and none of them needed a load event to work that out.
Write the fallback *before* the image, which is the order shadcn writes it in anyway. The image is positioned and the fallback is not, so the image paints above it without a `z-index` — and when it fails, an `<img>` with an empty `alt` renders nothing and the initials underneath are simply what is there.
The fallback is a stacking order
Radix's four load states, as two elements and no JavaScript.
Radix tracks those three as component state and re-renders. Here they are the same two elements in the same stacking order the whole time — there is no state machine, no script, and no flash of the wrong one, because the right thing is already on screen in the first byte.
It is also why `alt` is empty unless you give it one. A broken image with alt text draws that text and a broken-image icon *over* the fallback, which is exactly what this arrangement exists to avoid. Set `alt` only when the picture is the only thing naming the person — beside a name, as it nearly always is, a decorative image is the accessible choice anyway.
Sizes
Three, and everything inside follows them.
CN
CN
`sm`, the default, and `lg`. The size is written as `data-size` and read back by three other parts through `group-data-[size=…]/avatar:` — the fallback's text size and all three of the badge's dimensions — so it is set once here and everything else follows.
A square avatar is a `class` away: `rounded-lg` on the root and on the fallback, which is what the Sidebar's account menu uses.
Badges
A dot in the corner, sized from the avatar.
CN
CN
A dot in the corner — presence, unread, verified, whatever the page means by it. It sizes itself from the avatar it is in, and at `sm` it hides any icon inside it rather than squashing one into eight pixels.
Groups
A stack, and the count on the end of it.
The group overlaps its avatars with `-space-x-2` and rings each one with the page's background, which is how the stack reads as a stack. It finds them with `*:data-[slot=avatar]:ring-2` — by the attribute rather than by a class — so an avatar a page builds itself joins the stack correctly too.
The count at the end sizes itself from the avatars beside it with `group-has-data-[size=lg]/avatar-group:size-10`, which reads the size off a *sibling* rather than an ancestor. Set the size on the avatars and the count follows.
The boundary
The component, written out as the HTML it already was.

The same avatar written as plain HTML. `avatar_image_variants("")` carries `absolute inset-0` with it, which is the one utility that makes the fallback work — a page writing its own `<img>` needs it as much as the component does.
Nothing is lost writing it out, because there was never anything to lose: no script, no state, no boundary. This is the only component in the library where the hand-written version is exactly the component.