Prisma
Live Preview
Default (Material style)
Home
Search
3
Saved
Profile
Liquid Glass (Floating)
Home
Search
Wallet
Profile

--- description: Hướng dẫn Agent tự động tạo UI Component Bottom Navigation — thanh điều hướng cố định ở đáy màn hình mobile. Tích hợp trends iOS 26 Liquid Glass, Material Design 3 Expressive, Samsung OneUI 7.

Bottom Navigation Component Generation Skill

Skill này hướng dẫn bạn (AI Agent) tạo component Bottom Navigation — thanh điều hướng cố định ở đáy màn hình cho mobile apps.

1. Mục tiêu (Objective)

Tạo bottom navigation bar hỗ trợ 3-5 destinations, với active indicator, icon+label, collapsing on scroll, và tương thích với iOS 26 Liquid Glass + Material Design 3 Expressive + Samsung OneUI 7 design patterns.

2. AI Context & Intent (Ngữ cảnh cho AI)

Khi nào dùng Bottom Navigation?

  • Mobile primary navigation: 3-5 top-level destinations của app
  • Persistent across screens: luôn hiển thị, trừ khi fullscreen content
  • Equal importance: mỗi destination ngang hàng, không hierarchy

Phân biệt với component khác

Tình huống Component đúng Lý do
Mobile top-level navigation (3-5 items) Bottom Navigation Persistent, one-thumb reachable
Web horizontal navigation navigation-menu Desktop-first, wider layout
< 3 destinations Tabs Quá ít cho bottom nav
> 5 destinations Drawer / Sidebar Quá nhiều, cần overflow
Contextual actions (share, save) Bottom Bar / Toolbar Actions ≠ navigation
Secondary navigation within a section Tabs Scoped, not global

Decision Tree cho AI

text
Cần navigation cho mobile app?
├─ 3-5 top-level destinations, equal importance → Bottom Navigation
│   ├─ iOS style (liquid glass, floating) → variant="glass"
│   ├─ Material style (chip indicator) → variant="default"
│   └─ OneUI style (bold active, minimal) → variant="minimal"
├─ < 3 destinations → Tabs
├─ > 5 destinations → Drawer + Hamburger
├─ Desktop/web primary nav → navigation-menu
└─ Actions (not navigation) → Bottom Bar (bottom-bar pattern)

3. Platform Trend Research (2025-2026)

3.1. iOS 26 — Liquid Glass Tab Bar

Feature Spec
Material Liquid Glass — translucent, refractive
Shape Floating, rounded, inset from edges
Height 49pt content + safe area (34pt home indicator)
Active indicator Glass chip behind active icon
Behavior Collapses on scroll — shows only active tab when scrolling down, expands on scroll up
Search tab Can morph into search field positioned at bottom
Tab accessory "Shelf" above tab bar for mini-players, global status
Labels Always visible (3 tabs) or hidden when inactive (4-5 tabs)
Animation Spring curve on tab switch, glass refraction
Backdrop backdrop-filter: blur(30px) saturate(180%)

3.2. Material Design 3 Expressive — Navigation Bar

