Marginalia Article
Tufte 式邊註閱讀版面:主欄搭配固定寬度旁註欄,內文上標編號以細線連至對應旁註,捲動時旁註淡入並同步高亮,窄螢幕自動降級為行內展開註。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
640
<MarginaliaArticle />
安裝
npx shadcn@latest add https://webberui.com/r/marginalia-article.json或在 components.json 設定 registries 後,改用 @webberui/marginalia-article 安裝。
安裝依賴後,從 registry JSON(/r/marginalia-article.json 的 files[0].content)複製 marginalia-article.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-mergereact-dom 的 createPortal 為 React 內建,無需另行安裝。
使用
在內文中以 Sidenote 包住要標記的字句,note 屬性放入旁註內容即可;編號會依閱讀順序自動產生。
import {
MarginaliaArticle,
Sidenote,
} from "@/components/ui/marginalia-article";
<MarginaliaArticle>
<p>
Tufte 的版式把補充說明推到{" "}
<Sidenote note="讀者只要向右一瞥即可,無需移動視線到頁尾。">
主欄之外
</Sidenote>
,讓正文保持乾淨的閱讀動線。
</p>
</MarginaliaArticle>;若文章位於巢狀的捲動容器內,將該容器的 ref 傳入 container,焦點偵測才會以正確的捲動根運作:
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[400px] overflow-y-auto">
<MarginaliaArticle container={scrollerRef}>{/* … */}</MarginaliaArticle>
</div>;Props
MarginaliaArticle
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 文章內容,於內文中放入 Sidenote |
sidenoteWidth | string | "12rem" | 邊欄寬度(任意 CSS 長度) |
gap | string | "2.5rem" | 主欄與邊欄之間的間距 |
breakpoint | number | 640 | 文章可用寬度小於此值(px)時降級為行內展開註 |
container | React.RefObject<HTMLElement | null> | — | 巢狀捲動容器的 ref;預設以視窗為捲動根 |
className | string | — | 附加在最外層容器 |
Sidenote
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 內文中被標記的字句,成為焦點時高亮 |
note | React.ReactNode | — | 旁註內容:寬螢幕顯示於邊欄,窄螢幕行內展開 |
id | string | 自動 | 錨點 id;省略時自動產生,需穩定以正確配對 |
label | React.ReactNode | 自動編號 | 覆寫上標標記(例如以 ※ 取代數字) |
className | string | — | 附加在內文標記的外層 span |
細節
- 防重疊堆疊:多則旁註各自對齊其錨點的垂直位置,若彼此過近會自動向下讓位,維持最小間距。
- 連接線:以貝茲曲線自上標編號右緣平滑彎入邊欄;焦點旁註的線條加深、非焦點者淡出,未進入視口者則完全隱藏。
- 量測時機:位置與連接線相對於文章框計算,因此僅在版面或尺寸變動(
ResizeObserver/resize)時重算,捲動時不重算,效能穩定。 - 焦點判定:以
IntersectionObserver追蹤各錨點,取最接近閱讀線(視口約 32% 高度)者為當前焦點。
可及性
- 使用者系統開啟「減少動態效果」時,淡入、位移與連接線過渡全部退化為即時切換,版面與內容不變。
- 上標標記為真正的
<button>,可鍵盤聚焦與觸發;寬螢幕以aria-describedby關聯邊欄旁註,窄螢幕以aria-expanded/aria-controls描述行內展開狀態。 - 旁註容器帶
role="note";窄螢幕降級為可點擊展開的行內註,小螢幕仍能取得完整脈絡。 - 所有
IntersectionObserver、ResizeObserver與事件監聽於卸載時完整清理。