Bottom Nav
WhereUIแถบนำทางล่างสำหรับมือถือ 3–5 ปุ่ม ไอคอนพร้อมป้ายกำกับ รองรับสถานะ active ตัวเลขแจ้งเตือน และเว้นระยะ safe area ด้านล่างจอ
ติดตั้ง
Terminal
pnpm dlx shadcn@latest add @whereui/bottom-navต้องผูก registry และติดตั้ง @whereui/theme นำหน้าเสมอเมื่อติดตั้งครั้งแรก
การใช้งาน
import { BottomNav, BottomNavItem } from "@/components/ui/bottom-nav"import { HomeIcon } from "lucide-react"<BottomNav> <BottomNavItem href="/" label="หน้าแรก" active> <HomeIcon /> </BottomNavItem></BottomNav>ตัวอย่างเพิ่มเติม
สามปุ่ม ไม่มี badge
Props
| prop | type | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
| className | string | — | BottomNav ติดล่างจอด้วย fixed — ส่ง className="relative rounded-xl border" เพื่อวางในลำดับเอกสารแทน |
| href | string | — | ปลายทางของปุ่ม (BottomNavItem เป็น <a>) |
| active | boolean | false | หน้าปัจจุบัน — เป็นสี primary และใส่ aria-current="page" |
| badge | number | — | ตัวเลขแจ้งเตือนที่มุมไอคอน เกิน 99 แสดง 99+ |
| label | string | — | ข้อความใต้ไอคอน |
| children | React.ReactNode | — | ไอคอนของปุ่ม เช่น <HomeIcon /> |
ข้อควรรู้
- แถบเว้นความสูง env(safe-area-inset-bottom) ให้เนื้อหาหน้าเว้นพื้นที่ด้านล่างด้วย เช่น pb-24 เพื่อไม่ให้ตกใต้แถบ
- ความสูงปุ่มขั้นต่ำ 56px เหมาะกับการกดด้วยนิ้วบนจอสัมผัส
- ใช้คู่กับ PageShell ผ่านสล็อต bottomNav เพื่อโครงแอปที่สมบูรณ์
ซอร์สโค้ด
components/ui/bottom-nav.tsx
import * as React from "react"import { cn } from "cn"// แถบนำทางล่างสำหรับมือถือ — ใช้คู่กับ PageShell (สล็อต bottomNav) หรือวางเดี่ยวfunction BottomNav({ className, ...props }: React.ComponentProps<"nav">) { return ( <nav data-slot="bottom-nav" aria-label="การนำทางหลัก" className={cn( "fixed inset-x-0 bottom-0 z-40 flex items-stretch justify-around border-t bg-card/95 pb-[env(safe-area-inset-bottom)] text-muted-foreground backdrop-blur", className )} {...props} /> )}function BottomNavItem({ className, active = false, badge, label, children, ...props}: React.ComponentProps<"a"> & { /** ทำเครื่องหมายว่าเป็นหน้าปัจจุบัน */ active?: boolean /** ตัวเลขแจ้งเตือน (เกิน 99 แสดง 99+) */ badge?: number /** ข้อความใต้ไอคอน */ label: string}) { return ( <a data-slot="bottom-nav-item" data-active={active} aria-current={active ? "page" : undefined} className={cn( "flex min-h-14 min-w-14 flex-1 flex-col items-center justify-center gap-0.5 rounded-lg px-1 pb-1 pt-1.5 text-[0.7rem] font-medium transition-colors outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 data-[active=true]:text-primary", className )} {...props} > <span data-slot="bottom-nav-icon" className="relative [&_svg]:size-5" > {children} {badge ? ( <span data-slot="bottom-nav-badge" className="absolute -top-1.5 -right-2 grid h-4 min-w-4 place-items-center rounded-4xl bg-destructive px-0.5 text-[10px] leading-none font-semibold text-primary-foreground tabular-nums" > {badge > 99 ? "99+" : badge} </span> ) : null} </span> {label} </a> )}export { BottomNav, BottomNavItem }