Feature Spec
Name "Navigation bar" (renamed from "Bottom navigation")
Container height 80dp (taller than MD2's 56dp)
Active indicator chip shape — contrasting color behind active icon
Active icon Filled variant; inactive = outlined
Labels Always visible (3 items); icon-only when inactive (4-5 items)
No shadow Uses color mapping instead of elevation shadow
Expressive update Shorter, flexible bar; horizontal items for medium windows
Re-select Scrolls back to top of current page
Badge Supports notification badges on icons

3.3. Samsung OneUI 7 — Bottom Nav

Feature Spec
Style "Sophistic Minimalism" — clean, subtle gradients
Shape Full-width, flush to edges (with safe area)
Active indicator Bold icon + label, no chip shape
Inactive Lighter opacity, outlined icons
Transparency Subtle transparent background in dark mode
Glass UI (8.5 preview) Frosted glass panels, transparency effects, coming 2026
Now Bar Dynamic info strip above nav bar (lock screen only)

3.4. Cross-platform Convergence Patterns

Pattern iOS 26 MD3 Exp OneUI 7
Floating
chip indicator ✅ (glass) ✅ (color)
Collapse on scroll
Icon filled/outlined ❌ (same)
Glass/translucent ✅ (OneUI 8.5)
Always show labels 3 tabs ✅ 3 tabs ✅ ✅ always

4. Ngữ nghĩa & Phân loại (Semantics)

4.1. Visual Variants

Variant Mô tả Use case Platform feel
default Solid background, chip indicator Standard apps MD3 Expressive
glass Liquid glass, floating, inset Premium/luxury iOS 26
minimal Flat, bold active, no chip Clean minimalism OneUI 7
elevated Shadow + solid bg Cards-heavy layouts Cross-platform

4.2. Sub-components / Slots

Part Mô tả Tokens chính
Container Full-width bar, fixed bottom --background / --surface, safe area padding
Nav Item Icon + Label, tappable 44×44 min touch target (a11y), gap: var(--spacing-1) ← S2: 4px grid
Active Indicator Default: chip 64×32 behind icon. Glass: full-item chip wrapping icon+label Default: --primary-muted. Glass: --input bg + --border-ghost border
Icon 20×20 (all variants) Default: --primary (active), --muted-foreground (inactive). Glass: --foreground/--muted-foreground
Label Text below icon, Caption tier --font-size-xs (12px), --font-weight-medium (T20: ≤12px legibility), line-height: 1.5 ← T3
Badge Notification dot or count --destructive bg + --destructive-foreground fg (bg/fg pair)
Accessory (opt) Mini-player / status strip above bar --surface bg

4.3. Interactive States

State Visual Token
Idle (inactive) Outlined icon, muted label --muted-foreground
Active Filled icon, primary color, chip indicator --primary, --primary-muted
Pressed Scale down 0.92, haptic transform: scale(0.92)
Hover (web/tablet) Subtle background --state-layer-hover
Focus (keyboard) Focus ring --primary outline
Disabled Reduced opacity opacity: var(--disabled)

4.4. Behaviors

Behavior Description
Persistent Visible on all top-level screens
Hide on sub-screens Hidden when pushed into detail views
Collapse on scroll (glass variant) Shrinks to show only active tab
Re-select = scroll top Tapping active tab scrolls page to top
Badge update Real-time badge count update
Safe area Respects device safe area (home indicator)

4.5. Slot Map (Figma ↔ Code)

📎 Source: slot-manifest.jsonnavigation-bar · Layer: ground

Figma Slot data-slot CSS Class Required Accepts
Root navigation-bar .ground-bottomnav
Item navigation-bar-item .ground-bottomnav__item ✅ (3-5×) nav-item (icon + label)
Icon navigation-bar-icon .ground-bottomnav__icon icon
Label navigation-bar-label .ground-bottomnav__label text
html
<!-- HTML skeleton with data-slot -->
<nav class="ground-bottomnav" data-slot="navigation-bar" role="navigation" aria-label="Main">
  <button class="ground-bottomnav__item ground-bottomnav__item--active" data-slot="navigation-bar-item">
    <span class="ground-bottomnav__indicator">
      <span class="ground-bottomnav__icon" data-slot="navigation-bar-icon">🏠</span>
    </span>
    <span class="ground-bottomnav__label" data-slot="navigation-bar-label">Home</span>
  </button>
</nav>

5. Token Mapping

📦 Atomic Mapping: See ATOMIC-MAPPING.md for complete token spec.

📦 Atomic Mapping: UI Layer: Ground, Density Tier: — (fixed height)

Property Token Ghi chú
Container bg --background Solid variant
Container bg (glass) --surface-subtle + backdrop-filter Glass variant (Figma: base/surface-subtle)
Container fg --foreground
Border top --border Separator from content
Active icon fg --primary (default) / --foreground (glass) Glass uses foreground, not primary
Active label fg --primary (default) / --foreground (glass)
Inactive icon fg --muted-foreground
Inactive label fg --muted-foreground
Indicator bg (default) --primary-muted chip behind active icon
Indicator bg (glass) --input Full-item chip (Figma: base/input)
Indicator border (glass) 1px solid var(--border-ghost) Figma: base/border-ghost
Indicator size (default) 64×32px MD3 Expressive spec
Indicator radius --radius-full (default) / --comp-radius-capsule (glass) chip shape
Badge bg --destructive
Badge fg --destructive-foreground bg/fg pair required
Icon size 20px All variants
Icon size (glass) 20px Unified — same as default
Label font Caption tier --font-size-xs (12px), --font-weight-medium (T20)
Label line-height var(--font-leading-normal, 1.5) ← T3 rule: caption tier = 1.4-1.5
Item gap (icon→label) var(--spacing-1) (4px) ← S2 rule: 4px base unit
Item min-width (glass) 60px Figma: min-width 60px
Touch target 44×44 min a11y requirement (R3)
Container height 80px (MD3 Expressive) Use var(--spacing-20, 80px)
Safe area bottom env(safe-area-inset-bottom) iOS/Android
Transition --motion-quick Icon/indicator switch
Indicator transition --motion-spring Bouncy chip slide
z-index --z-index-fixed Above content
Elevation (elevated) --shadow-sm Elevated variant only

Glass variant tokens (Figma-aligned)

Property Token Note
Container bg var(--surface-subtle) Translucent (Figma: base/surface-subtle)
Container border 1px solid var(--card-border) Figma: base/card-border
Container radius var(--comp-radius-capsule) (9999px) Full chip capsule
Container padding var(--spacing-2) Figma: var(--style-item-padding-small)
Container gap var(--spacing-2) (8px) Figma: var(--spacing-2)
Backdrop blur(20px) saturate(180%) Hardcode OK (decorative)
Active item bg var(--input) Solid chip (Figma: base/input, #FFF light)
Active item border 1px solid var(--border-ghost) Figma: base/border-ghost
Active item radius var(--comp-radius-capsule) Same as container
Active color var(--foreground) Not --primary (Figma: base/foreground)
Inactive color var(--muted-foreground) Figma: base/muted-foreground
Item min-width 60px Figma: min-width 60px
Item padding var(--spacing-2) Figma: var(--style-item-padding-xsmall)
Icon size 20px Figma: sys-component 20×20

6. Props & API

typescript
interface BottomNavProps {
  /** Visual variant */
  variant?: 'default' | 'glass' | 'minimal' | 'elevated';
  /** Navigation items (3-5) */
  items: BottomNavItem[];
  /** Currently active item index */
  activeIndex: number;
  /** Callback when item tapped */
  onSelect: (index: number) => void;
  /** Collapse on scroll (glass variant only) */
  collapseOnScroll?: boolean;
  /** Show labels on inactive items */
  showInactiveLabels?: boolean;
}

interface BottomNavItem {
  /** Icon (active state — filled) */
  icon: ReactNode;
  /** Icon for inactive state (outlined) — optional */
  iconInactive?: ReactNode;
  /** Label text */
  label: string;
  /** Badge count (0 = dot only, >0 = number) */
  badge?: number;
  /** Href for navigation */
  href?: string;
}

7. Accessibility (a11y)

  • Semantic HTML: <nav role="navigation" aria-label="Main navigation">
  • Items: <a> tags if href, <button> if onClick. Each has aria-current="page" when active.
  • Labels: Always in DOM (can be aria-label if visually hidden)
  • Badge: aria-label includes badge count: "Home, 3 notifications"
  • Focus: :focus-visible ring on each nav item
  • Keyboard: Tab to navigate between items, Enter/Space to activate
  • Reduced motion: @media (prefers-reduced-motion: reduce) — disable spring animations
  • Touch target: Min 44×44px per item (WCAG 2.2 Level AA)
  • Contrast: Active/inactive icons must have ≥ 3:1 contrast ratio

8. Best Practices & Rules

  • 3-5 items ONLY: < 3 → use tabs; > 5 → use drawer
  • Không Hardcode: Mọi spacing, radius, color từ Token — trừ glass backdrop-filter
  • Safe area: Always pad bottom by env(safe-area-inset-bottom) or --safe-area-bottom
  • No scroll inside nav: Items cố định, không horizontal scroll
  • Label length: Max 1 từ (vd: "Home", "Search", "More")
  • Glass variant: Floating, inset from edges, rounded — chỉ dùng khi style = liquid/glass
  • Active indicator: chip shape cho default/glass, bold text cho minimal
  • Re-select: Tapping active item → scroll to top behavior (implement in JS)
  • Dark Mode: Tự động qua Mode tokens — glass bg đổi opacity

8.1. Mobile App Rules (B8, B12)

Rule ID Requirement
No Tooltip B8 .tooltip { display: none; }. Mobile không có hover — tooltip bị clip bởi container tạo dark line artifact.
Safe Area Token B12 padding-bottom PHẢI dùng var(--local-*-safe-area-bottom). KHÔNG hardcode px. Phone frame có rounded corners + home indicator.

8.2. Indicator chip Positioning

Khi HTML có indicator là sibling (không phải parent) của icon:

html
<button class="ground-bottomnav__item">
    <span class="ground-bottomnav__indicator"></span>  <!-- sibling -->
    <span class="ground-bottomnav__icon">...</span>
    <span class="ground-bottomnav__label">Home</span>
</button>

Indicator PHẢI dùng position: absolute để không chiếm flex space, tránh đội cell active cao hơn inactive:

css
.ground-bottomnav__item { position: relative; }
.ground-bottomnav__indicator {
    position: absolute; top: 0; left: 50%;
    transform: translateX(-50%);
    width: 64px; height: 32px;
    pointer-events: none;
}

9. Example Usage

jsx
{/* Default (Material style) */}
<BottomNav
  variant="default"
  items={[
    { icon: <HomeIcon />, label: "Home", badge: 3 },
    { icon: <SearchIcon />, label: "Search" },
    { icon: <WalletIcon />, label: "Wallet" },
    { icon: <ProfileIcon />, label: "Profile" },
  ]}
  activeIndex={0}
  onSelect={handleNav}
/>

{/* Glass (iOS 26 Liquid Glass) */}
<BottomNav
  variant="glass"
  items={items}
  activeIndex={activeIndex}
  onSelect={handleNav}
  collapseOnScroll
/>

{/* Minimal (Samsung OneUI style) */}
<BottomNav
  variant="minimal"
  showInactiveLabels={false}
  items={items}
  activeIndex={1}
  onSelect={handleNav}
/>

10. CSS Skeleton

css
/* Container — Ground layer */
.ground-bottomnav {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: var(--z-index-fixed);
  background: var(--background);
  border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom, var(--spacing-8-5));
}

/* Glass variant — floating capsule (Figma-aligned) */
.ground-bottomnav--glass {
  position: fixed;
  bottom: var(--spacing-2);
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface-subtle);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--card-border);
  border-radius: var(--comp-radius-capsule); /* 9999px — full chip */
  border-top: none; /* no top separator — floating */
  padding: var(--spacing-2);
  gap: var(--spacing-2);
}

