跳到正文
册页

容器

ScrollArea - 滚动区域

一个会滚动的盒子,滚动条在哪都长一样。

用法

什么时候用它

有边界的面板——很长的选项列表、一段日志。页面级或正文滚动用 scroll-slim 工具类更轻,也不需要组件。
TSX
import { ScrollArea } from '@misoto22/folio'

说明

一个会滚动的盒子,滚动条在每个平台上长得一样。

选它而不选 overflow-auto 的理由不是那根滚动条——而是 Radix 让视口可聚焦、让滚动条可操作,光秃秃的 overflow 容器做不到。一个内容本身不可聚焦的滚动区域,键盘是够不着的:没有东西可以 Tab 过去,于是没有鼠标时,折线以下的一切都不存在。

页面级或正文的滚动用 scroll-slim 工具类更轻,也不需要组件。这个是给有边界的面板用的:很长的选项列表、一段日志、一条长过自己那一栏的侧栏。

除非调用方另有说明,两个方向都能滚。以前那个“只竖向”的默认,看上去是在说画哪一根滚动条,实际上是在说内容的哪一半存在:Radix 是按已挂载的滚动条来设视口 overflow 的,所以没有滚动条的那个方向是 hidden,越过那条边的一切都够不着了——悄无声息,而且内容明明渲染得好好的。

结构

ScrollArea anatomy
部件说明
根节点必填那个有边界的盒子,className 也落在它上面。它是 overflow-hidden,自己没有尺寸,所以你在这里给的高度,是决定到底有没有东西会滚的唯一因素。
视口必填真正在滚的那个元素,也是带着 role="region"、tabIndex 0 和 label 的那个。Radix 在它上面把平台的滚动条藏起来,并把没有配滚动条的那个轴设成 overflow: hidden。它是定位过的,所以绝对定位的后代会跟着内容一起走,而不是一动不动地悬在上面。
滚动条每个方向一根:8px 的轨道,--rule-2 的胶囊滑块,touch-none——手指滚的是内容,不是这根条。只有指针在区域里时才画出来,停止滚动约 600ms 后淡出。
直角两根滚动条交汇处那个小方块,只有 orientation="both" 时才存在——而那正是默认值。

实践建议

推荐

  • 要给它一个高度。不给,根节点就和内容一样高,永远不会溢出,这个组件给页面添的就只有一个键盘停靠点。
  • 只有在“裁掉另一个轴”正是你的本意时,才去收窄 orientation。两个轴默认都会滚,因为没有配滚动条的那个轴被设成了 overflow: hidden——越过那条边的东西不只是没有标记,是任何按键、任何手势都够不到,而内容明明好端端地渲染着。
  • 内容正好齐着边界收尾时要传 type="always":平台的滚动条被藏了,我们这根又要等指针进来才画,所以静止时屏幕上没有任何东西说这个盒子会滚。

避免

  • 不要在它上面再造一套拖动滚动的交互——滑块是刻意设成 touch-none 的,视口是一个真正的溢出容器,所以触摸拖动、惯性和滚轮本来就归平台管,行为也正是读者预期的那样。
  • 不要在同一个轴上把一个套进另一个里:内层视口会一直吃掉滚轮事件,直到自己滚到头,所以想滚外层列表的读者,滚动的是内层那个。

示例

a bounded log

根节点上的那个高度,才是让这一切发生的东西:没有它,盒子和内容一样高、什么都不会溢出,而这个组件给页面添加的全部,就是一个键盘停靠点。那个停靠点也正是拿它而不是拿一个光秃秃的 overflow-auto 的理由——Radix 让视口保持可聚焦、让滚动条可操作,于是首屏之外的一切不用鼠标也够得着。label 之所以必填,也是同一个理由:一个没名字的键盘停靠点,播报出来只有一个「group」,别的什么都没有。页面级或正文的滚动,用 scroll-slim 这个工具类更轻,连组件都不需要。

  • 1a2b3c4deployed
  • 1a2d2b3deployed
  • 1a2f1a2deployed
  • 1a31091rolled back
  • 1a32f80deployed
  • 1a34e6fdeployed
  • 1a36d5edeployed
  • 1a38c4ddeployed
  • 1a3ab3crolled back
  • 1a3ca2bdeployed
  • 1a3e91adeployed
  • 1a40809deployed
  • 1a426f8deployed
  • 1a445e7rolled back
  • 1a464d6deployed
  • 1a483c5deployed
  • 1a4a2b4deployed
  • 1a4c1a3deployed

