Sidebar
shadcn's `sidebar-07` block, part for part — and the five components it takes to build one.
src/components/rahti_ui/sidebar.rs
The block
Open full screen →The frame is its own viewport, so the block inside lays itself out exactly as it does full screen — including the drawer it becomes when the frame is narrow.
Try it
Open the account menu at the bottom of the sidebar, and the team switcher at the top. Both are `popover` elements in the top layer — which is not a nicety here, because the sidebar's content pane clips and a menu that is not in the top layer would have its bottom half cut off.
Expand `Playground`, `Models`, `Documentation`. Each is a `<details>`, and each chevron is turned by its own row: `[[open]>summary_&]:rotate-90` reads the disclosure the icon's own `<summary>` belongs to.
Collapse the sidebar with the trigger in its header, or the rail down its right edge — and full screen, Ctrl / Cmd + B does the same. In the full-screen version, narrow the window past the `md` breakpoint and the same trigger opens it as a drawer.
What this block is made of
Four of those five open and close without a line of JavaScript — `<details>` and `popovertarget` are declarative associations in the markup, the way `<label for>` is. The scripts on that page do positioning, arrow keys and the sidebar's own collapse, and nothing else.
The menus
A `<button popovertarget="…">` opens a `popover` by id, with no script and no state. Light dismiss and `Escape` come with it. That is Radix's Portal, DismissableLayer and open-state, replaced by two attributes.
Because `popovertarget` takes an id and the trigger and the menu are separate components that cannot see each other, the pairing is a `menu` name written on both — the same divergence the Accordion's `name` and the Tabs' `value` made.
Placement happens twice. Where the browser has CSS anchor positioning it is an inline `position-anchor` and `position-area`, so the menu lands correctly with no JavaScript; elsewhere the script measures and writes `left`/`top`. Those are inline styles rather than classes because the anchor name varies per menu, and Tailwind can only emit classes it finds spelled out in the source.
`Account`, `Billing`, `Notifications` and `Log out` are `<a role="menuitem">` rather than shadcn's `<div>` — an item with an `href` is a link the browser can already focus and activate, so those four work with the runtime absent. `Upgrade to Pro` has no `href`, so it is shadcn's `<div>` and the script supplies `Enter` and `Space`.
Where the React context went
`useSidebar()` publishes seven values, and six exist only to write an attribute or pick a branch. The icon collapse, the offcanvas slide, the variants, the rail's four cursors and which parts vanish when the sidebar is a strip of icons are all `group-data-[…]` and `peer-data-[…]` in shadcn's own strings — so all 23 class strings crossed verbatim.
`isMobile` is a media query rather than a hook: both trees are in the document and `md:block` / `md:hidden` choose. Nothing measures the viewport and nothing re-renders on resize — at the cost of the sidebar's children being in the document twice, one copy `display: none`.
What did not cross
The tooltip on a collapsed menu button is a `title` attribute — there is no Tooltip in this library yet. `Skeleton` is two class strings inline for the same reason.
The dropdown's exit animation is dropped: a popover is `display: none` the instant it closes, so a keyframe exit would never be seen. shadcn's enter animation survives untouched, with `data-[state=open]:` rewritten to `open:` — Tailwind's variant for `:popover-open` as well as `[open]`.
And two Radix custom properties published by Floating UI and by nothing here: the menu's available height is asked of the browser with a `dvh` unit instead.