shadcn.io is not affiliated with official shadcn/ui
Bottom Tab Bar Navbar Block for Solid
A bottom tab bar for a running-diary app: five cells on shared hairlines, four links with a filled current tab and an unread dot, and a raised Shine Button that opens a drawer from the bottom with three ways to create.
Installation
Overview
A bottom tab bar for Solid with Ark UI, the navigation of an app you hold in one hand. It's a nav on the page's rails with five cells on shared hairlines, each meeting the top rule with a plus mark: Feed, Routes, Alerts and Profile are links with an icon over a label, the current one filled and marked by a short stroke on the rule so it isn't colour alone, and Alerts carries a dot and a spoken count until you open it. The centre cell holds a raised round Shine Button that opens a drawer from the bottom edge with a grabber and three ways to create (a post, a story or a live run) and a Cancel button. The bar is 64 pixels tall plus the home indicator's safe area, and it sits in flow here: pinning it to the bottom of a phone is one class change, described below. Its rails and top rule come from the Guides spine, so it lines up with every block above it. It's built from Drawer, Shine Button, Button and Guides, with your theme's tokens for every colour. For mobile web apps, social and tracking products, and any site whose few main destinations belong under the thumb.
FAQ
How do I pin it to the bottom of a phone?
Add fixed inset-x-0 bottom-0 z-40 md:hidden to the nav (it already paints bg-background, so the page doesn't show through), give the page pb-16 so its last content isn't hidden under the bar, and put viewport-fit=cover in your viewport meta tag so env(safe-area-inset-bottom) has a value: the bar already pads by it. In this preview it stays in flow at every width so you can see it. If your page also has a top navbar, give one of the two navs another name than Main, such as Tabs.
How do I change the tabs?
Edit FEED, ROUTES, ALERTS and PROFILE at the top of the file (an icon, an id and a label each) and UNREAD, the count Alerts starts with. The four are plain links with href="#": point them at your routes and derive the current one from your router instead of the block's current state, which only marks a tab when it's chosen. Four tabs and a centre action are as many as a 320-pixel phone holds with touch targets that big; a fifth link would need the Create cell to go.
How are the current tab and the unread dot shown?
The link for the page you're on has aria-current="page": its icon fills, its label turns foreground and medium weight, and a short foreground stroke lies on the bar's top rule. The unread dot is a small foreground circle, never a red one, and a visually hidden ", 2 unread" follows the label so a screen reader hears "Alerts, 2 unread". Opening Alerts reads them: the dot and the count go. Replace the count with your own data.
How does the Create button work?
It's a Shine Button named Create that opens a drawer from the bottom edge. The drawer has a title, a one-line description, three links (each an icon, a name and a line) and a Cancel button; Escape, Cancel, a swipe down or choosing a way closes it, and focus goes back to Create. The drawer's open state is held in the block (creating), so every way closes it.
The drawer is Ark's: it shows its own grabber.
The drawer's trigger asChild receives its props: spread them onto your button.
Why is it a nav and not a header?
A bottom tab bar isn't the page's header, so it doesn't use the header element the other navbars do. It's a nav landmark named Main with a list of links, and the Create button sits in the list as an item of its own, outside any link.
What moves, and what about reduced motion?
Colours ease between tabs and the drawer slides up from the bottom; nothing moves on load. With reduced motion the colours change at once and the drawer appears without sliding. In right-to-left layouts the cells run from the right and the hairlines and marks follow.