@prmichaelsen/acp-visualizer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/agent/commands/acp.clarification-address.md +417 -0
- package/agent/commands/acp.clarification-capture.md +386 -0
- package/agent/commands/acp.clarification-create.md +437 -0
- package/agent/commands/acp.clarifications-research.md +326 -0
- package/agent/commands/acp.command-create.md +432 -0
- package/agent/commands/acp.design-create.md +286 -0
- package/agent/commands/acp.design-reference.md +355 -0
- package/agent/commands/acp.handoff.md +270 -0
- package/agent/commands/acp.index.md +423 -0
- package/agent/commands/acp.init.md +546 -0
- package/agent/commands/acp.package-create.md +895 -0
- package/agent/commands/acp.package-info.md +212 -0
- package/agent/commands/acp.package-install.md +539 -0
- package/agent/commands/acp.package-list.md +280 -0
- package/agent/commands/acp.package-publish.md +541 -0
- package/agent/commands/acp.package-remove.md +293 -0
- package/agent/commands/acp.package-search.md +307 -0
- package/agent/commands/acp.package-update.md +361 -0
- package/agent/commands/acp.package-validate.md +540 -0
- package/agent/commands/acp.pattern-create.md +386 -0
- package/agent/commands/acp.plan.md +587 -0
- package/agent/commands/acp.proceed.md +882 -0
- package/agent/commands/acp.project-create.md +675 -0
- package/agent/commands/acp.project-info.md +312 -0
- package/agent/commands/acp.project-list.md +226 -0
- package/agent/commands/acp.project-remove.md +379 -0
- package/agent/commands/acp.project-set.md +227 -0
- package/agent/commands/acp.project-update.md +307 -0
- package/agent/commands/acp.projects-restore.md +228 -0
- package/agent/commands/acp.projects-sync.md +347 -0
- package/agent/commands/acp.report.md +407 -0
- package/agent/commands/acp.resume.md +239 -0
- package/agent/commands/acp.sessions.md +301 -0
- package/agent/commands/acp.status.md +293 -0
- package/agent/commands/acp.sync.md +364 -0
- package/agent/commands/acp.task-create.md +500 -0
- package/agent/commands/acp.update.md +302 -0
- package/agent/commands/acp.validate.md +466 -0
- package/agent/commands/acp.version-check-for-updates.md +276 -0
- package/agent/commands/acp.version-check.md +191 -0
- package/agent/commands/acp.version-update.md +289 -0
- package/agent/commands/command.template.md +339 -0
- package/agent/commands/git.commit.md +526 -0
- package/agent/commands/git.init.md +514 -0
- package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
- package/agent/commands/tanstack-cloudflare.tail.md +275 -0
- package/agent/design/.gitkeep +0 -0
- package/agent/design/design.template.md +154 -0
- package/agent/design/local.dashboard-layout-routing.md +288 -0
- package/agent/design/local.data-model-yaml-parsing.md +310 -0
- package/agent/design/local.search-filtering.md +331 -0
- package/agent/design/local.server-api-auto-refresh.md +235 -0
- package/agent/design/local.table-tree-views.md +299 -0
- package/agent/design/local.visualizer-requirements.md +349 -0
- package/agent/design/requirements.template.md +387 -0
- package/agent/index/.gitkeep +0 -0
- package/agent/index/acp.core.yaml +137 -0
- package/agent/index/local.main.template.yaml +37 -0
- package/agent/manifest.template.yaml +13 -0
- package/agent/manifest.yaml +302 -0
- package/agent/milestones/.gitkeep +0 -0
- package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
- package/agent/milestones/milestone-1-{title}.template.md +206 -0
- package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
- package/agent/package.template.yaml +86 -0
- package/agent/patterns/.gitkeep +0 -0
- package/agent/patterns/bootstrap.template.md +1237 -0
- package/agent/patterns/pattern.template.md +382 -0
- package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
- package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
- package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
- package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
- package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
- package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
- package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
- package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
- package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
- package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
- package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
- package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
- package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
- package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
- package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
- package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
- package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
- package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
- package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
- package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
- package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
- package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
- package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
- package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
- package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
- package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
- package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
- package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
- package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
- package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
- package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
- package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
- package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
- package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
- package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
- package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
- package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
- package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
- package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
- package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
- package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
- package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
- package/agent/progress.template.yaml +161 -0
- package/agent/progress.yaml +145 -0
- package/agent/schemas/package.schema.yaml +276 -0
- package/agent/scripts/acp.common.sh +1781 -0
- package/agent/scripts/acp.install.sh +333 -0
- package/agent/scripts/acp.package-create.sh +924 -0
- package/agent/scripts/acp.package-info.sh +288 -0
- package/agent/scripts/acp.package-install.sh +893 -0
- package/agent/scripts/acp.package-list.sh +311 -0
- package/agent/scripts/acp.package-publish.sh +420 -0
- package/agent/scripts/acp.package-remove.sh +348 -0
- package/agent/scripts/acp.package-search.sh +156 -0
- package/agent/scripts/acp.package-update.sh +517 -0
- package/agent/scripts/acp.package-validate.sh +1018 -0
- package/agent/scripts/acp.uninstall.sh +85 -0
- package/agent/scripts/acp.version-check-for-updates.sh +98 -0
- package/agent/scripts/acp.version-check.sh +47 -0
- package/agent/scripts/acp.version-update.sh +176 -0
- package/agent/scripts/acp.yaml-parser.sh +985 -0
- package/agent/scripts/acp.yaml-validate.sh +205 -0
- package/agent/tasks/.gitkeep +0 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
- package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
- package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
- package/agent/tasks/task-1-{title}.template.md +244 -0
- package/bin/visualize.mjs +84 -0
- package/package.json +48 -0
- package/src/components/ExtraFieldsBadge.tsx +15 -0
- package/src/components/FilterBar.tsx +33 -0
- package/src/components/Header.tsx +23 -0
- package/src/components/MilestoneTable.tsx +167 -0
- package/src/components/MilestoneTree.tsx +84 -0
- package/src/components/ProgressBar.tsx +20 -0
- package/src/components/SearchInput.tsx +22 -0
- package/src/components/Sidebar.tsx +54 -0
- package/src/components/StatusBadge.tsx +23 -0
- package/src/components/StatusDot.tsx +12 -0
- package/src/components/TaskList.tsx +36 -0
- package/src/components/ViewToggle.tsx +31 -0
- package/src/lib/config.ts +8 -0
- package/src/lib/file-watcher.ts +43 -0
- package/src/lib/search.ts +48 -0
- package/src/lib/types.ts +73 -0
- package/src/lib/useAutoRefresh.ts +31 -0
- package/src/lib/useCollapse.ts +31 -0
- package/src/lib/useFilteredData.ts +55 -0
- package/src/lib/yaml-loader-real.spec.ts +47 -0
- package/src/lib/yaml-loader.spec.ts +201 -0
- package/src/lib/yaml-loader.ts +265 -0
- package/src/routeTree.gen.ts +140 -0
- package/src/router.tsx +10 -0
- package/src/routes/__root.tsx +75 -0
- package/src/routes/api/watch.ts +29 -0
- package/src/routes/index.tsx +115 -0
- package/src/routes/milestones.tsx +50 -0
- package/src/routes/search.tsx +84 -0
- package/src/routes/tasks.tsx +63 -0
- package/src/services/progress-database.service.ts +46 -0
- package/src/styles.css +25 -0
- package/tsconfig.json +24 -0
- package/vite.config.ts +16 -0
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Unified Header & Navigation
|
|
2
|
+
|
|
3
|
+
**Category**: Design
|
|
4
|
+
**Applicable To**: All page headers, tab navigation, mobile menus, and back navigation
|
|
5
|
+
**Status**: Stable
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The header system provides a fixed 56px top bar (`UnifiedHeader`) with optional sub-header tabs (`SubHeaderTabs`), inline filter tabs (`FilterTabs`), smart back navigation, mobile hamburger menu, and notification bell integration. All pages share the same header chrome via this system. Content below the header uses `pt-14` (which includes `env(safe-area-inset-top)` via CSS override).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## When to Use This Pattern
|
|
16
|
+
|
|
17
|
+
**Use this pattern when:**
|
|
18
|
+
- Creating any new page that needs a header bar
|
|
19
|
+
- Adding tab navigation to a page (sub-header or inline)
|
|
20
|
+
- Building a page with action buttons in the header
|
|
21
|
+
- Implementing a new mobile-responsive navigation flow
|
|
22
|
+
|
|
23
|
+
**Don't use this pattern when:**
|
|
24
|
+
- Building a full-screen overlay (lightbox, modal) — these have their own chrome
|
|
25
|
+
- Creating a landing/marketing page with custom header design
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Core Principles
|
|
30
|
+
|
|
31
|
+
1. **Fixed Position + Safe Area**: Header is `fixed top-0` with `paddingTop: env(safe-area-inset-top)` for notch support
|
|
32
|
+
2. **Content Offset**: All content below uses `pt-14` (56px + safe area via CSS override in `styles.css`)
|
|
33
|
+
3. **Two-Tier Structure**: Main bar (always) + optional sub-header children (tabs)
|
|
34
|
+
4. **Smart Back Button**: Shows when `title` is set; uses `router.history.back()` with `/` fallback
|
|
35
|
+
5. **Max-Width Container**: `max-w-3xl mx-auto` keeps content centered and readable
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Implementation
|
|
40
|
+
|
|
41
|
+
### UnifiedHeader
|
|
42
|
+
|
|
43
|
+
**File**: `src/components/UnifiedHeader.tsx`
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
interface UnifiedHeaderProps {
|
|
47
|
+
/** Page title. Omit for homepage mode (shows "agentbase" branding). */
|
|
48
|
+
title?: string
|
|
49
|
+
/** Icon displayed next to the title. */
|
|
50
|
+
icon?: ReactNode
|
|
51
|
+
/** When true, renders the icon as-is instead of wrapping in a gradient circle. */
|
|
52
|
+
iconRaw?: boolean
|
|
53
|
+
/** Callback for the ellipsis (⋮) button. Page owns the panel/menu. */
|
|
54
|
+
onEllipsisPress?: () => void
|
|
55
|
+
/** Action buttons rendered directly in the header bar. */
|
|
56
|
+
headerActions?: ReactNode
|
|
57
|
+
/** SubHeaderTabs or other content rendered below the main bar. */
|
|
58
|
+
children?: ReactNode
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Structure**:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
<header fixed top-0 z-50 w-full>
|
|
66
|
+
├─ <div h-14 border-b> ← Main bar (56px)
|
|
67
|
+
│ └─ <div max-w-3xl mx-auto>
|
|
68
|
+
│ ├─ [Back button + separator] ← Only when title is set
|
|
69
|
+
│ ├─ [Icon] + [Title] ← Or branding if no title
|
|
70
|
+
│ ├─ <flex-1 spacer />
|
|
71
|
+
│ ├─ [headerActions] ← Custom action buttons
|
|
72
|
+
│ ├─ [Ellipsis button] ← If onEllipsisPress provided
|
|
73
|
+
│ ├─ [NotificationBell] ← If real user
|
|
74
|
+
│ └─ [Hamburger menu button]
|
|
75
|
+
└─ {children} ← SubHeaderTabs go here
|
|
76
|
+
|
|
77
|
+
{mobileMenuOpen && <MobileMenu />} ← Dropdown below header
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Exported Constants**:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
export const HEADER_HEIGHT_CLASS = 'pt-14' // Padding for content below header
|
|
84
|
+
export const HEADER_TOP_CLASS = 'top-14' // Top offset for fixed elements below header
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Back Navigation Logic**:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const handleBack = () => {
|
|
91
|
+
if (window.history.length > 1) {
|
|
92
|
+
router.history.back()
|
|
93
|
+
} else {
|
|
94
|
+
window.location.href = '/'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Back button only renders when title is set (non-homepage)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Mobile Menu**: Fixed dropdown below header (`top-14`) with:
|
|
101
|
+
- Navigation links (Chat, Conversations, Memories, etc.)
|
|
102
|
+
- Collapsible "Social" subsection with ChevronDown rotation
|
|
103
|
+
- Auth section: loading skeleton / user email + logout / login link
|
|
104
|
+
- Auto-closes on link click
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### SubHeaderTabs
|
|
109
|
+
|
|
110
|
+
**File**: `src/components/SubHeaderTabs.tsx`
|
|
111
|
+
|
|
112
|
+
Rendered as `children` of UnifiedHeader, below the main bar.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
export interface SubHeaderTab {
|
|
116
|
+
id: string
|
|
117
|
+
label: string
|
|
118
|
+
icon?: ReactNode
|
|
119
|
+
variant?: 'default' | 'ghost'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface SubHeaderTabsProps {
|
|
123
|
+
tabs: SubHeaderTab[]
|
|
124
|
+
activeId: string
|
|
125
|
+
onSelect: (id: string) => void
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Behavior**:
|
|
130
|
+
- Horizontal scrollable with `-webkit-overflow-scrolling: touch` and hidden scrollbar
|
|
131
|
+
- Active tab: 2px bottom border (`border-purple-500`) + white/purple text
|
|
132
|
+
- Inactive tab: gray text with hover transition
|
|
133
|
+
- Buttons use `min-w-min` to prevent shrinking
|
|
134
|
+
|
|
135
|
+
**Usage**:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
<UnifiedHeader title="Organize">
|
|
139
|
+
<SubHeaderTabs
|
|
140
|
+
tabs={[
|
|
141
|
+
{ id: 'unorganized', label: 'Unorganized', icon: <FolderOpen className="w-4 h-4" /> },
|
|
142
|
+
{ id: 'relationships', label: 'Relationships', icon: <Waypoints className="w-4 h-4" /> },
|
|
143
|
+
]}
|
|
144
|
+
activeId={activeTab}
|
|
145
|
+
onSelect={setActiveTab}
|
|
146
|
+
/>
|
|
147
|
+
</UnifiedHeader>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Content Offset with Tabs**: Use `pt-header-tabs` (6rem + safe area) instead of `pt-14` when SubHeaderTabs are present.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### FilterTabs
|
|
155
|
+
|
|
156
|
+
**File**: `src/components/feed/FilterTabs.tsx`
|
|
157
|
+
|
|
158
|
+
Inline pill-style filter controls rendered within page content (not in header).
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
export interface FilterTab {
|
|
162
|
+
id: string
|
|
163
|
+
label: string
|
|
164
|
+
icon?: ReactNode
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
interface FilterTabsProps {
|
|
168
|
+
tabs: FilterTab[]
|
|
169
|
+
activeId: string
|
|
170
|
+
onSelect: (id: string) => void
|
|
171
|
+
hidden?: boolean
|
|
172
|
+
className?: string
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Behavior**:
|
|
177
|
+
- Container: `flex gap-1 mb-4 p-1 bg-gray-800/50 rounded-lg overflow-x-auto`
|
|
178
|
+
- Active button: gradient background (`from-purple-600 to-blue-600`), white text, shadow
|
|
179
|
+
- Inactive button: gray text, hover effects
|
|
180
|
+
- Can be hidden with `hidden` prop
|
|
181
|
+
- `whitespace-nowrap` on buttons prevents text wrapping
|
|
182
|
+
|
|
183
|
+
**Usage**:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
<FilterTabs
|
|
187
|
+
tabs={[
|
|
188
|
+
{ id: 'all', label: 'All', icon: <Brain className="w-3.5 h-3.5" /> },
|
|
189
|
+
{ id: 'agent', label: 'Agent', icon: <Bot className="w-3.5 h-3.5" /> },
|
|
190
|
+
]}
|
|
191
|
+
activeId={contentView}
|
|
192
|
+
onSelect={setContentView}
|
|
193
|
+
/>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### Layout Routes with Outlet
|
|
199
|
+
|
|
200
|
+
**Pattern**: Keep feeds mounted while showing detail views via TanStack Router layout routes.
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
/memories.tsx ← Layout with feed + <Outlet />
|
|
204
|
+
/memories/index.tsx ← Feed content (hidden when detail showing)
|
|
205
|
+
/memories/$memoryId.tsx ← Detail view (renders via Outlet)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
function MemoriesLayout() {
|
|
210
|
+
const indexMatch = useMatch({ from: '/memories/', shouldThrow: false })
|
|
211
|
+
const showingDetail = !indexMatch
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<>
|
|
215
|
+
{/* Feed — hidden via CSS, stays mounted to preserve state */}
|
|
216
|
+
<div style={{ display: showingDetail ? 'none' : undefined }}>
|
|
217
|
+
<UnifiedHeader title="Memories">
|
|
218
|
+
<SubHeaderTabs ... />
|
|
219
|
+
</UnifiedHeader>
|
|
220
|
+
<main className="pt-header-tabs">
|
|
221
|
+
{/* Feed content */}
|
|
222
|
+
</main>
|
|
223
|
+
</div>
|
|
224
|
+
{/* Detail — renders when child route matches */}
|
|
225
|
+
{showingDetail && <Outlet />}
|
|
226
|
+
</>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Key**: Use `display: none` (not unmount) to preserve scroll position, loaded items, and filter state.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Anti-Patterns
|
|
236
|
+
|
|
237
|
+
### Forgetting `pt-14` Below Fixed Header
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// Bad: Content renders behind the fixed header
|
|
241
|
+
<UnifiedHeader title="Page" />
|
|
242
|
+
<main>{content}</main>
|
|
243
|
+
|
|
244
|
+
// Good: Offset content below header
|
|
245
|
+
<UnifiedHeader title="Page" />
|
|
246
|
+
<main className="pt-14">{content}</main>
|
|
247
|
+
|
|
248
|
+
// Good (with tabs): Use pt-header-tabs
|
|
249
|
+
<UnifiedHeader title="Page"><SubHeaderTabs ... /></UnifiedHeader>
|
|
250
|
+
<main className="pt-header-tabs">{content}</main>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Using SubHeaderTabs Outside UnifiedHeader
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// Bad: Tabs render without proper fixed positioning
|
|
257
|
+
<UnifiedHeader title="Page" />
|
|
258
|
+
<SubHeaderTabs tabs={tabs} activeId={id} onSelect={fn} />
|
|
259
|
+
|
|
260
|
+
// Good: Pass as children to UnifiedHeader
|
|
261
|
+
<UnifiedHeader title="Page">
|
|
262
|
+
<SubHeaderTabs tabs={tabs} activeId={id} onSelect={fn} />
|
|
263
|
+
</UnifiedHeader>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Checklist
|
|
269
|
+
|
|
270
|
+
- [ ] `UnifiedHeader` used on every page (with or without title)
|
|
271
|
+
- [ ] `pt-14` applied to content container (or `pt-header-tabs` when tabs present)
|
|
272
|
+
- [ ] Tab state synced to URL via `validateSearch` + `navigate({ search })` for deep-linkability
|
|
273
|
+
- [ ] Mobile menu items auto-close on navigation
|
|
274
|
+
- [ ] Safe-area-inset-top applied via inline style on fixed header element
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
**Status**: Stable
|
|
279
|
+
**Last Updated**: 2026-03-14
|
|
280
|
+
**Contributors**: Community
|