Large Title Collapse Header
iOS 式大標題頁首,捲動時以共享元素縮移為置中小標題,下拉以橡皮筋回彈拉出搜尋列,區段標題依序吸附在頁首之下。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
44
52
<LargeTitleCollapseHeader />
安裝
npx shadcn@latest add https://webberui.com/r/large-title-collapse-header.json安裝依賴後,從 registry JSON(/r/large-title-collapse-header.json 的 files[0].content)複製 large-title-collapse-header.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
元件本身即是捲動容器——在 className 設定高度,以 LargeTitleSection 包住每個區段。捲動時大標題縮移為導覽列的置中小標題,區段標題依序吸附在導覽列之下:
import {
LargeTitleCollapseHeader,
LargeTitleSection,
} from "@/components/ui/large-title-collapse-header";
<LargeTitleCollapseHeader title="設定" search className="h-[520px]">
<LargeTitleSection title="常用">
<ul>…</ul>
</LargeTitleSection>
<LargeTitleSection title="一般">
<ul>…</ul>
</LargeTitleSection>
</LargeTitleCollapseHeader>啟用 search 後,觸控裝置可於頁面頂端下拉、以橡皮筋回彈拉出搜尋列,非觸控裝置則使用導覽列右側的搜尋鈕。搜尋框支援受控/非受控雙模式:
const [query, setQuery] = React.useState("");
<LargeTitleCollapseHeader
title="設定"
search
searchPlaceholder="搜尋設定"
searchValue={query}
onSearchChange={setQuery}
>
{/* 依 query 過濾後的區段 */}
</LargeTitleCollapseHeader>Props
LargeTitleCollapseHeader
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
title | React.ReactNode | — | 頁首大標題;捲動時以共享元素縮移為導覽列的置中小標題 |
children | React.ReactNode | — | 內容區,通常放入若干 LargeTitleSection |
leading | React.ReactNode | — | 導覽列左側插槽(如返回鈕) |
trailing | React.ReactNode | — | 導覽列右側插槽(如動作鈕);啟用 search 時搜尋鈕排在其後 |
search | boolean | false | 啟用下拉搜尋列與導覽列搜尋鈕 |
searchPlaceholder | string | "搜尋" | 搜尋框 placeholder |
searchValue | string | — | 受控搜尋字串;提供時搭配 onSearchChange |
defaultSearchValue | string | "" | 非受控模式的預設搜尋字串 |
onSearchChange | (value: string) => void | — | 搜尋字串變更回呼 |
navHeight | number | 44 | 導覽列高度(px),同時是區段標題的吸附位置 |
largeTitleHeight | number | 52 | 大標題列高度(px),決定縮移過渡的捲動距離 |
className | string | — | 外層捲動容器的 class(在此設定高度) |
contentClassName | string | — | 內容區外層的 class |
LargeTitleSection
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
title | React.ReactNode | — | 區段標題,吸附在導覽列之下 |
children | React.ReactNode | — | 區段內容 |
id | string | — | 套用在區段標題上的錨點 id |
className | string | — | 附加到最外層 section 的 class |
headerClassName | string | — | 附加到吸附標題列的 class(例如覆寫底色以配合頁面) |
細節
- 縮移為共享元素式的交叉淡入:大標題隨捲動縮小、上移並淡出退到導覽列後方,導覽列的置中小標題同步放大淡入,兩者接手成一個連續的形變
- 所有過渡皆由
useScroll的捲動位移驅動(useTransform直接映射),不依賴 scroll 事件重排版面,捲動效能與原生一致 - 導覽列底色與分隔線在收合過程中才淡入,展開狀態下頁首保持通透
- 搜尋列以
useMotionValue承載展開高度:觸控下拉時以指數阻尼計算橡皮筋位移、鬆手依臨界值決定展開或回彈;橡皮筋過度伸展時搜尋欄位隨之延展 - 區段標題以原生
position: sticky; top: navHeight吸附在導覽列之下,後續區段抵達吸附線時接手前一段,依序輪替 - 導覽列與區段標題採
backdrop-blur半透明底色,內容捲過時保有層次
可及性
- 大標題為真正的標題語意(
<h2>),導覽列中的小標題僅供視覺、標記aria-hidden,避免朗讀重複 - 區段標題為
<h3>,可搭配id作為錨點 - 搜尋鈕為原生
<button>,標記aria-expanded;搜尋列收合時整體aria-hidden、輸入框tabIndex={-1}不會被鍵盤聚焦,展開後可正常聚焦,Escape收合 - 使用者系統開啟「減少動態效果」時,搜尋列的展開/回彈改為即時切換(停用彈簧回彈),下拉手勢與捲動收合仍可操作
- 計時器、事件監聽與手勢綁定於卸載時全數清理