/* Glass active item — full chip wrapping icon+label */
.ground-bottomnav--glass .ground-bottomnav__item--active {
  background: var(--input);
  border: 1px solid var(--border-ghost);
  border-radius: var(--comp-radius-capsule);
  color: var(--foreground);
  min-width: 60px;
  padding: var(--spacing-2);
}

/* Glass inactive items */
.ground-bottomnav--glass .ground-bottomnav__item {
  color: var(--muted-foreground);
  min-width: 60px;
  padding: var(--spacing-2);
}

/* Glass icon size — 20px per Figma */
.ground-bottomnav--glass .ground-bottomnav__icon {
  width: 20px;
  height: 20px;
}

/* Glass: no separate indicator chip — active item IS the chip */
.ground-bottomnav--glass .ground-bottomnav__indicator::before {
  display: none;
}

/* Nav items container */
.ground-bottomnav__items {
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: var(--spacing-20, 80px); /* MD3 Expressive: 80dp */
  padding: 0 var(--spacing-2);
}

/* Individual item */
.ground-bottomnav__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-1); /* S2: 4px base unit */
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--spacing-3); /* horizontal only — flex centers vertically */
  border: none;
  background: none;
  color: var(--muted-foreground);
  cursor: pointer;
  position: relative;
  transition: color var(--duration-fast-2) var(--easing-standard);
}

