WebberUI

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.jsonfiles[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型別預設值說明
titleReact.ReactNode頁首大標題;捲動時以共享元素縮移為導覽列的置中小標題
childrenReact.ReactNode內容區,通常放入若干 LargeTitleSection
leadingReact.ReactNode導覽列左側插槽(如返回鈕)
trailingReact.ReactNode導覽列右側插槽(如動作鈕);啟用 search 時搜尋鈕排在其後
searchbooleanfalse啟用下拉搜尋列與導覽列搜尋鈕
searchPlaceholderstring"搜尋"搜尋框 placeholder
searchValuestring受控搜尋字串;提供時搭配 onSearchChange
defaultSearchValuestring""非受控模式的預設搜尋字串
onSearchChange(value: string) => void搜尋字串變更回呼
navHeightnumber44導覽列高度(px),同時是區段標題的吸附位置
largeTitleHeightnumber52大標題列高度(px),決定縮移過渡的捲動距離
classNamestring外層捲動容器的 class(在此設定高度)
contentClassNamestring內容區外層的 class

LargeTitleSection

Prop型別預設值說明
titleReact.ReactNode區段標題,吸附在導覽列之下
childrenReact.ReactNode區段內容
idstring套用在區段標題上的錨點 id
classNamestring附加到最外層 section 的 class
headerClassNamestring附加到吸附標題列的 class(例如覆寫底色以配合頁面)

細節

  • 縮移為共享元素式的交叉淡入:大標題隨捲動縮小、上移並淡出退到導覽列後方,導覽列的置中小標題同步放大淡入,兩者接手成一個連續的形變
  • 所有過渡皆由 useScroll 的捲動位移驅動(useTransform 直接映射),不依賴 scroll 事件重排版面,捲動效能與原生一致
  • 導覽列底色與分隔線在收合過程中才淡入,展開狀態下頁首保持通透
  • 搜尋列以 useMotionValue 承載展開高度:觸控下拉時以指數阻尼計算橡皮筋位移、鬆手依臨界值決定展開或回彈;橡皮筋過度伸展時搜尋欄位隨之延展
  • 區段標題以原生 position: sticky; top: navHeight 吸附在導覽列之下,後續區段抵達吸附線時接手前一段,依序輪替
  • 導覽列與區段標題採 backdrop-blur 半透明底色,內容捲過時保有層次

可及性

  • 大標題為真正的標題語意(<h2>),導覽列中的小標題僅供視覺、標記 aria-hidden,避免朗讀重複
  • 區段標題為 <h3>,可搭配 id 作為錨點
  • 搜尋鈕為原生 <button>,標記 aria-expanded;搜尋列收合時整體 aria-hidden、輸入框 tabIndex={-1} 不會被鍵盤聚焦,展開後可正常聚焦,Escape 收合
  • 使用者系統開啟「減少動態效果」時,搜尋列的展開/回彈改為即時切換(停用彈簧回彈),下拉手勢與捲動收合仍可操作
  • 計時器、事件監聽與手勢綁定於卸載時全數清理

On this page