Repository navigation
Releases
v0.83.0
Added
placement="top"on DialogContent, on a desktop and a tablet (and on DialogSheetContent there): the dialog stays centered across, its top edge atvar(--dialog-top, 8vh), and it is never taller than the window less that top and 16px, so it never runs past the window's bottom. Its width still comes fromsize. The content carriesdata-placement="top". To anchor the card to something, the app measures it and sets--dialog-topin the content'sstyle; raft-ui measures nothing. Below 768px it does nothing.- Why: search opens from the top bar's field, and its panel's top edge belongs on the field, which a centered dialog can't do (slock drew it on DialogSurface with its own positioning, #triage:#278).
- Affects: nothing until a dialog passes
placement="top":"center", the default, renders the same DOM and classes as before. - Docs: https://raft-design.botiverse.build/components/dialog/#at-the-top
- On a phone every centered dialog moves up while the on-screen keyboard is open, whatever its
placement: its top edge goes where the phone's top bar ends (--header-heightbelow the top of what is visible, under the status bar), it ends at least 16px above the keyboard, and it goes back to the middle when the keyboard closes. While lifted the content carriesdata-soft-keyboard. A phone sheet doesn't move; it keeps its own keyboard box.- Why: a centered dialog covered by the keyboard is always wrong, its field or its buttons out of sight, so this is platform behavior and no app opts in (stdrc, 2026-10-21: centered unless the keyboard is open). slock did it with its own
narrow="top"; that maps to the default now. - Affects: every centered
DialogContentbelow 768px, only while a text field in it has focus and the visible area is over 100px shorter than the window; at rest nothing changes. A DialogSheetContent's keyboard box now reads the keyboard through the same check, once a frame: a focused checkbox, radio or button no longer counts as typing. An app that lifted its dialogs itself (slock'snarrow="top") can drop that code. - Docs: https://raft-design.botiverse.build/components/dialog/#the-keyboard
- Why: a centered dialog covered by the keyboard is always wrong, its field or its buttons out of sight, so this is platform behavior and no app opts in (stdrc, 2026-10-21: centered unless the keyboard is open). slock did it with its own
- SelectionToolbar (from
@botiverse/raft-ui/wip): the floating toolbar for selected items (messages, tasks), in the BatchBar style.SelectionToolbarDockfloats it 12px above the bottom (plus the safe area) and 20px from the sides;SelectionToolbarCount(with the number inSelectionToolbarCountValue, Space Mono),SelectionToolbarHint,SelectionToolbarActionswithSelectionToolbarAction(secondary,menu,moreItem),SelectionToolbarMorewithSelectionToolbarMoreItemsin its menu,SelectionToolbarItemfor another control such as a select-all checkbox, andSelectionToolbarClose. A floating panel's frame (line-seamring,shadow-float) as tall as a head: 44px withmdbuttons 4px apart with a mouse, 56px with 44px bar buttons side by side showing their icons alone by touch. It is a Base UI Toolbar (one Tab stop, arrow keys inside) and never wraps: when its row doesn't fit the width it floats in, measured, the hint, the secondary actions, the labels (the first action's last) and then the other actions give way, the last first; the count and × stay.- Why: the toolbar for selected items is one bar shared by message multi-select and task bulk actions, in Noel's BatchBar style:
mdghost buttons with a 14px icon and a label, no primary (stdrc, 2026-11-10, #wg-task-v2); by touch it aligns with the head, so a phone fits the tasks' Status, Assignee and Nudge at 360px (Noel, 2026-11-10). - Affects: nothing yet. The stable
MessageMultiSelectToolbarandMessageMultiSelectToolbarActionskeep their earlier bar until SelectionToolbar leaves wip; move to it from@botiverse/raft-ui/wipwhen you are ready. - Docs: https://raft-design.botiverse.build/components/selection-toolbar/, https://raft-design.botiverse.build/components/#toolbar-for-selected-items
- Why: the toolbar for selected items is one bar shared by message multi-select and task bulk actions, in Noel's BatchBar style:
- FitToolbar (from
@botiverse/raft-ui/wip): a Base UI Toolbar whose items give way in named steps as the region it sits in narrows:label(to an icon),more(into More),collapse(a group to one button) andwrap(a group to a line of its own, withwrap; groups with the samewrapLineshare one, and a wrapped group gets back what its items gave before it wrapped). Items declare their steps and order withuseFitItem, or are aFitToolbarButton; aFitToolbarGroup overflow="scroll"scrolls sideways instead, itsdata-activeitems kept in view; groups areFitToolbarGroup(firstLine,wrapOrder,wrapLine,collapsed,collapseOrder); More takesuseFitMoreand listsFitToolbarMoreItems. It measures its own region's width, never the window's, again when its content changes on its own, and writes each step asdata-fit-<step>. A SegmentedControl inside it follows itslabelstep; its owniconsOnlyBelowis off there.- Why: one measured engine for every toolbar that has to give way (responsive.md "measure the content rather than guess a width"), used by SelectionToolbar now and meant for the list toolbars of #proj-raft-ui:#225, instead of a breakpoint guess or a hook per app. Its step names and API stay in wip until sindresorhus confirms them for those toolbars.
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
Changed
- DialogSheetContent, DialogCommit and DialogSheetBack are stable: import them, and their props types, from
@botiverse/raft-ui.@botiverse/raft-ui/wipno longer exports them.- Why: slock is moving its own dialogs, its phone sheets among them, onto raft-ui (#triage:#278), and a product builds on the stable entry.
- Affects: an import from
@botiverse/raft-ui/wipfails; import from@botiverse/raft-uiinstead. No app imported them from/wip(slock draws its sheets itself today), and the docs' examples have moved. - Docs: https://raft-design.botiverse.build/components/dialog/#making-a-dialog-a-sheet
Fixed
- A label/value list puts its labels above when a chip or a long word of a wrapping value can't fit beside its label: DescriptionList
direction="horizontal"and MessageActionCard's properties now also stack when one chip of a value that wraps either way (a row of chips wider than the list) is wider than the value column but no wider than the list, and likewise when a long word in such a value (an ID, a commit SHA, a URL) is: beside the label the chip was cut and the word ran past the value column. A word set tooverflow-wrap: anywherebreaks where it must, and doesn't decide. There is no new prop.- Why: the one-line rule let a row of chips wider than the list stay beside its labels, where one long chip was cut off with no way to read it (mobile #4507, the Computer page's Detected Runtimes chip).
- Affects: lists whose value wraps either way and holds a chip or a long word wider than the value column now stack instead of staying beside their labels (slock's Computer page among them). Such a word used to run past the value column in DescriptionList and break mid-word in MessageActionCard properties; it now reads whole on its own line. Every other list lays out as before; the extra measurement runs only for values wider than the list.
- Docs: https://raft-design.botiverse.build/components/description-list/#labels-go-above-only-when-a-value-doesnt-fit
- The server tab's popover keeps one whole server row and opens on the current server:
MobileNavServerPopoverListis never shorter than one two-line row. Where the footer actions and the pages would leave it less inside 60% of the screen,MobileNavServerPopoverContentgrows past 60% as far as that row needs; where the screen is too short even for that, the whole popover scrolls, the footer and the pages with it, in a ScrollArea. On open the list scrolls, with no animation, to the row witharia-current="true", and focus goes to that row (a touch still focuses the popover itself), so focus no longer scrolls the list back to the top.- Why: with 5 pages in the grid, a 360dp phone squeezed the list until the current server was half hidden, and the list always opened at the top (Lagoon, mobile #4500; Jasper's review of raft-design #1117).
- Affects: every
MobileNavServerPopoverContent. A popover that fit before is unchanged except that it opens scrolled to the current server; one whose list was squeezed below a row is now taller than 60%, or scrolls as a whole. Opening from a mouse or keyboard focuses the current server's row instead of the first row; passinitialFocusto choose otherwise. The popover's parts now sit in a ScrollArea (data-slot="mobile-nav-server-popover-scroll") inside the popup, so a selector for a direct child ofmobile-nav-server-popover-contentno longer matches them. Mark the current server's row witharia-current="true"for the scroll and the focus to find it. - Docs: https://raft-design.botiverse.build/components/mobile-nav/#the-server-tab
- A settings row's switch centers on its text, not on the SaveStatus line under it: in a
ListRow variant="plain"whoseListRowBodyends with aSaveStatus, both ends (the switch, value, chevron, checkbox, radio or button, and a leading icon, avatar or tile) center on the title and description. The row moves the line's reserved space (4px and one footnote line) into its bottom padding, so its height is unchanged and nothing moves when "Saved" or a one-line error appears. There is no new prop.- Why: the ends centered on the whole body, the empty reserved line included, so the switch sat visibly below the text (slock's Administration › Hide #all channel; Jasper and Kirby, 2026-11-09, #proj-raft-ui:#220).
- Affects: in such rows the ends move up by half the reserved line (11px). A row without a
SaveStatusis unchanged. An error that wraps still grows the row, and the ends move down by half of each extra line. Beside a title alone on a phone, the 30p...
v0.82.0
Changed
- OtterIndicator uses Ottie's approved four poses: relaxed side-lying, quiet typing, a puzzled head tilt with a question mark, and one celebratory hop. The SVG props and ref remain supported. It pauses outside the viewport and in hidden documents; reduced motion shows a still. The HTML art layer sizes from the SVG box so Safari/WebKit scales it correctly at 40px and larger.
- Why: the previous unrelated otter drawing and sleeping wait pose did not match Ottie or the recent-activity card (Artea's approved B study).
- Affects: consumers of OtterIndicator, including Raft's recent-activity card. Artwork loads as separate transparent WebP files only for a displayed status; there is no frame timer or per-frame React update.
- Docs: https://raft-design.botiverse.build/components/steps/#ottie-illustration
Breaking
ComposerDropOverlayPrompttakes its text aschildren, which is required: the icon cluster stays, and the text under it is what you pass. It no longer falls back to the built-in "Drop files to attach", and children no longer replace the icons.- Why: the component library carried product copy in English that no app could translate (Remy's helper-text sweep, #proj-workspace:#250).
- Affects:
<ComposerDropOverlayPrompt />no longer type-checks; pass the text, e.g.<ComposerDropOverlayPrompt>{t("dropToAttach")}</ComposerDropOverlayPrompt>. A caller that passed children for a label without icons usesComposerDropOverlayLabelinstead. - Docs: https://raft-design.botiverse.build/components/composer/
Added
- Data parts beside the explanation parts: three additive parts, so data no longer has to sit in a slot meant for a sentence that explains. Nothing existing changes: no prop, part or style is renamed or restyled.
ListRowSubtitle: data about a row's subject under its title (an agent's description, a channel's topic, an app's byline), in the title's ink at 400 andtext-footnote. It follows the row's tone and disabled state with the title.ListRowDescriptionstays the place for a setting's consequence.- ChoiceList items take
meta: lines of data people compare across rows (a quota, free disk, a count, a status), intext-footnoteinforeground-mutedas the description is, after thedescriptionlines. A row still draws at most two lines in all, the description's first; a meta line has a description line's height, so moving a line from one to the other doesn't move the rows. Radios and checkboxes are described by the description lines, then the meta lines. MessageActionCardHeaderMeta: the time on a resolved card, in Space Mono at the hint tier: a line of its own under the title, or the end of theMessageActionCardHeaderDescriptionline it is placed in ("Approved by Martin · 10:51").- Why: grey text is to mean one of two things, a sentence written to explain or a component's own notes on its data (times, counts, IDs), and data people read as the content uses the body ink. Apps had put data in the explanation parts (
ListRowDescription, a ChoiceList item'sdescription,MessageActionCardHeaderDescription) because there was nowhere else for it; these parts give it a place before those parts are kept for explanations (Kirby, Remy's helper-text plan). - Affects: nothing until an app uses them. Data moved into
ListRowSubtitlegets darker and a size smaller than inListRowDescription; data moved into a ChoiceList item'smetalooks as it did indescription; a time moved intoMessageActionCardHeaderMetais set in Space Mono at the hint tier. - Docs: https://raft-design.botiverse.build/components/list-row/#data-and-explanation, https://raft-design.botiverse.build/components/choice-list/#content, https://raft-design.botiverse.build/components/message-action-card/#anatomy
Fixed
-
A Resizable seam's handle no longer covers the scrollbar beside it: the 12px handle (24px under a coarse pointer) lies over the start of the panel after the boundary instead of straddling it; its hover and focus line stays on the seam.
- Why: a panel's scrollbar runs along its end edge, the seam's other side, and the centered handle covered its last 6px, where the thumb is, so dragging the thumb resized the panel instead (Raft app's Activity list, #proj-raft-ui:21bae128).
- Affects: the handle now takes the pointer over the first 12px of the next panel (24px by touch) instead of 6px on each side. Nothing else moves.
- Docs: https://raft-design.botiverse.build/components/resizable/#behavior
-
ScrollArea measures in the frame after mounting, not while React commits: where a
ResizeObserverwatches the sizes, the first read of the container'sclientHeightandscrollHeightwaits for the next animation frame, as every later one already did. Without aResizeObserverit still measures at once.- Why: the read in the commit forced a layout of the whole page each time an area mounted. Entering a server in the Raft app mounts the sidebar, the message list and the panels at once, and those reads took about 120ms of layout (InNOut's measurement, #proj-frontend:#40).
- Affects: the frame runs before the paint that first shows the area, so nothing on screen changes. Until it runs, an overflowing area has no thumb length and no overflow classes, and
onEdgeChangeis first called in it: code that reads them in a layout effect right after mounting finds them unset, and can callactionsRef.measurefor a measure at once. - Docs: https://raft-design.botiverse.build/components/scroll-area/
Full Changelog: v0.81.0...v0.82.0
v0.81.0
Fixed
- ScrollArea's scrollbars paint over everything inside the area: the gutters layer is at the highest
z-index, and the area's root is a stacking context of its own (isolation: isolate), so a sticky heading or any other positioned content inside the area, whatever itsz-index, no longer covers the thumb, with sticky and absolute gutters alike, and the scrollbars still never paint over anything outside the area.- Why: in the Raft app's activity list each day's sticky heading (an opaque background at z-15) painted over the thumb as it slid by, cutting it into pieces, while the gutters were at z-1 (Artea, #proj-raft-ui:21bae128). Starting the scrollbar below the stuck heading (
railInset) fixed only the heading at the top, not the ones scrolling past. - Affects: content inside a ScrollArea can no longer cover anything outside it, whatever its
z-index: a popup, a menu or a dragged copy rendered in place inside an area stacks with the area. Render those through a portal (raft-ui's overlays do, and nowDragLiftby default, below). The scrollbars' strip at the edge now takes the pointer over positioned content there too, as it already did over the rest. How the scrollbars look is unchanged. - Docs: https://raft-design.botiverse.build/components/scroll-area/
- Why: in the Raft app's activity list each day's sticky heading (an opaque background at z-15) painted over the thumb as it slid by, cutting it into pieces, while the gutters were at z-1 (Artea, #proj-raft-ui:21bae128). Starting the scrollbar below the stuck heading (
DragLiftrenders the copy indocument.bodyby default: the lifted copy goes through a portal to the body unlesscontainernames another element, ornullkeeps it in place; the portal keeps the theme scope it was rendered in.TaskDragOverlayfollows, asSortableTabsListandSortableComposerAttachmentsalready did.- Why: in place inside a ScrollArea, now a stacking context, the copy couldn't rise above anything outside the area. On the Raft app's Tasks board a dragged card was cut off by the left rail and covered by the top bar (
TaskDragOverlaywith nocontainer). - Affects: a
DragLiftorTaskDragOverlaywithoutcontainernow renders its copy in the body: CSS that styles the copy through an ancestor of the item (.board [data-slot=drag-lift]) no longer reaches it, and neither do CSS variables set on that ancestor; style the copy through its own children. The writing direction (dir) is carried over, below. Passcontainer={null}for the old in-place copy. How the copy looks is unchanged. - Docs: https://raft-design.botiverse.build/components/drag/#anatomy
- Why: in place inside a ScrollArea, now a stacking context, the copy couldn't rise above anything outside the area. On the Raft app's Tasks board a dragged card was cut off by the left rail and covered by the top bar (
- In a right-to-left layout the dragged copy keeps its direction:
DragLift's portal carries the item's computed writing direction (dir) to the body, as it carries the theme scope.- Why: in the body, the copy took its direction from
<html>, so an item dragged out of a right-to-left container came out left-to-right (Jasper's review of #1094). - Affects: a copy of an item in a
dir="rtl"container now lays out right-to-left. Nothing else around the item reaches the copy (font, colour, a container's custom properties); the docs now say so. - Docs: https://raft-design.botiverse.build/components/drag/#anatomy
- Why: in the body, the copy took its direction from
- ChoiceList's row actions keep clear of the scrollbar: in a list that scrolls (search on, or
fill), a row's action sits 12px from the end instead of 4px, and rows with an action keep their text 52px from the end instead of 44px.- Why: the scrollbar's 16px strip at the end of a ScrollArea now paints over and takes the pointer from everything in the area; the last 8px of each 32px action lay under it, so a click there paged the list instead.
- Affects: in a scrolling ChoiceList, each action moves 8px toward the start, and a long name or description cuts off 8px sooner. A list that doesn't scroll is unchanged.
- Docs: https://raft-design.botiverse.build/components/choice-list/#behavior
Full Changelog: v0.80.0...v0.81.0
v0.80.0
Changed
- A panel head is one line from 768px by touch too:
PanelHeaderContentputs thePanelMetaafter the title on its line from 768px at any pointer, where it used to do so only with a mouse; a phone still puts it under the title.- Why: a touch tablet stacked the title and its count while a laptop showed them on one line, so the same page looked different on the two, and the Raft app had started overriding the layout page by page (xxchan, Kirby, #proj-workspace:dc3c9fc9).
- Affects: on a touch screen 768px or wider, every panel head (Tasks, Artifacts, a thread, a profile, a channel) shows its subtitle on the title's line, and the subtitle truncates first. Head heights don't change (56px by touch). An app that stacked them itself can drop its override.
- Docs: https://raft-design.botiverse.build/components/panel/#anatomy
Fixed
- A gallery row of three images sits three across:
MessageImageGalleryRowwith three images (count: 3) lays them out three across at every width, on the shorter rows already sized for three, where it used to put two over one.- Why: six images showed as rows of 2, 1, 2, 1 and read as an F (xxchan, #triage:c3c22ffb).
- Affects: three, six and nine images in a message now show three to a row; each image is narrower (about 117px instead of 178px in a 360px gallery), so a wide screenshot is cropped more in the thumbnail. Opening an image still shows all of it.
- Docs: https://raft-design.botiverse.build/components/message-attachment/#rules
Full Changelog: v0.79.0...v0.80.0
v0.79.0
Changed
- A sidebar row out of view isn't laid out or painted:
SidebarItemsetscontent-visibility: auto, with one line (1lh) as its content height until it has been laid out (the last laid-out height after that, for a row that wraps) and a 2pxoverflow-clip-margin.- Why: a server with hundreds of channels relaid every row whenever the window's width changed, about 115ms per relayout at 300 channels (InNOut, #proj-frontend:57f32c96). On a 300-row sidebar, a width sweep's layout time drops from 95–122ms to 29–33ms (70 width changes, 3 runs each).
- Affects: nothing visible. The selected row's outer sticker edge, which reaches 1.5px past the row, still paints. A browser without
overflow-clip-marginkeeps laying every row out. Something an app draws past a row's box by more than 2px gets clipped. - Docs: https://raft-design.botiverse.build/components/sidebar/#behavior
Full Changelog: v0.78.0...v0.79.0
v0.78.0
Changed
raft-ui/prefer-scroll-areaalso reads class lists kept in a const and aclassNameproperty: a native-scrolling class in aconstof the same file is reported when the const is used as a class there (const log = "max-h-64 overflow-auto", thenclassName={log}orcn(log)), wherever in the file it is declared; and so is one in aclassName,classor*ClassNameproperty, such as a recipe slot's option (content({ className: "overflow-y-auto" })). The escape comment works on them as before. (#1078)- Why: apps keep many class lists in consts; Stamp's port of the rule found four native scrollers this way that the rule missed (Kirby, #proj-raft-ui:#209).
- Affects: upgrading can report scrollers the rule didn't see before; fix them, or mark an allowed exception. The Raft app gets one, the onboarding form panel (
ONBOARDING_CREATE_FORM_PANEL_CLASS); its desktop app none. A string in a const that is never used as a class is still not reported. - Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
Full Changelog: v0.77.1...v0.78.0
v0.77.1
Fixed
- The lint plugin's recommended config keeps the app's own Oxlint plugins:
recommended.jsonsets"plugins": []. (#1076)- Why: an extended config without
pluginscounts as Oxlint's default plugins, so extending it turnedunicornback on in an app that lists its plugins without it (36 new warnings in slock, Kirby, #proj-raft-ui:#209). Same on Oxlint 1.85, 1.87 and 1.91. - Affects: an app that extends
recommended.jsonkeeps exactly thepluginsit lists; an app that lists none keeps Oxlint's defaults, as before. The ESLint config is unchanged. - Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
- Why: an extended config without
Full Changelog: v0.77.0...v0.77.1
v0.77.0
Changed
- Select, Combobox and ComboboxSelect lists scroll in a ScrollArea: each list is a
useScrollAreaContainercontainer with absolute gutters, inside a frame (data-slot="select-list-frame",combobox-list-frame) that holds the overlay scrollbar. The list is never a Tab stop: focus stays in the input or on the options. (#1074)- Why: they were the last native-scrolling exception for Base UI lists; on Windows they drew a classic scrollbar with arrows that took width from the options (Artea's runtimes list, #proj-raft-ui:20a421bd).
- Affects: the scrollbar is the thin overlay thumb on every platform.
SelectList/ComboboxListrender a frame<div>around the list element;ref,className,styleand the other props still go to the list. CSS or tests that expected the list to be the popup's direct child, or waited ondocument.getAnimations()to finish (the thumb runs on a scroll timeline), need updating. - Docs: https://raft-design.botiverse.build/components/scroll-area/
Full Changelog: v0.76.1...v0.77.0
v0.76.1
Fixed
- Select and Combobox lists never scroll sideways: a Select list reaches into the popup's 4px inset and pads it back, so a
SelectSeparatormeets the popup's edge and stays inside the list, and Select and Combobox lists setoverflow-x-hidden. (#1070)- Why: the separator reached 4px past each side of the list, which is the scroller, so the list scrolled sideways by 8px, and a classic scrollbar (Windows) drew a horizontal bar under the runtimes list (Artea, #proj-raft-ui:20a421bd). The list also clipped the line 4px short of the popup's edge.
- Affects: every Select with a separator, such as the runtime picker's Show installed only row: no sideways scroll, and the line now runs to the popup's border. Options keep their inset. A list action keeps its highlight after it adds items above it, as before.
- Docs: https://raft-design.botiverse.build/components/scroll-area/
Full Changelog: v0.76.0...v0.76.1
v0.76.0
Changed
- VirtualList writes no
--virtual-list-width: the list no longer writes its width as a CSS variable on its scroller, andrefreshListWidth()on its imperative API does nothing (deprecated; it will be removed). (#1068)- Why: the variable inherits, so a write on the scroller restyled every row under it, on every frame of a window resize, and nothing read it (InNOut's trace of a window drag in the app, #proj-frontend:57f32c96).
- Affects: CSS that read
var(--virtual-list-width)gets nothing; read the width with the list'sonResizeor a ResizeObserver. Neither raft-ui nor the Raft app read it. - Docs: https://raft-design.botiverse.build/components/virtual-list/
Fixed
- Resizing a window no longer restyles everything inside a ScrollArea: the sizes ScrollArea measures are no longer written on the root. With sticky gutters, the viewport's height (
--r-scroll-area-viewport-height) and the container's padding (--r-scroll-area-padding-top,-leftand-right) go on the gutters layer, and withfadethe viewport's height and width (--r-scroll-area-viewport-width) on the content its mask is on; with absolute gutters, which read none of them, nothing is written. They are registered not to inherit (@property). A message timeline (useScrollAreaContaineron a VirtualList) restyled every message on every frame of a window drag. (#1068)- Why: in the app, dragging the window's edge took 61-73ms a frame, 75-85% of it restyling, because the viewport's size changed on the root of each column's message list (InNOut, #proj-frontend:57f32c96).
- Affects: dragging the window's edge in the Raft app (a channel with a thread open, 1600 to 900 to 1600px wide): style recalculation 2.7s to 1.5s and the median step 52 to 27ms with this release, 0.3s and 17ms together with the app's own fix. CSS that read those variables inside an area now sees their initial values (100% and 0px); they were internal, and neither raft-ui nor the Raft app read them. How the scrollbars and
fadelook is unchanged. - Docs: https://raft-design.botiverse.build/components/scroll-area/
Full Changelog: v0.75.0...v0.76.0