/* Active state */
.ground-bottomnav__item--active {
  color: var(--primary);
}

/* Active indicator chip — 64×32 per MD3 Expressive */
.ground-bottomnav__indicator {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 32px;
}

.ground-bottomnav__item--active .ground-bottomnav__indicator::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--primary-muted);
  color: var(--primary); /* bg/fg pair */
  border-radius: var(--radius-full);
  transition: all var(--duration-slow-1) var(--easing-spring);
}

/* Icon */
.ground-bottomnav__icon {
  position: relative; /* above chip */
  width: 20px; /* unified icon size */
  height: 20px;
}

/* Label — Caption tier (T3 + §6.1) */
.ground-bottomnav__label {
  font-size: var(--font-size-xs); /* 12px — Caption tier (11-12px) */
  font-weight: var(--font-weight-medium);
  line-height: var(--font-leading-normal, 1.5); /* T3: caption = 1.4-1.5 */
  letter-spacing: var(--font-tracking-wider);
}

/* Badge */
.ground-bottomnav__badge {
  position: absolute;
  top: 0;
  right: var(--spacing-1);
  min-width: var(--spacing-4);
  height: var(--spacing-4);
  border-radius: var(--radius-full);
  background: var(--destructive);
  color: var(--destructive-foreground); /* bg/fg pair */
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-bold);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--spacing-1);
}

/* Minimal variant — no chip, bold active */
.ground-bottomnav--minimal .ground-bottomnav__indicator::before {
  display: none;
}
.ground-bottomnav--minimal .ground-bottomnav__item--active {
  color: var(--foreground);
  font-weight: var(--font-weight-bold);
}

/* Reduced motion — M1 rule */
@media (prefers-reduced-motion: reduce) {
  .ground-bottomnav__item,
  .ground-bottomnav__indicator::before {
    transition: none;
  }
}