Default
Transparent (over hero image)
Explore
Discover amazing places
Glass (iOS 26 Liquid Glass)
Glass app bar floats over content
---
description: Hướng dẫn Agent tự động tạo UI Component App Bar — thanh điều hướng cố định ở đầu màn hình mobile app. Tích hợp trends iOS 26 Liquid Glass, Material Design 3 Expressive, Samsung OneUI 7.
App Bar Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) tạo component App Bar — thanh tiêu đề + điều hướng ở đầu mỗi screen trong mobile app.
1. Mục tiêu (Objective)
Tạo mobile app bar (top bar) hỗ trợ back navigation, title, actions, collapsing large title, và tương thích iOS 26 Liquid Glass floating bars + Material Design 3 Expressive flexible app bars + Samsung OneUI 7 minimal headers.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng App Bar?
- Screen-level header: mỗi màn hình cần tiêu đề + back button
- Contextual actions: 1-3 action icons (search, menu, share...)
- Mobile-first: compact height, touch-friendly
Phân biệt với component khác
| Tình huống |
Component đúng |
Lý do |
| Mobile screen header (back + title + actions) |
App Bar |
Compact, screen-level |
| Desktop horizontal nav (logo + links) |
navigation-menu |
Wide, site-level |
| Bottom mobile navigation |
Bottom Navigation |
Navigation ≠ header |
| Search-only header |
Search Bar |
Single purpose |
| Section header within page |
Section title (typography) |
In-flow, no sticky |
Decision Tree cho AI
Cần header cho mobile screen?
├─ Screen title + back button → App Bar
│ ├─ Fixed trên hero image → variant="transparent"
│ ├─ Medium title + subtitle → variant="medium"
│ ├─ Large title collapse on scroll → variant="large"
│ ├─ Standard solid background → variant="default"
│ ├─ Glass/translucent → variant="glass"
│ └─ Search-focused → variant="search"
│ (Small / Medium có thể showTitle={false} khi context đủ rõ)
├─ Web desktop navigation → navigation-menu
├─ No title, just actions → Toolbar
└─ In-flow section heading → Typography heading
3. Platform Trend Research (2025-2026)
3.1. iOS 26 — Liquid Glass Navigation Bar
| Feature |
Spec |
| Material |
Liquid Glass — translucent, floating |
| Height |
44pt (navigation bar) + status bar (44-59pt) |
| Title alignment |
Center-aligned (default), Large title (left, collapses) |
| Large title |
Starts large (34pt), scrolls into inline (17pt) |
| Back button |
Chevron ‹ + previous screen title (auto) |
| Glass effect |
.glassEffect() modifier, floats over content |
| Bar buttons |
Glass chip shape, interactive hover/press |
| Grouped bars |
Nav bar + toolbar morph into glass bubbles |
| Search bar |
Can embed in large title area, morphs on activation |
| Transition |
Glass elements reshape contextually between screens |
3.2. Material Design 3 Expressive — App Bar
| Feature |
Spec |
| Name |
"App bar" (simplified from "Top app bar") |
| Variants |
Small, Medium flexible, Large flexible, Search |
| Small |
64dp height, title + navigation icon + actions |
| Small (no title) |
Title hidden — chỉ nav icon + actions, dùng khi context đủ rõ |
| Medium flexible |
112dp, larger title + subtitle, collapsible |
| Medium flexible (no title) |
Title hidden — chỉ giữ nav + actions, content area trống |
| Large flexible |
Prominent title, imagery support, filled buttons |
| Search variant |
Search field integrated, icons inside/outside |
| Title on/off |
Small & Medium có thể ẩn title tùy use case (e.g. map, media viewer) |
| Title align |
Left (default) or center |
| Colors |
Surface container, on-surface text |
| Elevation |
Scroll: 0→3dp on scroll for shadow |
| Actions |
1-2 action icons recommended |
| Deprecated |
Old "medium" and "large" → use "flexible" versions |
3.3. Samsung OneUI 7
| Feature |
Spec |
| Style |
Clean, minimal, subtle radius |
| Large title |
Bold, left-aligned, collapses on scroll |
| Back button |
Simple arrow ←, no label |
| Actions |
Icon-only, right-aligned |
| Search |
Bottom-positioned search in some system apps |
| Glass preview (8.5) |
Transparency effects, frosted panels |
| Color |
Follows system dynamic color, subtle gradients |
3.4. Cross-platform Convergence
| Feature |
iOS 26 |
MD3 Exp |
OneUI 7 |
| Height (compact) |
44pt |
64dp |
56dp |
| Large title |
✅ collapses |
✅ flexible |
✅ collapses |
| Glass/transparent |
✅ |
❌ |
✅ (preview) |
| Center title |
✅ default |
✅ option |
❌ |
| Subtitle |
❌ |
✅ (flexible) |
❌ |
| Embedded search |
✅ (large area) |
✅ (search variant) |
✅ |
| Back label |
✅ auto |
❌ |
❌ |
4. Ngữ nghĩa & Phân loại (Semantics)
4.1. Visual Variants
| Variant |
Mô tả |
Use case |
Platform feel |
default |
Solid background, shadow on scroll (= MD3 Small) |
Standard screens |
MD3 / cross-platform |
medium |
Taller bar, larger title + subtitle (= MD3 Medium flexible) |
Detail screens, section landing |
MD3 Expressive |
transparent |
No background, content behind |
Hero image overlay |
Cross-platform |
glass |
Liquid glass, floating, translucent |
Premium / iOS-like |
iOS 26 |
large |
Large title, collapses on scroll (= MD3 Large flexible) |
Top-level screens |
iOS + OneUI + MD3 |
search |
Morphs title into search field |
Search-focused screens |
MD3 Expressive |
Title on/off: default (Small) và medium hỗ trợ showTitle={false} — ẩn title khi context đã đủ rõ (map view, media player, camera). Actions vẫn hiển thị.
4.2. Sub-components / Slots
| Part |
Mô tả |
Tokens chính |
| Container |
Full-width, fixed/sticky top |
--background / transparent |
| Leading |
Back button / menu icon |
--foreground, 44×44 touch |
| Title |
Screen title text |
heading-4 style |
| Large Title |
Large scrollable title |
heading-1 / display-small |
| Subtitle |
Secondary text below title |
body-small, --muted-foreground |
| Actions |
1-3 action icon buttons |
--foreground, 44×44 touch |
| Search Field |
Expandable search input |
Reuses search component |
4.3. Slot Map (Figma ↔ Code)
📎 Source: slot-manifest.json → app-bar · Layer: ground
| Figma Slot |
data-slot |
CSS Class |
Required |
Accepts |
| Root |
app-bar |
.ground-appbar |
✅ |
— |
| Leading |
app-bar-leading |
.ground-appbar__leading |
❌ |
back-button, menu-button, icon-button |
| Title |
app-bar-title |
.ground-appbar__title |
✅ |
text, search-field |
| Subtitle |
app-bar-subtitle |
.ground-appbar__subtitle |
❌ |
text |
| Actions |
app-bar-actions |
.ground-appbar__actions |
❌ |
icon-button-group (max 3) |
<!-- HTML skeleton with data-slot -->
<header class="ground-appbar" data-slot="app-bar" role="banner">
<div class="ground-appbar__leading" data-slot="app-bar-leading">
<button aria-label="Go back">←</button>
</div>
<h1 class="ground-appbar__title" data-slot="app-bar-title">Title</h1>
<div class="ground-appbar__actions" data-slot="app-bar-actions">
<button aria-label="More">⋯</button>
</div>
</header>
4.4. Interactive States
| State |
Visual |
Token |
| Resting |
Background transparent or solid |
--background |
| Scrolled |
Shadow appears, title shrinks (large) |
--shadow-sm |
| Back pressed |
Scale 0.92, navigate back |
transform: scale(0.92) |
| Action hover |
Subtle bg circle |
--state-layer-hover |
| Action focus |
Focus ring |
--primary outline |
| Search active |
Title morphs into input field |
--input, --input-border |
4.4. Behaviors
| Behavior |
Description |
| Sticky |
Stays at top during scroll (default) |
| Collapse |
Large title shrinks to inline on scroll |
| Elevation on scroll |
Shadow appears after scrolling past threshold |
| Back navigation |
Leading button triggers page pop / router back |
| Search morph |
Title area transforms into search input (search variant) |
| Status bar |
Content positioned below status bar (safe area) |
| Glass morph |
Glass shape adapts during screen transitions (glass variant) |
5. Elevation Layer Classification
Kết luận: App Bar = Ground layer — không phải Surface.
Tại sao Ground?
App Bar là page chrome — cấu trúc cố định của trang, luôn hiển thị, không dismissable.
| Tiêu chí |
Ground |
Surface |
App Bar |
| Luôn hiển thị? |
✅ |
❌ (tạm thời) |
✅ → Ground |
| Overlay content? |
❌ |
✅ |
❌ → Ground |
| Dismissable? |
❌ |
✅ |
❌ → Ground |
| Shadow mặc định? |
None |
--shadow-lg |
None → Ground |
Scroll Elevation — nâng elevation nhưng không đổi layer
Khi scroll, App Bar thêm --shadow-sm để báo hiệu content bên dưới bị che. Đây là visual feedback, không phải thay đổi layer.
| State |
Background |
Shadow |
Layer |
| Resting (chưa scroll) |
--background |
None |
Ground |
| Scrolled (floating) |
--background |
--shadow-sm |
Ground + modifier |
MD3 xử lý tương tự: elevation 0dp → 3dp khi scroll, nhưng app bar vẫn ở "Surface container" role (= Ground trong Prisma).
⚡ Quy tắc nhanh: Luôn hiển thị + không dismiss = Ground. Shadow on scroll = elevation modifier, không đổi layer.
Cross-platform evidence
| Platform |
Token/Role |
Prisma mapping |
| MD3 Expressive |
Surface container, elevation 0→3dp |
Ground + --shadow-sm on scroll |
| Apple HIG (iOS 26) |
Navigation bar, opaque/glass |
Ground (glass = decorator) |
| Samsung OneUI 7 |
System header, no elevation |
Ground |
6. Token Mapping
📦 Atomic Mapping: See ATOMIC-MAPPING.md for complete token spec.
📦 Atomic Mapping: UI Layer: Ground, Density Tier: — (fixed height)
Transparent variant overlays content → fg may need --foreground-inverse on dark heroes.
| Property |
Token |
Ghi chú |
| Container bg |
--background |
default variant |
| Container bg (transparent) |
transparent |
Hero overlay |
| Container bg (glass) |
--background + opacity + backdrop blur |
Glass variant (Ground layer, decorative glass) |
| Container fg |
--foreground |
|
| Container fg (transparent on dark) |
--foreground-inverse |
White on hero |
| Border bottom |
--border |
Optional, appears on scroll |
| Shadow on scroll |
--shadow-sm |
Subtle elevation |
| Title font |
heading-4 text style |
18px, semibold |
| Large title font |
heading-1 text style |
30px, bold |
| Subtitle font |
body-small text style |
14px, normal |
| Subtitle fg |
--muted-foreground |
|
| Back icon size |
--icon-lg (24px) |
|
| Action icon size |
--icon-lg (24px) |
|
| Touch target |
44×44px min |
--size-min-width-11 |
| Button bg (ghost) |
transparent |
|
| Button hover |
--state-layer-hover |
|
| Height (compact) |
44-56px |
--spacing-11 to --spacing-14 |
| Status bar offset |
env(safe-area-inset-top) |
Or --safe-area-top |
| Inline padding |
--spacing-4 (16px) |
--block-padding-small |
| Action gap |
--item-inline-gap-default (8px) |
|
| Transition |
--motion-smooth |
Title collapse |
| z-index |
--z-index-fixed |
Above content |
Glass variant tokens
| Property |
Value |
Note |
| Backdrop |
blur(20px) saturate(160%) |
Decorative OK |
| Glass border |
1px solid var(--border) |
Token-based |
| Floating margin |
var(--spacing-2) var(--spacing-4) 0 |
Inset |
| Floating radius |
--radius-2xl (24px) |
Rounded |
6. Props & API
interface AppBarProps {
/** Visual variant */
variant?: 'default' | 'medium' | 'transparent' | 'glass' | 'large' | 'search';
/** Screen title */
title: string;
/** Show/hide title — default: true. Set false for map, media, camera screens */
showTitle?: boolean;
/** Subtitle (medium, large variants) */
subtitle?: string;
/** Show back button */
showBack?: boolean;
/** Back button label (iOS style — default: hidden) */
backLabel?: string;
/** Back handler */
onBack?: () => void;
/** Action buttons (max 3) */
actions?: AppBarAction[];
/** Add shadow on scroll */
elevateOnScroll?: boolean;
/** Collapse large title on scroll */
collapseOnScroll?: boolean;
}
interface AppBarAction {
/** Icon element */
icon: ReactNode;
/** Accessible label */
label: string;
/** Click handler */
onClick: () => void;
/** Badge count */
badge?: number;
}
7. Accessibility (a11y)
- Semantic HTML:
<header role="banner"> containing <nav> for actions
- Title: Use
<h1> for page title inside app bar (primary heading)
- Back button:
<button aria-label="Go back"> or <a> with aria-label
- Actions: Each
<button> has descriptive aria-label (e.g., "Open search", "More options")
- Focus:
:focus-visible ring on all interactive elements
- Keyboard: Tab through back → title (if focusable) → actions. Escape = go back.
- Reduced motion:
@media (prefers-reduced-motion) — disable collapse animation
- Touch target: Min 44×44px for back + actions (WCAG 2.2)
- Contrast: Title and icons must have ≥ 4.5:1 contrast (transparent variant: check on hero bg!)
- Landmark: App bar should be a navigation landmark
8. Best Practices & Rules
- Max 3 actions: More → hide behind "more" menu (dropdown-menu)
- Title clarity: Short, descriptive. Max ~20 characters.
- Không Hardcode: colors, spacing, typography từ tokens
- Safe area: Always offset by status bar height
- Transparent variant: Ensure readable contrast — may need text shadow or gradient overlay
- Large title: Only for top-level screens, not detail screens
- Glass variant: Only with liquid/glass style showcase — never mix with flat styles
- Dark Mode: transparent variant may need inverted colors
- Search morph: Only search variant — don't add search to all bars
9. Example Usage
{/* Default app bar */}
<AppBar
variant="default"
title="Flight"
showBack
onBack={() => router.back()}
actions={[
{ icon: <MoreIcon />, label: "More options", onClick: openMenu }
]}
elevateOnScroll
/>
{/* Transparent over hero */}
<AppBar
variant="transparent"
title="Sydney"
showBack={false}
actions={[
{ icon: <BellIcon />, label: "Notifications", onClick: openNotifs, badge: 2 },
{ icon: <ProfileIcon />, label: "Profile", onClick: openProfile }
]}
/>
{/* Large title (iOS/OneUI style) */}
<AppBar
variant="large"
title="Boarding Pass"
showBack
onBack={goBack}
collapseOnScroll
/>
{/* Glass (Liquid Glass) */}
<AppBar
variant="glass"
title="Search"
actions={actions}
/>
10. CSS Skeleton
/* Container — Ground layer */
.ground-appbar {
position: fixed;
top: 0; left: 0; right: 0;
z-index: var(--z-index-fixed);
background: var(--background);
padding-top: env(safe-area-inset-top, var(--spacing-11));
transition: box-shadow var(--duration-normal-1) var(--easing-standard);
}
/* Scrolled — shadow appears */
.ground-appbar--scrolled {
box-shadow: var(--shadow-sm);
}
/* Inner layout */
.ground-appbar__inner {
display: flex;
align-items: center;
height: var(--spacing-11); /* 44px compact */
padding: 0 var(--spacing-4);
gap: var(--spacing-2);
}
/* Back button */
.ground-appbar__back {
width: var(--size-min-width-11); /* 44px touch */
height: var(--size-min-width-11);
border-radius: var(--radius-full);
background: none;
border: none;
color: var(--foreground);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin-left: calc(-1 * var(--spacing-2));
transition: background var(--duration-fast-2) var(--easing-standard);
}
.ground-appbar__back:hover {
background: var(--state-layer-hover);
}
/* Title */
.ground-appbar__title {
flex: 1;
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--font-tracking-snug);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
color: var(--foreground);
}
/* Actions */
.ground-appbar__actions {
display: flex;
align-items: center;
gap: var(--spacing-0-5);
}
.ground-appbar__action {
width: var(--size-min-width-11);
height: var(--size-min-width-11);
border-radius: var(--radius-full);
background: none;
border: none;
color: var(--foreground);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
transition: background var(--duration-fast-2) var(--easing-standard);
}
.ground-appbar__action:hover {
background: var(--state-layer-hover);
}
/* ─── Transparent variant ─── */
.ground-appbar--transparent {
background: transparent;
color: var(--foreground-inverse, #fff);
}
.ground-appbar--transparent .ground-appbar__title,
.ground-appbar--transparent .ground-appbar__back,
.ground-appbar--transparent .ground-appbar__action {
color: var(--foreground-inverse, #fff);
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
/* ─── Medium flexible variant ─── */
.ground-appbar--medium {
/* Taller container for medium flexible */
}
.ground-appbar--medium .ground-appbar__inner {
height: auto;
flex-direction: column;
align-items: flex-start;
padding: var(--spacing-3) var(--spacing-4);
}
.ground-appbar--medium .ground-appbar__row {
display: flex;
align-items: center;
width: 100%;
height: var(--spacing-11); /* 44px nav row */
gap: var(--spacing-2);
}
.ground-appbar--medium .ground-appbar__title {
font-size: var(--font-size-xl);
font-weight: var(--font-weight-semibold);
text-align: left;
padding: var(--spacing-1) 0;
}
.ground-appbar--medium .ground-appbar__subtitle {
font-size: var(--font-size-sm);
color: var(--muted-foreground);
line-height: var(--leading-snug);
}
/* ─── Title hidden (Small / Medium) ─── */
.ground-appbar--no-title .ground-appbar__title,
.ground-appbar--no-title .ground-appbar__subtitle {
display: none;
}
/* ─── Glass variant ─── */
.ground-appbar--glass {
background: color-mix(in srgb, var(--background) 70%, transparent);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
border: 1px solid var(--border);
margin: var(--spacing-2) var(--spacing-4) 0;
border-radius: var(--radius-2xl);
}
/* ─── Large title variant ─── */
.ground-appbar--large .ground-appbar__large-title {
padding: var(--spacing-2) var(--spacing-5);
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
letter-spacing: var(--font-tracking-tight);
color: var(--foreground);
transition: all var(--duration-normal-2) var(--easing-standard);
}
.ground-appbar--large.ground-appbar--collapsed .ground-appbar__large-title {
height: 0;
padding: 0;
opacity: 0;
overflow: hidden;
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.ground-appbar,
.ground-appbar__back,
.ground-appbar__action,
.ground-appbar--large .ground-appbar__large-title {
transition: none;
}
}