SidebarNavItem
One row for status, title, recency (`meta`), quantity (`count`), and active; groups collapse with native disclosure.
Wraps: lucide-react, details
Use it
Examples import from the package. With copied source, import from @/components/ui/sidebar-nav instead.
import { SidebarNavItem, StatusDot } from "@zedtech/zedkit-ui";
<nav aria-label="Runs">
<ul>
<li>
<SidebarNavItem status={<StatusDot tone="success" />} meta="2m ago" count="3">
Nightly
</SidebarNavItem>
</li>
</ul>
</nav>Active
import { SidebarNavItem } from "@zedtech/zedkit-ui";
<SidebarNavItem asChild active>
<a href="/runs/1">Nightly</a>
</SidebarNavItem>Group
import { SidebarNavGroup, SidebarNavItem } from "@zedtech/zedkit-ui";
<SidebarNavGroup label="Runs">
<SidebarNavItem>Nightly</SidebarNavItem>
<SidebarNavItem>Staging</SidebarNavItem>
</SidebarNavGroup>Kit not installed yet?
Install
Terminal pnpm add @zedtech/zedkit-ui @fontsource-variable/inter @fontsource-variable/space-groteskLoad the styles
Tailwind does not scan node_modules, so the @source line points it at the kit's class names. The path is relative to this file.
src/app/globals.cssReplace the contents@import "tailwindcss"; @import "@zedtech/zedkit-ui/styles.css"; @source "../../node_modules/@zedtech/zedkit-ui/src/**/*.{ts,tsx}"; @source not "../../node_modules/@zedtech/zedkit-ui/src/**/*.test.*";Pick a look
Set data-identity="glass" on <html> so the finished app matches the site. Fresh app: replace src/app/layout.tsx. Existing app: merge, and keep ThemeProvider and other providers.
src/app/layout.tsxReplace the contentsimport "./globals.css"; import "./fonts"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en" data-identity="glass"> <body>{children}</body> </html> ); }Load the fonts
The Glass look uses Inter and Space Grotesk; these imports self-host them.
src/app/fonts.tsCreate the fileimport "@fontsource-variable/inter"; import "@fontsource-variable/space-grotesk";Import and render
src/app/page.tsxReplace the contentsimport { SidebarNavItem, StatusDot } from "@zedtech/zedkit-ui"; export default function Page() { return ( <main className="px-page-gutter py-section-sm"> <nav aria-label="Runs"> <ul> <li> <SidebarNavItem status={<StatusDot tone="success" />} meta="2m ago" count="3"> Nightly </SidebarNavItem> </li> </ul> </nav> </main> ); }
Package version 2.0.1.
Props
Show inherited HTML attributes
Standard attributes from the underlying element. See MDN.
Accessibility
Default host is a native button (type="button") with min-h-target (44px on a phone); for navigation, pass asChild with a link. asChild merges onto a single host element (a string, Fragment, or several children fall back to a button). active sets aria-current="page" on a link host and aria-current="true" on the button host (override via props). The title is the accessible name; status and trail stay aria-hidden by design — put those facts in the title if they must be announced. Item titles and group labels truncate. Group is native details/summary (Enter/Space, find-in-page). The chevron is aria-hidden. Callers own landmarks and list structure (nav wrapping ul/li). Disabled asChild hosts keep href and tab order; click, Enter, and Space are cancelled.
Troubleshooting
`asChild` needs one host element (a link for navigation) — a string, Fragment, or several children fall back to a button, they do not throw. `disabled` on `asChild` becomes `aria-disabled` (not `disabled` or `type`) and cancels click/Enter/Space; href and tab order stay. Status/meta/count wrappers omit when those props are missing (`status={0}` and `count={0}` still render). `meta` is recency or secondary text; `count` is a quantity; both sit in one trail as separate spans. Groups start closed; pass `open` to expand. Pass `StatusDot` / `Badge` in; this module does not import them. Do not wrap rows in a kit `<nav>` — keep `nav`/`ul`/`li` on the caller.