WebberUI

Focus Recession Stage

頁面級焦點系統:任一區塊進入聚焦/編輯模式時,其餘版面整體向後退場——縮小、調暗、模糊成景深背景,Esc 或點外部彈簧回彈歸位。

載入預覽⋯

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

<FocusRecessionStage />

安裝

npx shadcn@latest add https://webberui.com/r/focus-recession-stage.json

或在 components.json 設定 registries 後,改用 @webberui/focus-recession-stage 安裝。

安裝依賴後,從 registry JSON(/r/focus-recession-stage.jsonfiles[0].content)複製 focus-recession-stage.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge

FocusTrigger 的轉場會讀取 --wb-duration-fast--wb-ease-out CSS 變數,皆內建 fallback(200mscubic-bezier(0.22,1,0.36,1)),未定義也能正常運作。

使用

import {
  FocusRecessionStage,
  FocusRegion,
} from "@/components/ui/focus-recession-stage";

<FocusRecessionStage className="grid grid-cols-2 gap-4">
  <FocusRegion id="inbox" label="聚焦 收件匣">
    <InboxCard />
  </FocusRegion>
  <FocusRegion id="calendar" label="聚焦 行事曆">
    <CalendarCard />
  </FocusRegion>
</FocusRecessionStage>;

區塊內含互動元素(輸入框、多個按鈕)時,改用 FocusTrigger 觸發,並關掉整塊觸發:

import { FocusRegion, FocusTrigger, useFocusRegion } from "@/components/ui/focus-recession-stage";

function NoteCard() {
  const { focused } = useFocusRegion();
  return (
    <div>
      {focused ? <textarea autoFocus /> : <p>筆記預覽…</p>}
      <FocusTrigger>{focused ? "完成" : "編輯"}</FocusTrigger>
    </div>
  );
}

<FocusRegion id="note" activateOnPress={false}>
  <NoteCard />
</FocusRegion>;

Props

FocusRecessionStage

Prop型別預設值說明
focusedIdstring | null受控的聚焦區塊 id;傳 null 為無聚焦,不傳則由元件內部管理
defaultFocusedIdstring | nullnull非受控模式的初始聚焦 id
onFocusedChange(id: string | null) => void聚焦目標變更時觸發(含 Esc/點外部解除時)
variant'depth' | 'flat' | 'spotlight''depth'景深款式預設,可再以下方參數覆寫
recedeScalenumber依 variant覆寫:退場區塊縮放比例
recedeOpacitynumber依 variant覆寫:退場區塊透明度(0–1)
recedeBlurnumber依 variant覆寫:退場區塊模糊半徑(px)
focusScalenumber依 variant覆寫:聚焦區塊放大比例
scrimOpacitynumber依 variant覆寫:遮罩不透明度(0–1)
scrimbooleantrue聚焦時是否鋪一層暗色遮罩統一景深
dismissOnEscbooleantrue按 Esc 是否解除聚焦
dismissOnOutsideClickbooleantrue點擊聚焦區塊以外處是否解除聚焦
childrenReact.ReactNode直接放入 FocusRegion 子元件
classNamestring附加到舞台容器的 class;可在此設定 grid/flex 版面

FocusRegion

Prop型別預設值說明
idstring—(必填)唯一識別 key:聚焦配對與點擊外部判定用
activateOnPressbooleantrue整塊即為觸發器(點擊/Enter/Space 聚焦);含互動內容或改用 FocusTrigger 時設為 false
disabledbooleanfalse停用此區塊:不可被聚焦、無按鈕語意
labelstring整塊作為觸發器時的無障礙名稱(aria-label
childrenReact.ReactNode區塊內容;可透過 useFocusRegion() 讀取聚焦狀態
classNamestring附加到區塊外框的 class

FocusTrigger

放在 FocusRegionactivateOnPress={false})內的原生 button,點擊在「聚焦/解除」間切換。承接所有原生 <button> 屬性(onClick 除外,由元件內部接管)。

useFocusRegion()

FocusRegion 子樹內呼叫,回傳 { id, focused, receded, focus, dismiss },可依 focused 條件顯示編輯 UI 或展開細節。

細節

  • 景深退場:任一區塊聚焦時,聚焦區塊放大抬升到遮罩之上、維持全清晰;其餘區塊同時縮小、調暗、模糊為景深背景,並蓋上一層暗色遮罩統一整體氛圍
  • 三款款式depth 縮小+模糊+調暗(預設)、flat 只調暗不位移(低干擾)、spotlight 強聚光(更暗更模糊、聚焦區塊抬升更明顯);每個維度都可用個別 prop 微調覆寫
  • 彈簧回彈:位移用 spring(微幅過衝),Esc 或點外部解除時整體以同一 spring 彈回歸位;透明度與模糊改用 tween,避免過衝造成模糊閃動
  • 受控/非受控:可完全交給元件內部管理,或以 focusedId + onFocusedChange 受控串接自家狀態
  • 純 transform 效果:縮放、模糊、透明度皆不觸發重排(reflow),聚焦區塊以 z-index 抬升覆蓋鄰塊,版面不位移
  • 兩種觸發方式:整塊觸發(activateOnPress,適合展示型卡片)或 FocusTrigger(適合區塊內含輸入框、多個按鈕)
  • 解除聚焦:監聽於聚焦後才掛載、下一幀才生效,不會誤捕捉觸發聚焦的那次點擊;監聽器在解除與 unmount 時全數清理

可及性

  • 整塊觸發時帶 role="button"tabIndex={0}aria-label,支援 Enter/Space 聚焦
  • 退場中的區塊標記 aria-hidden 並移出 Tab 焦點序(tabIndex={-1})、停用指標事件,成為單純的背景
  • data-focus-state="focused | receded | idle" 對外暴露狀態,方便自訂樣式掛勾
  • 使用者系統開啟「減少動態效果」時,聚焦與退場改為瞬間切換(無 spring/無漸變),景深與焦點功能完全保留

On this page