both axes

orientation 默认就是 both,这里把它写出来,是因为那个没有滚动条的轴会被设成 overflow: hidden——所以一旦把它收窄成 vertical,比盒子更宽的内容就不只是没有标记,而是被裁掉了,没有任何按键、任何手势够得到。只有当某个轴确实不可能溢出时,才去收窄它。只有两个轴都在时,才会有那个角上的方块。也别把它们在同一个轴上互相嵌套:内层视口会一直吃掉滚轮,直到它自己到底,所以一个对着外层列表的读者,动的会是里面那个。

Request log
timestamplevelserviceregionrequest idmessage
2026-09-06T04:12:31Zinfoapiap-southeast-27f2a1brequest completed
2026-09-06T04:13:31Zinfoworkerap-southeast-27f49d0request completed
2026-09-06T04:14:31Zinfoworkerap-southeast-27f6985request completed
2026-09-06T04:15:31Zinfoapiap-southeast-27f893arequest completed
2026-09-06T04:16:31Zwarnworkerap-southeast-27fa8efretrying upstream after 502
2026-09-06T04:17:31Zinfoworkerap-southeast-27fc8a4request completed
2026-09-06T04:18:31Zinfoapiap-southeast-27fe859request completed
2026-09-06T04:19:31Zinfoworkerap-southeast-280080erequest completed
2026-09-06T04:20:31Zinfoworkerap-southeast-28027c3request completed
2026-09-06T04:21:31Zinfoapiap-southeast-2804778request completed
2026-09-06T04:22:31Zwarnworkerap-southeast-280672dretrying upstream after 502
2026-09-06T04:23:31Zinfoworkerap-southeast-28086e2request completed
2026-09-06T04:24:31Zinfoapiap-southeast-280a697request completed
2026-09-06T04:25:31Zinfoworkerap-southeast-280c64crequest completed

a permanent bar

type="always" 无论指针在不在里面都把两条滚动条画出来。默认是 hover,而平台自己的滚动条是藏起来的,所以静止时屏幕上没有任何东西说明这个盒子会滚——当最后一行明显被切掉一半时,这没问题;而当内容碰巧正好在边界处结束时,这就是个陷阱,而这份列表差不多就是如此。别在它上面再搭一套拖拽滚动:那个滑块刻意是 touch-none 的,而视口是一个真正的溢出容器,所以触摸拖动、惯性和滚轮已经是平台的了,而且表现符合读者的预期。

⌘K
Open the palette
⌘\
Toggle the sidebar
⌘⇧D
Deploy the current branch
⌘⇧L
Follow the logs
⌘/
Search this page
G then O
Go to overview
G then D
Go to deploys
G then S
Go to settings
?
Show every shortcut

键盘操作

ScrollArea keyboard interactions
按键作用
Tab把焦点移进这个区域——没有鼠标时,这一步才让它能滚。
↑↓Page UpPage Down滚动它。

无障碍

  • 视口保持可聚焦。一个内容本身不可聚焦的滚动区域没有 Tab 停靠点,折线之外的一切对没有鼠标的人等于不存在。
  • label 必填,因为一个没有名字的键盘停靠点只会播报“group”。
  • 除非调用方去收窄 orientation,否则两个轴都会滚,所以比盒子宽的内容仍然够得到,而不是在没有滚动条的情况下被裁掉。