WebberUI

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.jsonfiles[0].content)複製 marginalia-article.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge

react-domcreatePortal 為 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型別預設值說明
childrenReact.ReactNode文章內容,於內文中放入 Sidenote
sidenoteWidthstring"12rem"邊欄寬度(任意 CSS 長度)
gapstring"2.5rem"主欄與邊欄之間的間距
breakpointnumber640文章可用寬度小於此值(px)時降級為行內展開註
containerReact.RefObject<HTMLElement | null>巢狀捲動容器的 ref;預設以視窗為捲動根
classNamestring附加在最外層容器

Sidenote

Prop型別預設值說明
childrenReact.ReactNode內文中被標記的字句,成為焦點時高亮
noteReact.ReactNode旁註內容:寬螢幕顯示於邊欄,窄螢幕行內展開
idstring自動錨點 id;省略時自動產生,需穩定以正確配對
labelReact.ReactNode自動編號覆寫上標標記(例如以 取代數字)
classNamestring附加在內文標記的外層 span

細節

  • 防重疊堆疊:多則旁註各自對齊其錨點的垂直位置,若彼此過近會自動向下讓位,維持最小間距。
  • 連接線:以貝茲曲線自上標編號右緣平滑彎入邊欄;焦點旁註的線條加深、非焦點者淡出,未進入視口者則完全隱藏。
  • 量測時機:位置與連接線相對於文章框計算,因此僅在版面或尺寸變動(ResizeObserver / resize)時重算,捲動時不重算,效能穩定。
  • 焦點判定:以 IntersectionObserver 追蹤各錨點,取最接近閱讀線(視口約 32% 高度)者為當前焦點。

可及性

  • 使用者系統開啟「減少動態效果」時,淡入、位移與連接線過渡全部退化為即時切換,版面與內容不變。
  • 上標標記為真正的 <button>,可鍵盤聚焦與觸發;寬螢幕以 aria-describedby 關聯邊欄旁註,窄螢幕以 aria-expanded / aria-controls 描述行內展開狀態。
  • 旁註容器帶 role="note";窄螢幕降級為可點擊展開的行內註,小螢幕仍能取得完整脈絡。
  • 所有 IntersectionObserverResizeObserver 與事件監聽於卸載時完整清理。

On this page