@botiverse/raft-ui 0.95.3 → 0.96.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -98
- package/dist/components/app-link-card/app-link-card.d.mts +17 -1
- package/dist/components/app-link-card/app-link-card.mjs +70 -2
- package/dist/components/app-link-card/index.d.mts +2 -2
- package/dist/components/combobox/combobox.mjs +1 -1
- package/dist/components/combobox-select/combobox-select.mjs +1 -1
- package/dist/components/context-menu/context-menu.mjs +1 -1
- package/dist/components/dialog/dialog-sheet-context.mjs +4 -2
- package/dist/components/dialog/dialog-sheet.mjs +9 -1
- package/dist/components/dialog/dialog.mjs +22 -10
- package/dist/components/drawer/drawer.d.mts +1 -1
- package/dist/components/drawer/drawer.mjs +17 -4
- package/dist/components/dropdown-menu/dropdown-menu.mjs +1 -1
- package/dist/components/lightbox/lightbox.d.mts +1 -1
- package/dist/components/lightbox/lightbox.mjs +11 -9
- package/dist/components/notification-center/notification-center.mjs +1 -1
- package/dist/components/popover/popover.mjs +1 -1
- package/dist/components/preview-card/preview-card.mjs +1 -1
- package/dist/components/select/select.mjs +1 -1
- package/dist/components/tooltip/tooltip.mjs +1 -1
- package/dist/lib/fill-contrast.mjs +63 -0
- package/dist/lib/frame-insets.mjs +56 -12
- package/dist/lib/overlay-depth.d.mts +5 -0
- package/dist/lib/overlay-depth.mjs +30 -11
- package/dist/lib/popup-boundary.d.mts +24 -0
- package/dist/lib/popup-boundary.mjs +256 -0
- package/dist/lint/no-raw-z-index.mjs +4 -4
- package/dist/lint/prefer-scroll-area.d.mts +60 -19
- package/dist/lint/prefer-scroll-area.mjs +393 -38
- package/dist/wip-exports.d.mts +6 -5
- package/dist/wip-exports.mjs +5 -4
- package/dist/wip.d.mts +3 -2
- package/package.json +1 -1
- package/skills/raft-ui-guide/SKILL.md +3 -1
- package/skills/raft-ui-guide/rules/layout.md +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
## [0.96.0](https://github.com/botiverse/raft-design/compare/v0.95.4...v0.96.0) (2026-11-13)
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- **`raft-ui/prefer-scroll-area` finds native scrollers it used to miss, and its message says what to write for the direction and the element** ([Use raft-ui › Lint rules](https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules)). It now reads a native-scrolling class in a list joined into a `const` (`[…].join(" ")`), in a `const` built from another `const`, in a member of an object or variant map of the same file that is read as a class (`cls.body`; every member when read as `VARIANTS[kind]`), in an argument of one call that isn't a class helper inside a class attribute (`className={wrap("overflow-y-auto")}`) or of the call a JSX spread spreads (`{...guard("overflow-x-auto")}`), in a function that returns the class (`className={(state) => …}`), in the default of a parameter with a class name (`{ className = "overflow-y-auto" }`), and in a `…Class`, `classNames` or `…ClassNames` prop; a `style` kept in a `const` object of the same file (`style={styles.pre}`) or under a `style` key (`{...{ style: { overflowY: "auto" } }}`), the value `overlay` and two values (`overflow: "hidden auto"`); and the arbitrary values `overflow-[auto]`, `overflow-y-[scroll]`, `overflow-[hidden_auto]` and `[overflow:overlay]`. A `const` is resolved by scope, so a parameter of the same name is not the const. The message gives the replacement: `<ScrollArea className="…sizing…" contentClassName="…layout, gap, padding…">` for `overflow-y-*`, with `orientation="horizontal"` for `overflow-x-*`, and with `orientation="both"` for `overflow-auto`, `overflow-scroll` or x and y classes together, since a vertical area cuts off content wider than itself. On a `<textarea>` it names `ScrollAreaTextarea`; on a `<pre>`, `<html>`, `<body>` or `<iframe>` it gives no replacement, only the exception comment; in a `classNames` object it names `useScrollAreaContainer` and `ScrollAreaGutters`; on a `ScrollArea` or `PanelScrollViewport` element's own class it says to remove the class, and names no exception. Each message ends with the address of ScrollArea's page as Markdown. The exception comment at the end of a line of code now covers that line only: it also covered the line after it, so a comment after one string of a class list silenced the next string.
|
|
6
|
+
- Why: apps that had the rule on kept shipping native scrollers in the shapes it didn't read, and the old message named ScrollArea without saying where the element's sizing and layout classes go, so a fix often left grid or gap classes on the area, where the gutters layer takes the first cell.
|
|
7
|
+
- Affects: an end-of-line `// raft-ui: native-scroll …` comment no longer also exempts the next line, so upgrading may surface reports that were hidden by accident. An app that extends the recommended config can see new reports after the upgrade, as errors. Replace each element as its message says ([Replacing a native scroller](https://raft-design.botiverse.build/components/scroll-area/#replacing-a-native-scroller)), or, for a region that must scroll natively, put `// raft-ui: native-scroll <reason>` on a line of its own right above the reported string or style property. A native scroller that was silenced only by a comment at the end of the line above is reported now: move the comment to its own line. Set `"raft-ui/prefer-scroll-area": "warn"` in the app's own `rules` while the reports are cleared. The message ids are `preferScrollArea`, `preferScrollAreaHorizontal`, `preferScrollAreaBoth`, `preferScrollAreaTextarea`, `preferScrollAreaContainer`, `nativeScrollElement` and `scrollAreaAlreadyScrolls`.
|
|
8
|
+
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **A popup opened in a sheet stays in the sheet, under its head** ([Dialog › Popups in a sheet](https://raft-design.botiverse.build/components/dialog/#popups-in-a-sheet), [Drawer › Behavior](https://raft-design.botiverse.build/components/drawer/#behavior)). In `DialogSheetContent` below 768px and in a `Drawer`, every raft-ui popup with no `collisionBoundary` of its own (Select, Combobox, ComboboxSelect, DropdownMenu, ContextMenu, Popover, PreviewCard, Tooltip, NotificationCenter) flips and sizes between the bottom of the sheet's head and the bottom of what is visible of the sheet (its bottom edge, or the top of the on-screen keyboard), instead of the window. It is decided for each popup as it is placed, and two are left to the window: one anchored in the head (a tooltip, a menu or a popover on a head button), and a list with less than 150px, about four rows, of that area on both sides of its field. `SheetPopupBoundary` (`@botiverse/raft-ui/wip`, props `head` and `surface`) does the same for a sheet an app draws itself. A Dialog, AlertDialog, Drawer or Lightbox opened from the sheet, and an app's own surface in `OverlayLayerProvider`, start without it.
|
|
13
|
+
- Why: on a phone with the keyboard up the sheet is all the screen there is, and a select list that flipped upward covered the head, or, portaled into the sheet's scrolling body, was drawn under it, where its first rows could be neither seen nor pressed.
|
|
14
|
+
- Affects: a list, a menu or a popover opened from a field in a phone sheet or a drawer that used to open over the head now flips or shrinks under it, when one side of the field has 150px of room; a popup on a head button, a centered dialog and everything outside a sheet are unchanged, and a `collisionBoundary` you pass still wins (`"clipping-ancestors"` opts a popup out). `OverlayLayerProvider` now resets the sheet's popup boundary, so `SheetPopupBoundary` must sit inside it: a boundary put around the provider no longer reaches the popups in it. An app that kept its own boundary for lists in a sheet (a context, a rect under the head, wrappers around `SelectContent` and `ComboboxContent` that pass it as `collisionBoundary`) can delete them: wrap its sheet's body in `SheetPopupBoundary`, or nothing at all in `DialogSheetContent` and `Drawer`.
|
|
15
|
+
- Docs: https://raft-design.botiverse.build/components/dialog/#popups-in-a-sheet
|
|
16
|
+
- **A popup given an `anchor` is bounded by a sheet according to that anchor, not its trigger** ([Dialog › Popups in a sheet](https://raft-design.botiverse.build/components/dialog/#popups-in-a-sheet)). Select, Combobox, ComboboxSelect, DropdownMenu, ContextMenu, Popover, PreviewCard, Tooltip and NotificationCenter pass their `anchor` to the sheet's boundary: an element, a ref, a virtual element or a function. A virtual element is in the sheet's head when the middle of its rect is. An `anchor` that is empty when the popup is placed falls back to the trigger found in the page.
|
|
17
|
+
- Why: the sheet decided by the open trigger it found in the page and ignored `anchor`, so a popup with no trigger, anchored to a button in the sheet's head, was held under the head, and one anchored in the body from a head trigger opened over the head.
|
|
18
|
+
- Affects: only a popup with an `anchor`, in `DialogSheetContent` below 768px, a `Drawer` or under `SheetPopupBoundary`: it now opens over the head or stays under it by where its anchor is. A `ContextMenu` with an `anchor` is decided by it as well; one opened at the pointer stays bounded as before. The trigger a sheet finds in the page is now found once a frame for each popup, not twice for each overflow check; the sheet is still measured at each check.
|
|
19
|
+
- Docs: https://raft-design.botiverse.build/components/dialog/#popups-in-a-sheet
|
|
20
|
+
- **A popup opened from a `layer={1}` dialog is drawn over the dialog** ([Elevation › Overlays](https://raft-design.botiverse.build/foundations/elevation/#overlays)). `DialogContent layer={1}` lifts its portal to `z-dialog-layer`, as a stacking context of its own (`relative isolate`, `data-layer="1"`), and its scrim and card stay at `z-dialog` inside it, the scrim under the card whatever `layer` its `overlay` props carry.
|
|
21
|
+
- Why: Base UI portals a select list or a menu opened from a dialog into the dialog's portal, at `z-popup` (50). With the card and the scrim themselves at 60 the list was drawn under both, and apps portaled each list into the dialog's scrolling body instead, where a ScrollArea's stacking context kept it under the sticky head.
|
|
22
|
+
- Affects: `DialogContent layer={1}`: the card and its scrim no longer carry `z-dialog-layer`, their portal does; the dialog is still above a `layer={0}` one. A `layer` passed in `overlay` beside `layer={1}` is ignored, and can be removed. A `container` passed to a popup only to keep it over a `layer={1}` dialog can be removed. `DialogOverlay layer={1}` used on its own is unchanged.
|
|
23
|
+
- Docs: https://raft-design.botiverse.build/foundations/elevation/#overlays
|
|
24
|
+
- **Popup boundaries stay in place in Safari on iOS while the keyboard has panned the page** ([Dialog › Popups in a sheet](https://raft-design.botiverse.build/components/dialog/#popups-in-a-sheet)). The boundary rects raft-ui builds, a sheet's and the frame insets' ([Page structure › Frame insets](https://raft-design.botiverse.build/patterns/page-structure/#frame-insets)), are moved by the visual viewport's offset in WebKit: twice for a popup fixed to the window, once for a sheet's popup that is positioned `absolute` or portaled into a transformed element.
|
|
25
|
+
- Why: `@floating-ui/dom` 1.8 subtracts the visual viewport's offset from a boundary given as a rect, and for a fixed popup whose offset parent is the window it also adds it to the trigger's rect, so the boundary sat up to twice the offset above where it was drawn and a list could run under a sheet's head again.
|
|
26
|
+
- Affects: WebKit only, and only while `visualViewport.offsetTop` or `offsetLeft` is not 0; elsewhere the rects are as before. A rect an app passes as `collisionBoundary` is not corrected; an app that added the offset to its own sheet boundary can drop that with the boundary. Checked by running the library's own `computePosition` and `detectOverflow` on a stubbed visual viewport, not on an iOS device.
|
|
27
|
+
- Docs: https://raft-design.botiverse.build/components/dialog/#popups-in-a-sheet
|
|
28
|
+
|
|
29
|
+
## [0.95.4](https://github.com/botiverse/raft-design/compare/v0.95.3...v0.95.4) (2026-11-13)
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **AppLinkCardBadge** (`@botiverse/raft-ui/wip`): the item's state on an AppLinkCard, a soft Badge of its `variant`, with `fill` for the app's color (`#RRGGBB`) ([AppLinkCard › The app's color](https://raft-design.botiverse.build/components/app-link-card/#the-apps-color)) ([#1024](https://github.com/botiverse/raft-design/pull/1024)). Only the fill changes: the type is black or white, whichever has the higher WCAG 2 contrast with it, the fill is the same in both modes, and a hairline ring (`line-hairline`) is added in the mode where the fill is below 1.5:1 against the card (`layer-card`). Without a valid `fill`, and in forced colors, it is the plain soft Badge.
|
|
34
|
+
- Why: an app may send a color with its state's badge, so a state such as Stamp's Merged reads the same in Raft as on the app's own pages.
|
|
35
|
+
- Affects: nothing until a card passes `fill`; without it the badge renders as `<Badge appearance="soft">` did.
|
|
36
|
+
- Docs: https://raft-design.botiverse.build/components/app-link-card/#the-apps-color
|
|
37
|
+
|
|
1
38
|
## [0.95.3](https://github.com/botiverse/raft-design/compare/v0.95.2...v0.95.3) (2026-11-12)
|
|
2
39
|
|
|
3
40
|
### Fixed
|
|
@@ -48,7 +85,7 @@
|
|
|
48
85
|
### Breaking
|
|
49
86
|
|
|
50
87
|
- **raft-ui no longer exports its own icons: import them from `@botiverse/ricons`** ([Icons](https://raft-design.botiverse.build/components/icons/)). `MessageIcon`, `ChatIcon`, `DirectMessageIcon`, `ThreadIcon`, `CommentIcon`, `FeedbackIcon`, `ArtifactIcon`, `FollowThreadIcon`, `UnfollowThreadIcon`, `JoinChannelIcon`, `LeaveChannelIcon`, `JoinPrivateChannelIcon`, `LeavePrivateChannelIcon`, `JoinJointChannelIcon`, `LeaveJointChannelIcon`, `FollowArtifactIcon`, `UnfollowArtifactIcon`, `AddCommentIcon`, `ReopenTaskIcon` and the types `ActionIconProps`, `ConversationIconProps` and `ObjectIconProps` moved to the new package `@botiverse/ricons` (packages/icons), with the same names, types and drawings. raft-ui depends on it for its own components and doesn't export them: the icons have one import path.
|
|
51
|
-
- Why: the icons are their own package, kept in this repository, so an app can use them without raft-ui; a second path through raft-ui would leave two ways to import the same icon
|
|
88
|
+
- Why: the icons are their own package, kept in this repository, so an app can use them without raft-ui; a second path through raft-ui would leave two ways to import the same icon.
|
|
52
89
|
- Affects: an import of any of these names from `@botiverse/raft-ui` no longer compiles. Add `@botiverse/ricons` to the app's dependencies and import them from it: `import { FollowThreadIcon, UnfollowThreadIcon, ThreadIcon } from "@botiverse/ricons";`. The `[data-icon-sizes]` rule (`UnfollowArtifactIcon`'s minus at 16px) moved to `@botiverse/ricons/styles.css`, which raft-ui's `styles.css` imports, so an app that loads raft-ui's stylesheet needs no other CSS.
|
|
53
90
|
- Docs: https://raft-design.botiverse.build/components/icons/
|
|
54
91
|
|
|
@@ -79,11 +116,11 @@
|
|
|
79
116
|
### Changed
|
|
80
117
|
|
|
81
118
|
- **`raft-ui/no-raw-z-index` is an error in the recommended config** ([Use raft-ui › Lint rules](https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules)). `recommended.json` and `configs.recommended` now turn it on as `"error"` (it was `"off"` since 0.85.0); every rule of the plugin is now an error in it.
|
|
82
|
-
- Why: slock has moved its z-index values to the layer names and turned the rule on as an error in its own config
|
|
119
|
+
- Why: slock has moved its z-index values to the layer names and turned the rule on as an error in its own config, and Stamp is doing the same.
|
|
83
120
|
- Affects: an app that extends the recommended config and still writes a z-index number gets an error for each one on the upgrade. Use a layer name ([Layer order](https://raft-design.botiverse.build/foundations/elevation/#layer-order)), or keep a number with a `// raft-ui: z-index <reason>` comment on the line above; `"raft-ui/no-raw-z-index": "warn"` in the app's own `rules` lowers it while the reports are cleared.
|
|
84
121
|
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
85
122
|
- **ScrollArea's scrollbars are at `z-scrollbar` (49), not 2147483647** ([ScrollArea › Rules](https://raft-design.botiverse.build/components/scroll-area/#rules)). The gutters layer takes `var(--z-index-scrollbar)`, the top in-page layer. The area's root is still `isolate`, so the value ranks the scrollbars only among the area's own content: they stay over every named layer inside it, sticky headings (`z-sticky`), raised parts, floating controls and chrome up to `z-app` (45). It was the last z-index number raft-ui kept for want of a name.
|
|
86
|
-
- Why: the large number was kept
|
|
123
|
+
- Why: the large number was kept only until the apps' layers inside their ScrollAreas had names, which slock has now done; a sticky heading at z-15 had covered the thumb, and every named in-page layer is below 49.
|
|
87
124
|
- Affects: nothing for content inside an area that uses the layer names. A part inside a ScrollArea with a z-index of 50 or more (an overlay's number, a raw value such as 100 or 1000) is now drawn over the scrollbar, where it used to be drawn under it; name it, or portal it if it is a popup. Nothing changes outside the area.
|
|
88
125
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/#rules, https://raft-design.botiverse.build/foundations/elevation/#layer-order
|
|
89
126
|
|
|
@@ -92,7 +129,7 @@
|
|
|
92
129
|
### Fixed
|
|
93
130
|
|
|
94
131
|
- **Components used only on an app's lazy pages stay out of the chunk that loads raft-ui first.** `dist/index.mjs` and `dist/wip.mjs` are now the drag Escape guard import plus `export * from "./index-exports.mjs"` / `"./wip-exports.mjs"`; the names live in those two modules, which are not listed in `sideEffects`. Exports and types are unchanged, and the guard still installs when raft-ui loads.
|
|
95
|
-
- Why: both entries are in `sideEffects` so a bundler keeps the guard import, but a module with side effects keeps every import it has, so Rolldown put every raft-ui component an app used anywhere (the task board, InboxItem, SelectionToolbar, Meter, Search…) into that first chunk. Raft Desktop's first screen grew past its size gate
|
|
132
|
+
- Why: both entries are in `sideEffects` so a bundler keeps the guard import, but a module with side effects keeps every import it has, so Rolldown put every raft-ui component an app used anywhere (the task board, InboxItem, SelectionToolbar, Meter, Search…) into that first chunk. Raft Desktop's first screen grew past its size gate; with this and slock's startup closure reading the barrel, it is about 10.7 KiB gzip smaller.
|
|
96
133
|
- Affects: nothing in an app's code. A bundler that follows `sideEffects` can now leave unused components out of the first chunk.
|
|
97
134
|
|
|
98
135
|
## [0.91.0](https://github.com/botiverse/raft-design/compare/v0.90.0...v0.91.0) (2026-11-10)
|
|
@@ -100,7 +137,7 @@
|
|
|
100
137
|
### Fixed
|
|
101
138
|
|
|
102
139
|
- **`FitToolbarSearch` cuts a placeholder that doesn't fit at a word: "Search…", not "Search …"** ([FitToolbar › Behavior](https://raft-design.botiverse.build/components/fit-toolbar/#behavior)). It measures the placeholder in the input's own font against the field's room, again when the field changes size, a font arrives or the placeholder changes, and shows the longest prefix that ends at a word (between any two characters in Chinese or Japanese) and fits with "…" after it, trailing spaces trimmed. A placeholder that fits is shown whole. `placeholder:text-ellipsis` is gone from the input; typed text still ends in an ellipsis.
|
|
103
|
-
- Why: CSS `text-overflow: ellipsis` cuts after the space and showed "Search …", which reads as a typo
|
|
140
|
+
- Why: CSS `text-overflow: ellipsis` cuts after the space and showed "Search …", which reads as a typo.
|
|
104
141
|
- Affects: a FitToolbarSearch whose placeholder is cut; its input's `placeholder` attribute is now the cut text. The accessible name is still the whole `aria-label`. Pages pass the whole placeholder as before.
|
|
105
142
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
|
|
106
143
|
|
|
@@ -109,7 +146,7 @@
|
|
|
109
146
|
### Changed
|
|
110
147
|
|
|
111
148
|
- **`FitToolbarSearch`'s least width is capped at 120px** ([FitToolbar › Behavior](https://raft-design.botiverse.build/components/fit-toolbar/#behavior)). `FIT_SEARCH_MAX_LEAST` is now 120 (was 160): the search icon, the field's side padding and "Search…", the same cap at every width. The measured least width is still the icon and the whole placeholder when that is smaller; a longer placeholder ends in an ellipsis.
|
|
112
|
-
- Why: slock's Members › Agents at 375px left "Agents ▾" under 160px and the search collapsed to its icon
|
|
149
|
+
- Why: slock's Members › Agents at 375px left "Agents ▾" under 160px and the search collapsed to its icon.
|
|
113
150
|
- Affects: a search whose placeholder takes more than 120px ("Search tasks" takes about 140px) now keeps its field down to 120px with the placeholder cut, and the toolbar's earlier steps stop sooner. An explicit `minWidth` is unchanged and not capped.
|
|
114
151
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
|
|
115
152
|
|
|
@@ -118,7 +155,7 @@
|
|
|
118
155
|
### Changed
|
|
119
156
|
|
|
120
157
|
- **`FitToolbarSearch` keeps at most 160px by default; a longer placeholder ends in an ellipsis** ([FitToolbar › Behavior](https://raft-design.botiverse.build/components/fit-toolbar/#behavior)). Its measured least width is now its icon and its whole placeholder, or 160px, whichever is smaller; the cap is exported as `FIT_SEARCH_MAX_LEAST`. A placeholder that doesn't fit the field is cut with an ellipsis (`text-overflow: ellipsis` on the input and its `::placeholder`) and the field stays shown instead of collapsing to its button.
|
|
121
|
-
- Why: on a 375px phone the Members toolbar's "Search members" (16px on a touch screen, so iOS doesn't zoom) needed about 180px where about 165px was free, so the search collapsed to its icon; the placeholder is only a hint and goes once the person types
|
|
158
|
+
- Why: on a 375px phone the Members toolbar's "Search members" (16px on a touch screen, so iOS doesn't zoom) needed about 180px where about 165px was free, so the search collapsed to its icon; the placeholder is only a hint and goes once the person types.
|
|
122
159
|
- Affects: a search whose placeholder takes more than 160px now keeps its field down to 160px, and the toolbar's earlier steps stop sooner. A placeholder that fits is measured as before. An explicit `minWidth` is unchanged and not capped.
|
|
123
160
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
|
|
124
161
|
|
|
@@ -127,7 +164,7 @@
|
|
|
127
164
|
### Added
|
|
128
165
|
|
|
129
166
|
- **`FitToolbarSplitButton`: a split button as one FitToolbar item** ([FitToolbar › Anatomy](https://raft-design.botiverse.build/components/fit-toolbar/#anatomy)). The primary action and its ▾ (Create agent and the other ways to add an agent) are `Button`'s split parts, `ButtonSplit`, `ButtonSplitMain` and `ButtonSplitMenu`, each rendered as a toolbar button. The pair takes its steps as a whole: from `plusOrder` on the main part is + alone, from `labelOrder` on its icon alone (only with an `icon`), its label kept as its name and tooltip, and the ▾ stays. It never moves into More. `groupLabel` names the pair, `menuLabel` the ▾, and `menuTrigger` is the menu's trigger (`<DropdownMenuTrigger />`); `disabled` disables both parts, and other props go on the main part. In the toolbar the arrow keys reach the main part, then the ▾; the toolbar stays one Tab stop.
|
|
130
|
-
- Why: slock's Members toolbar built Create agent from two `FitToolbarButton`s with the split's classes copied onto them: no die-cut edge in dark mode, and the parts gave way one by one
|
|
167
|
+
- Why: slock's Members toolbar built Create agent from two `FitToolbarButton`s with the split's classes copied onto them: no die-cut edge in dark mode, and the parts gave way one by one.
|
|
131
168
|
- Affects: nothing existing. Move a hand-built split in a FitToolbar onto it.
|
|
132
169
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
|
|
133
170
|
|
|
@@ -142,7 +179,7 @@
|
|
|
142
179
|
### Added
|
|
143
180
|
|
|
144
181
|
- **ListRow `tone="warning"` and `tone="danger"`: an action's risk** ([An action's risk](https://raft-design.botiverse.build/components/list-row/#an-actions-risk)). An action row in a danger zone, such as Archive, Leave or Delete at the end of a channel's details, colors its title and its leading `ListRowIcon` `warning-strong` (it interrupts but can be undone) or `danger-strong` (it cannot be undone), with Button's names and meanings. Nothing else changes: the description, the value, the chevron and the hover and press fills stay as on any row. Only an interactive row takes it (a display-only row ignores it: only an action has a risk), and a disabled row greys everything as for every tone. The row still carries `data-tone`. The new `RiskTone` type (`"warning" | "danger"`) is exported beside `RowTone`; `SidebarItem` keeps `RowTone`.
|
|
145
|
-
- Why: slock's channel details panel colored these rows by hand (`text-danger-strong` on the row, `text-current` on the title and icon), so the color, its contrast and the disabled state were each product's to get right (slock channel details
|
|
182
|
+
- Why: slock's channel details panel colored these rows by hand (`text-danger-strong` on the row, `text-current` on the title and icon), so the color, its contrast and the disabled state were each product's to get right (slock channel details). The strong inks keep at least 4.5:1 on every surface, hovered or pressed, in both modes and every theme (`list-row.risk-contrast.test.ts`).
|
|
146
183
|
- Affects: `ListRowIcon` now reads `text-(--list-row-risk-ink,var(--list-row-ink-3))` instead of `text-(--list-row-ink-3)`, and `ListRowTitle` gains `text-(--list-row-risk-ink)`; both render as before on a row without a risk tone (the title inherits the row's ink, as it did). A row colored with a class of its own keeps working; move it onto `tone`. Feedback, Forms and Color now name the danger zone's colored rows as the one place an action is colored before its confirmation; a menu item still isn't.
|
|
147
184
|
- Docs: https://raft-design.botiverse.build/components/list-row/#an-actions-risk
|
|
148
185
|
|
|
@@ -155,8 +192,8 @@
|
|
|
155
192
|
- `FitToolbarGroup` `moreOrder` and `moreItem`: a group of how-to-show controls (sort, grouping, view) moves into More together at that step, each as its `moreItem` (a view switch as a radio choice in the menu).
|
|
156
193
|
- `FitToolbarGroup` `end`: an item pinned at the end of a scrolling group (`overflow="scroll"`), such as a filter line's Clear all ×. Without `overflow="scroll"` it is not rendered, and development warns.
|
|
157
194
|
- `FitToolbarMore` and `FitToolbarMoreItems`: the More (⋯) button, shown only while something is in it, and the items it holds. Its label comes from the page, in the page's language ("More" by default, in English).
|
|
158
|
-
- Why: a list toolbar with nothing at the start of a line left its buttons hanging, and the search was the first thing squeezed out when a page narrowed (
|
|
159
|
-
- Affects: nothing for an existing toolbar. Step names (`label`, `more`, `collapse`, `wrap`, `plus`) and their order are unchanged, and toolbars that use none of the new parts lay out as before. slock's Tasks, Members, Computers, Search and Apps move onto the new parts in their own PR
|
|
195
|
+
- Why: a list toolbar with nothing at the start of a line left its buttons hanging, and the search was the first thing squeezed out when a page narrowed (rule in #1158).
|
|
196
|
+
- Affects: nothing for an existing toolbar. Step names (`label`, `more`, `collapse`, `wrap`, `plus`) and their order are unchanged, and toolbars that use none of the new parts lay out as before. slock's Tasks, Members, Computers, Search and Apps move onto the new parts in their own PR.
|
|
160
197
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
|
|
161
198
|
|
|
162
199
|
## [0.85.0](https://github.com/botiverse/raft-design/compare/v0.84.0...v0.85.0) (2026-11-10)
|
|
@@ -164,8 +201,8 @@
|
|
|
164
201
|
### Added
|
|
165
202
|
|
|
166
203
|
- **Named layers, `z-below` to `z-drag`, and the lint rule `raft-ui/no-raw-z-index`**: every z-index has a name and a place in one order ([Layer order](https://raft-design.botiverse.build/foundations/elevation/#layer-order)). The names are ordinary Tailwind `z-*` utilities from `--z-index-*` theme variables in `styles.css` (`@theme static`, so CSS can always read `var(--z-index-sticky)`): in the page `z-below` -1, `z-base` 0, `z-raised` 1, `z-control` 10, `z-sticky` 20, `z-float` 30, `z-chrome` 35, `z-cover` 40, `z-app` 45, `z-scrollbar` 49; overlays `z-popup` and `z-dialog` 50, `z-dialog-layer` 60, `z-lightbox` 70, `z-toast` 80, `z-drag` 999. They take every variant (`max-md:z-chrome`, `before:z-below`), and `cn` and the recipes merge them with a number an app passes (`z-popup` and `z-40` keep `z-40`). raft-ui's own components use them. The new rule reports a z class with a number or an arbitrary value (`z-10`, `-z-1`, `z-[60]`), a numeric `zIndex` in a style object, a `zIndex={n}` prop and `el.style.zIndex = n`; a number kept on purpose takes a `// raft-ui: z-index <reason>` comment on the line above. `z-index-named.test.ts` holds raft-ui's own code to the same rule, CSS included, with its kept exceptions listed.
|
|
167
|
-
- Why: slock alone writes 186 z-index values, and the same role has up to five numbers (sticky headers at 2, 10, 15, 20 and 30) while one number has up to seven roles; a share lightbox at 100 hides its own toast, and a badge menu at 1000 sits above the drag lift
|
|
168
|
-
- Affects: nothing on the upgrade for an app that extends `recommended.json`: the rule is `"off"` in it for now, so an app that lints with `--max-warnings=0` (slock) stays green. It turns on there as `"error"` once slock has moved to the names
|
|
204
|
+
- Why: slock alone writes 186 z-index values, and the same role has up to five numbers (sticky headers at 2, 10, 15, 20 and 30) while one number has up to seven roles; a share lightbox at 100 hides its own toast, and a badge menu at 1000 sits above the drag lift.
|
|
205
|
+
- Affects: nothing on the upgrade for an app that extends `recommended.json`: the rule is `"off"` in it for now, so an app that lints with `--max-warnings=0` (slock) stays green. It turns on there as `"error"` once slock has moved to the names. An app can opt in early with `"raft-ui/no-raw-z-index": "warn"` or `"error"` in its own `rules`. Map each value with the table in Elevation: a local lift is `z-raised` inside an `isolate` root (there is no third local level: order a third part by DOM order); a popup, menu, dialog, lightbox, toast or drag copy takes raft-ui's component and no z-index of its own. Nothing may sit above the toasts (80) or the drag lift (999). slock's InlineBadgeEditor (1000) and SelectShareLightbox (100) are not exceptions: they move onto `Popover` and `z-lightbox` when slock moves to the names.
|
|
169
206
|
- Docs: https://raft-design.botiverse.build/foundations/elevation/#layer-order, https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
170
207
|
|
|
171
208
|
### Changed
|
|
@@ -175,7 +212,7 @@
|
|
|
175
212
|
- Up: MessageList's jump controls and SelectionToolbar 10 → 30 (`z-float`), the pull-to-refresh indicator 10 → 20 (`z-sticky`), PanelHeader's sticky phone header 30 → 35 (`z-chrome`), ThreadPanel's header 20 → 35 (inside the panel's `isolate`), AppShellOverlaySlot 20 → 40 (`z-cover`).
|
|
176
213
|
- Down: MessageItem's hover toolbar 20 → 10 and ComposerAttachment's remove button 20 → 10 (`z-control`), the composer attachment row's arrows 30 → 10 (outside the row's ScrollArea, so still over it). The sidebar header mask -10 → -1 (`z-below`).
|
|
177
214
|
- Isolation: a tab is now `isolate` instead of `z-10`, and a dragged tab is lifted by `var(--z-index-raised)` instead of 20, all inside the tab list's `isolate`. TaskDraggable is `isolate`, so a card's own parts rank only inside the card, and a dragged card at `z-raised` is above every other card.
|
|
178
|
-
- Why: the scale has one number per role; the old numbers made the same role differ between components and forced slock to workarounds (a day heading at `z-15`, an `isolate` around the artifacts dashboard, `max-md:z-40` beside the phone header)
|
|
215
|
+
- Why: the scale has one number per role; the old numbers made the same role differ between components and forced slock to workarounds (a day heading at `z-15`, an `isolate` around the artifacts dashboard, `max-md:z-40` beside the phone header).
|
|
179
216
|
- Affects: an app layer that sat between the old and the new number of a raft-ui layer it overlaps. In slock, move these in the same upgrade: a cover at `z-30` beside a PanelHeader (MainLayout, ProfilePanel, ChannelSideArea, LegacyTaskPanel, TaskConversationDialog, `index.css`) to `z-cover`, or the header, now 35, is drawn over it; the typing line (`z-6`) to `z-float`, so it stays over the end fade and beside the jump pills; a hit target at `z-10` in a message (MessageItem) to `z-raised`, so the hover toolbar, now 10, stays above it; ThreadsInbox's day heading (`z-15`) and "new" pill (`z-20`) to `z-sticky` and `z-float`, which InboxItem's parts at 1 now allow.
|
|
180
217
|
- Docs: https://raft-design.botiverse.build/foundations/elevation/#in-page-layers
|
|
181
218
|
|
|
@@ -184,50 +221,50 @@
|
|
|
184
221
|
### Added
|
|
185
222
|
|
|
186
223
|
- **LightboxBody, the scrolling body of a document-mode lightbox** (from `@botiverse/raft-ui/wip`): a ScrollArea under `LightboxToolbar` that fills the rest of the content (`min-h-0 flex-1`), takes the pointer back from `LightboxContent` (`pointer-events-auto`), and never passes a scroll on to the page under the scrim (`overscroll-contain`), on `layer-canvas`, the toolbar's surface. `className` sets another surface (`bg-layer-sidebar`); every other ScrollArea prop, `render` included, is the area's, except `overscroll`. It carries `data-slot="lightbox-body"`. It is a tab stop even when it has nothing to scroll (`tabIndex={0}`), with the inset focus ring (a 2px `foreground` outline inside its edge, after keyboard input only) instead of ScrollArea's ring outside it, which the window's edges hid. Its content is padded by `env(safe-area-inset-left|right|bottom)`, and `contentClassName` and `contentStyle` style a box of the caller's inside that (`data-slot="lightbox-body-content"`), so a phone held sideways doesn't put a wide file under the notch or its end under the home bar, and the scrollbar stays at the window's edge.
|
|
187
|
-
- Why: slock's FileViewerShell and SelectShareLightbox each write a lightbox's scrolling body of their own
|
|
224
|
+
- Why: slock's FileViewerShell and SelectShareLightbox each write a lightbox's scrolling body of their own. A ScrollArea stops a scroll only while it has something to scroll, so a body too short to scroll passed the wheel on to the page under the scrim, which document mode doesn't lock.
|
|
188
225
|
- Affects: nothing until a lightbox uses it. The document-mode example on the Lightbox page now does, with the same DOM and classes as before apart from `data-slot`, `overscroll-contain`, `tabIndex` and the focus ring, and the safe-area box its `contentClassName` now sits in.
|
|
189
226
|
- Docs: https://raft-design.botiverse.build/components/lightbox/#document-mode
|
|
190
227
|
- **Lightbox document mode.** `<Lightbox modal="trap-focus">` traps focus without locking the page's scroll, for a file read top to bottom whose own body scrolls (a PDF, Markdown, a diagram); the content stays neither `inert` nor hidden, so find in page reaches it. In document mode `LightboxContent` takes Base UI's own focus defaults: focus moves into the lightbox when it opens and back to `LightboxTrigger` when it closes. A lightbox opened from state passes `finalFocus={openerRef}` to send focus back to what opened it.
|
|
191
|
-
- Why: slock's FileViewerShell (images and attachment previews: PDF, documents, Mermaid) and SelectShareLightbox move onto raft-ui's Lightbox, and they kept their own body-scroll switch and focus return
|
|
228
|
+
- Why: slock's FileViewerShell (images and attachment previews: PDF, documents, Mermaid) and SelectShareLightbox move onto raft-ui's Lightbox, and they kept their own body-scroll switch and focus return.
|
|
192
229
|
- Affects: nothing changes for a lightbox until it passes `modal="trap-focus"`; `modal` keeps its default, `true`, and in it `initialFocus` and `finalFocus` stay `false`.
|
|
193
230
|
- Docs: https://raft-design.botiverse.build/components/lightbox/#document-mode
|
|
194
231
|
- **`attached` on ListRow: a row that belongs to the setting above it**: in a `ListRowGroup`, an `attached` row draws no hairline above it, and a `plain` one has no top padding, so it follows the row it belongs to with only that row's 12px bottom inset between them. An interactive one (a chevron, a menu) keeps the control height as its least, so it stays a 44px target on a phone and a touch screen (34px with a mouse on a desktop, which it already is). The row carries `data-attached`. Tie it to its setting for assistive technology with the setting's title id: a display-only attached row takes `role="group"` and `aria-labelledby`, an interactive one `aria-describedby`; ListRow can't know which title it belongs to. The row above keeps its height, and its switch stays centered on its own title and description; the row after draws its hairline as usual.
|
|
195
|
-
- Why: a hairline says "another setting", so quiet hours' start and end times, or a server's channel overrides under its level, read as settings of their own
|
|
232
|
+
- Why: a hairline says "another setting", so quiet hours' start and end times, or a server's channel overrides under its level, read as settings of their own.
|
|
196
233
|
- Affects: nothing until a row passes `attached`: without it, `ListRow` renders the same DOM and classes as before. slock's quiet hours times, its channel overrides and the message font size preview should take it.
|
|
197
234
|
- Docs: https://raft-design.botiverse.build/components/list-row/#a-row-that-belongs-to-the-setting-above, https://raft-design.botiverse.build/patterns/forms/#a-settings-row
|
|
198
235
|
- **FitToolbar's `plus` step: the primary action as + alone** (from `@botiverse/raft-ui/wip`). `FitToolbarButton` takes `plusOrder`: from that step on it is a + square, its icon and label gone, the label kept as its accessible name and shown as its tooltip; it carries `data-fit-plus`, and `useFitItem({ plus })` gives any item the step. Every item's steps share one order for the toolbar, by their numbers (lower first; at the same number the last item first, and one item's steps as `label`, `more`, `collapse`, `wrap`, `plus`). Give `plus` a higher number than any `wrap`, so the primary becomes + only once the groups have wrapped. `FitToolbarButton`'s `icon` is optional now, for a primary that has none until it is +.
|
|
199
|
-
- Why: Tasks at 375px and 420px: once the filters have wrapped to their own scrolling line, the first line (Mine and All with their counts, sort, list or board, Create task) fits only with Create task as
|
|
236
|
+
- Why: Tasks at 375px and 420px: once the filters have wrapped to their own scrolling line, the first line (Mine and All with their counts, sort, list or board, Create task) fits only with Create task as +. The step was left out of 0.83.0 for want of a test.
|
|
200
237
|
- Affects: nothing until a button passes `plusOrder`: without it, `FitToolbarButton` renders the same DOM and classes as before.
|
|
201
238
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/#behavior
|
|
202
239
|
- **`messageCard` and `messageEmbed`, from `@botiverse/raft-ui`**: the recipes every card in a message is drawn from, so an app's own card takes the same parts. `messageCard()` gives `source` (with `sourceIcon`, `sourceName`, `sourceHost`, `sourceHostText`), `sourceFooter`, `body`, `eyebrow`, `title`, `heading`, `meta`, `actions`, `actionRow`, `inset`, `actionGroup` (the buttons as one item of a row that also holds a status line) and `popover`; `messageEmbed({ size })` the frame and its widths, which `MessageEmbed size` applies.
|
|
203
|
-
- Why: the rule for a card in a message is to take its inset, title, details and buttons from these recipes, never classes of its own; slock's Artifact, audio and app message cards can't follow it without them
|
|
240
|
+
- Why: the rule for a card in a message is to take its inset, title, details and buttons from these recipes, never classes of its own; slock's Artifact, audio and app message cards can't follow it without them.
|
|
204
241
|
- Affects: nothing until an app uses them.
|
|
205
242
|
- Docs: https://raft-design.botiverse.build/patterns/cards-in-messages/#one-place-for-the-styles
|
|
206
243
|
- **`size="item"` on PopoverContent**: a popover about an app's item is 400px wide (never wider than the room the positioner leaves it, so on a phone the screen less its padding), and its `PopoverBody`, `PopoverTitle` and `PopoverActions` take a card's parts: the 12px inset, the `text-subhead` bold title, one row of buttons. The content carries `data-size="item"`.
|
|
207
|
-
- Why: the question an app's link asks before it shows previews stands in for the item card it leads to, so it looks like that card
|
|
244
|
+
- Why: the question an app's link asks before it shows previews stands in for the item card it leads to, so it looks like that card; a popover belongs to the family of what it is about.
|
|
208
245
|
- Affects: nothing until a popover passes `size="item"`; the default renders the same DOM and classes as before. slock's link-preview question should take it.
|
|
209
246
|
- Docs: https://raft-design.botiverse.build/components/popover/#about-an-item
|
|
210
247
|
- **FitToolbarPicker and FitToolbarButton's `collapsedIcon`, for the Tasks filters** (from `@botiverse/raft-ui/wip`). `FitToolbarPicker` is a picker's trigger as one toolbar item: pass the popup's trigger as `trigger` (`<DropdownMenuTrigger />`, `<PopoverTrigger />`) with an `icon`, a label and what is chosen (`value`); it is a PickerTriggerButton, filled `info-soft` once a value is chosen (an applied filter, Select › Filters); a count is set in Space Mono and any other value in the label's font. From `labelOrder` on its label gives way and the icon, the value and the chevron stay (👤 1 ⌄), named "Assignee 1". `FitToolbarButton`'s `collapsedIcon` shows only once its label has given way, in place of `icon` (Clear filters becomes ×). A `FitToolbarButton` with neither refuses its `label` step.
|
|
211
|
-
- Why: the Tasks filters are pickers that must keep what is chosen in view once they narrow, and Clear filters reads as text while it has room
|
|
248
|
+
- Why: the Tasks filters are pickers that must keep what is chosen in view once they narrow, and Clear filters reads as text while it has room.
|
|
212
249
|
- Affects: nothing until a page uses them. A `FitToolbarButton` without `collapsedIcon` renders the same DOM and classes as before, plus `data-fit-needs-icon`.
|
|
213
250
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/#anatomy
|
|
214
251
|
|
|
215
252
|
### Changed
|
|
216
253
|
|
|
217
254
|
- **Toasts are drawn above a lightbox**: the toast viewport and an anchored toast's positioner move from `z-70` to `z-80`, above the lightbox's `z-70`. Elevation now writes the order down ([Layer order](https://raft-design.botiverse.build/foundations/elevation/#layer-order)).
|
|
218
|
-
- Why: a toast fired from inside a lightbox (Link copied, a failed download from FileViewerShell's toolbar) was drawn under it, and in document mode the lightbox's body is opaque, so it couldn't be seen
|
|
255
|
+
- Why: a toast fired from inside a lightbox (Link copied, a failed download from FileViewerShell's toolbar) was drawn under it, and in document mode the lightbox's body is opaque, so it couldn't be seen.
|
|
219
256
|
- Affects: every toast; it is drawn as before over the page, the popups and the dialogs. Nothing else sits between `z-70` and `z-80`.
|
|
220
257
|
- Docs: https://raft-design.botiverse.build/foundations/elevation/#layer-order, https://raft-design.botiverse.build/components/toast/#where-it-appears
|
|
221
258
|
|
|
222
259
|
- **SaveStatus shows only a failure, and takes no room otherwise**: an empty `SaveStatus` is hidden (`empty:hidden`) and has no height, so a setting with it is as tall as one without it. With a `SaveStatusError` in it, the line appears 4px under the control or the description, as before, and the setting grows by it. `SaveStatusSaved` is deprecated: it renders nothing, whatever its children (no check, no "Saved", no live region). `ListRow variant="plain"` no longer leaves a SaveStatus line out of what its ends center on: its ends center on the whole row, with an error shown too.
|
|
223
|
-
- Why: a setting that applies at once needs no "Saved": the control showing its new value is the confirmation, as in macOS's and iOS's settings. The reserved, usually empty line made those rows taller than their neighbors
|
|
260
|
+
- Why: a setting that applies at once needs no "Saved": the control showing its new value is the confirmation, as in macOS's and iOS's settings. The reserved, usually empty line made those rows taller than their neighbors. This replaces the reserved "Saved" line chosen on 2026-10-30.
|
|
224
261
|
- Affects: every `SaveStatus` while it holds no error, and every plain `ListRow` whose body ends in one (slock's `ImmediateSaveStatus` and `ImmediateSwitchRow`, its Language & region, onboarding, member permissions, server translation and system channels settings, the composer's send key and the channel's thread sort): the row or field is shorter by the line and its 4px, and a switch at a row's end moves up by half of that. A failure now grows the setting by its line, and a row's ends move down by half of it. Drop `SaveStatusSaved` and its "Saved" copy; render `SaveStatusError` when a change failed and nothing otherwise. A plain row without a SaveStatus is unchanged except that it no longer carries the `--list-row-status-space` classes. slock, on upgrading:
|
|
225
262
|
- `SETTINGS_STACK_CARD_DESKTOP` (settingsCardClasses.ts:12) still draws a stacked card's last SaveStatus inside the 16px below it (`[&_[data-slot=save-status]:last-child]:mt-0` and `mb-[calc(-1lh)]`, the 2026-11-04 rule this replaces). With an error shown, its 18px line overlaps the next setting by 2px, or runs into the card's padding, and the setting doesn't grow. On staging only PublicVisibilitySection takes the class, with no SaveStatus, so nothing shows it yet; drop the two classes.
|
|
226
263
|
- `settingsImmediateApply.behavior.test.tsx` fails: `assertIdleLine` looks for the "Saved" live region (`getByRole("status")`, `data-slot=save-status-saved`), and "Saved shows the check inside the same line" waits for "Saved". Assert the idle line is hidden and empty instead, and drop the Saved test. The "saved" state in ImmediateSetting.tsx becomes dead; `SETTING_SAVED_MS` is also used by AppNotificationsControls.tsx, so keep it while that does.
|
|
227
264
|
- Docs: https://raft-design.botiverse.build/components/save-status/, https://raft-design.botiverse.build/patterns/forms/#settings-that-apply-at-once
|
|
228
265
|
|
|
229
266
|
- **Cards in messages take their styles from one place**: MessageEmbed's recipe now holds the card frame and its two widths, `size="item"` (at most 400px, the message's width below 768px) and `size="content"` (at most 560px), and the `messageCard` recipe beside it holds the parts every card and item popover shares: the source row, the body and its 12px inset, the id line, the title (`text-subhead`, 2 lines), the state and details row and the buttons row (at most two buttons; a third action goes in a More menu the card supplies; a status line or note wraps onto its own line before the buttons do, and they go under each other, whole, only in a column narrower than both). AppLinkCard, PreviewCard's body parts, MessageActionCard, MessageForwardedBundle and MessageReplies are drawn from them, and a test (`card-styles.test.ts`) fails, naming the file, line and class, on a card or popover component that writes its own radius, width, inset or title size.
|
|
230
|
-
- Why: the same frame had widths from 176 to 576px, insets of 16, 12 and 10px and titles of 17 and 15px; a card's style changed alone and drifted from the others (
|
|
267
|
+
- Why: the same frame had widths from 176 to 576px, insets of 16, 12 and 10px and titles of 17 and 15px; a card's style changed alone and drifted from the others (change one card and the others follow).
|
|
231
268
|
- Affects: `MessageEmbed` without `size` renders as before.
|
|
232
269
|
- AppLinkCard: its header, body and buttons sit 12px from the edges instead of 16px, and its title is `text-subhead` (15px on a phone, 14px on a desktop) instead of `text-headline` (17/16px), so the card is shorter.
|
|
233
270
|
- PreviewCard: `PreviewCardBody`, `PreviewCardActions` and `PreviewCardFooter` take the same 12px, and a `PreviewCardTitle` in a body the same title and its link styles. This is every card that uses them, the item card (`size="item"`) and a default-size card that puts its sentence in `PreviewCardBody`, such as slock's 300px connect-account card. A profile card (`PreviewCardHeader`, `PreviewCardDescription`) is unchanged.
|
|
@@ -237,51 +274,51 @@
|
|
|
237
274
|
|
|
238
275
|
### Fixed
|
|
239
276
|
|
|
240
|
-
- **Lightbox document mode: the keys scroll the document, never the page under the scrim**: with `initialFocus` unset, a document lightbox with a `LightboxBody` focuses the body when opened by a click or a key, so Page Down, the arrow keys and Space scroll the file at once; before, focus landed on the first toolbar control (Close), and those keys scrolled the page under the scrim while the file stayed put. A tap still focuses the lightbox itself, and a lightbox without a body still focuses its first control. A key that would scroll the page from elsewhere in the lightbox (a toolbar button, the lightbox itself after a tap) scrolls `LightboxBody` instead, by the browser's own steps, and does nothing with a body of the caller's own; keys a control uses (a text field's, a button's Space, a menu's arrows) are left to it
|
|
277
|
+
- **Lightbox document mode: the keys scroll the document, never the page under the scrim**: with `initialFocus` unset, a document lightbox with a `LightboxBody` focuses the body when opened by a click or a key, so Page Down, the arrow keys and Space scroll the file at once; before, focus landed on the first toolbar control (Close), and those keys scrolled the page under the scrim while the file stayed put. A tap still focuses the lightbox itself, and a lightbox without a body still focuses its first control. A key that would scroll the page from elsewhere in the lightbox (a toolbar button, the lightbox itself after a tap) scrolls `LightboxBody` instead, by the browser's own steps, and does nothing with a body of the caller's own; keys a control uses (a text field's, a button's Space, a menu's arrows) are left to it.
|
|
241
278
|
- Affects: document-mode lightboxes with a `LightboxBody` (none in slock yet). An `initialFocus` of the caller's still wins.
|
|
242
279
|
- Docs: https://raft-design.botiverse.build/components/lightbox/#accessibility
|
|
243
|
-
- **Lightbox document mode focuses as Base UI does when opened by a tap**: with `initialFocus` unset, a document lightbox opened by a tap focuses itself, not its first toolbar control, so a phone shows no ring and opens no keyboard; a click or a key still focuses the first control
|
|
244
|
-
- **The drag hint is drawn with the lifted copy, at `999`**: it sat at `z-70` with the lightbox, where the DOM order decided which was on top; elevation.md's Layer order lists it
|
|
245
|
-
- **TaskBoard:** `TaskBoardColumnChevron` draws a `ChevronDown` when given no children, so a column heading always shows whether the column is open. slock passed it empty, so no column had the arrow and a collapsed Closed column read as "Closed 2" over nothing
|
|
280
|
+
- **Lightbox document mode focuses as Base UI does when opened by a tap**: with `initialFocus` unset, a document lightbox opened by a tap focuses itself, not its first toolbar control, so a phone shows no ring and opens no keyboard; a click or a key still focuses the first control.
|
|
281
|
+
- **The drag hint is drawn with the lifted copy, at `999`**: it sat at `z-70` with the lightbox, where the DOM order decided which was on top; elevation.md's Layer order lists it.
|
|
282
|
+
- **TaskBoard:** `TaskBoardColumnChevron` draws a `ChevronDown` when given no children, so a column heading always shows whether the column is open. slock passed it empty, so no column had the arrow and a collapsed Closed column read as "Closed 2" over nothing.
|
|
246
283
|
|
|
247
284
|
- **What opens inside a lightbox is drawn above it, and a lightbox opened from a dialog has its scrim**: `LightboxPortal` is a stacking context of its own at `z-70` (`relative isolate z-70`), with the scrim and the content inside it in document order. Base UI already put a Tooltip, Popover, menu and its submenus, Select, Combobox, PreviewCard, Dialog, AlertDialog or Drawer opened inside `LightboxContent` in the lightbox's portal; now its own `z-50` counts inside the lightbox, above the content. The lightbox's scrim is always rendered (`forceRender`), and the lightbox joins the stack of modal surfaces: opened from a dialog it draws `layer-backdrop-nested` (`data-nested`), and a Dialog, AlertDialog or Drawer opened inside it draws the nested scrim over it.
|
|
248
|
-
- Why: the scrim and the content sat at `z-70` and `z-71` among the page's layers, so a popup opened inside, at `z-50` in the lightbox's portal, was drawn under the scrim, and a press on it landed on the scrim and closed the lightbox. And Base UI renders no backdrop for a dialog opened under another, so a lightbox opened from a dialog had no scrim: the dialog showed through undimmed, and a press beside the file went to the dialog
|
|
285
|
+
- Why: the scrim and the content sat at `z-70` and `z-71` among the page's layers, so a popup opened inside, at `z-50` in the lightbox's portal, was drawn under the scrim, and a press on it landed on the scrim and closed the lightbox. And Base UI renders no backdrop for a dialog opened under another, so a lightbox opened from a dialog had no scrim: the dialog showed through undimmed, and a press beside the file went to the dialog.
|
|
249
286
|
- Affects: every lightbox. The DOM is the same; the portal element gains `relative isolate z-70`, the scrim loses `z-70` and the content `z-71`, and a scrim stacked on another surface carries `data-nested`. A popup given a `container` outside the lightbox, or an app's own portal to the body, is drawn under the lightbox, as before. `LightboxPortalProps` takes a string `className`, as `LightboxContentProps` does.
|
|
250
287
|
- Docs: https://raft-design.botiverse.build/components/lightbox/#popups-inside, https://raft-design.botiverse.build/components/lightbox/#behavior
|
|
251
288
|
- **A cover or an excerpt no longer cuts off a card's corners**: `MessageEmbedContent` that is the card's first or last part takes the card's inner corner, `radius-container` less the 1px border, on the two corners it shares with the card, and clips what it holds to them (`overflow: clip`). A cover at the top is round at the top and square at its foot. The card itself still clips nothing, so a title link's focus ring around the whole card (its `::after`) still shows.
|
|
252
|
-
- Why: an Artifact card's cover or excerpt ran square to the card's edges and covered the border's arc, so its top corners looked cut off, in light and dark
|
|
289
|
+
- Why: an Artifact card's cover or excerpt ran square to the card's edges and covered the border's arc, so its top corners looked cut off, in light and dark. The docs' link card had the same fault.
|
|
253
290
|
- Affects: every card on MessageEmbed whose first or last part is `MessageEmbedContent`: slock's Artifact card, the docs' link card, and MessageForwardedBundle's items when the bundle has no footer (they now clip at the card's bottom corners). Content between two rows is unchanged, and so is MessageQuotedPreview: its frame is a square bar (`data-frame="bar"` on its root and on `MessageQuotedPreviewLoading`), so its content stays square and clips nothing.
|
|
254
291
|
- Docs: https://raft-design.botiverse.build/components/message-embed/#anatomy
|
|
255
292
|
- **FitToolbar measures only what is laid out in its rows**: it leaves out what is hidden, out of flow (`position: absolute` or `fixed`) or of no size, and looks through `display: contents` on its lines too. A line has broken only where an item sits wholly below or above the first on it, so items of different heights on one line (a primary Button beside 32px filters) no longer read as a broken line.
|
|
256
|
-
- Why: a popover or menu opened from a toolbar item puts Base UI focus guards beside its trigger, fixed at the window's corner, and the toolbar counted them: opening it made labels give way
|
|
293
|
+
- Why: a popover or menu opened from a toolbar item puts Base UI focus guards beside its trigger, fixed at the window's corner, and the toolbar counted them: opening it made labels give way. And a wrapping toolbar whose first line mixed heights wrapped or gave way where it fit.
|
|
257
294
|
- Affects: every FitToolbar and SelectionToolbar. Opening a popup from one no longer changes it; a wrapping toolbar with items of different heights on a line gives way less, only where it doesn't fit.
|
|
258
295
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/#behavior
|
|
259
296
|
- **DialogFooter and AlertDialogFooter decide whether to stack from the buttons laid out in their row only**: they leave out what is hidden, out of flow (`position: absolute` or `fixed`) or of no size, and look through `display: contents`, as FitToolbar does (one shared measure). There is no new prop.
|
|
260
|
-
- Why: an open Menu or Popover puts Base UI focus guards beside its trigger, and a Select or Combobox always adds a hidden input, each fixed at the window's corner. The footer counted them as the row's start, so the row reached to the window's left edge: a menu opened from the footer stacked the buttons at any width, and a Select in the footer stacked them for good, nothing open
|
|
297
|
+
- Why: an open Menu or Popover puts Base UI focus guards beside its trigger, and a Select or Combobox always adds a hidden input, each fixed at the window's corner. The footer counted them as the row's start, so the row reached to the window's left edge: a menu opened from the footer stacked the buttons at any width, and a Select in the footer stacked them for good, nothing open.
|
|
261
298
|
- Affects: a footer holding a Menu or Popover trigger, a Select or a Combobox now stacks only when its buttons don't fit. A button inside a `display: contents` wrapper now counts toward the row; before, the wrapper read as no size and its button was left out. Every other footer is unchanged.
|
|
262
299
|
- Docs: https://raft-design.botiverse.build/components/dialog/#the-footer, https://raft-design.botiverse.build/patterns/responsive/#switch-on-the-regions-width
|
|
263
300
|
- **BannerAction comes back beside the text with a menu open from it, or a Select in it**: once the actions had taken a line of their own, they measured their width with a Range over all their contents. They now measure from their first laid-out item's left to the last one's right, with the same shared measure. There is no new prop.
|
|
264
|
-
- Why: the Range took in an open menu's focus guards and a Select's hidden input, fixed at the window's corner, so the actions seemed as wide as the distance to the window's left edge: they stayed under the text while a menu was open, and for good with a Select in them
|
|
301
|
+
- Why: the Range took in an open menu's focus guards and a Select's hidden input, fixed at the window's corner, so the actions seemed as wide as the distance to the window's left edge: they stayed under the text while a menu was open, and for good with a Select in them.
|
|
265
302
|
- Affects: a banner whose actions hold a Menu or Popover trigger, a Select or a Combobox. Actions of plain buttons measure the same width as before.
|
|
266
303
|
- Docs: https://raft-design.botiverse.build/components/banner/#anatomy, https://raft-design.botiverse.build/patterns/responsive/#switch-on-the-regions-width
|
|
267
304
|
- **A sorted board column shows that it is the place while a card is over it**: `TaskDropZone` takes `dragInto`. With it, the zone carries `data-drag-into` and takes `fill-subtle` while a card it allows is over it, its cards and all; never at rest, and never while it refuses the card (`dropState` `blocked`).
|
|
268
|
-
- Why: Drag and drop says a column whose order isn't the person's to set (sorted by last update, by due date) is itself the place and takes `fill-subtle` while the item is over it, with no placeholder between its items. TaskDropZone had no way to: a sorted column with cards showed nothing while a card was over it, only an empty one its placeholder (
|
|
305
|
+
- Why: Drag and drop says a column whose order isn't the person's to set (sorted by last update, by due date) is itself the place and takes `fill-subtle` while the item is over it, with no placeholder between its items. TaskDropZone had no way to: a sorted column with cards showed nothing while a card was over it, only an empty one its placeholder (the channel Tasks panel shares the column).
|
|
269
306
|
- Affects: nothing until a `TaskDropZone` passes `dragInto`: without it, every zone renders the same DOM and classes as before, and a column whose cards are drop targets keeps its placeholder as the one signal.
|
|
270
307
|
- Docs: https://raft-design.botiverse.build/components/task-dnd/#states, https://raft-design.botiverse.build/components/task-board/#states, https://raft-design.botiverse.build/patterns/drag-and-drop/#where-it-will-land
|
|
271
308
|
- **FitToolbar's focus follows the reading order, and a SegmentedControl in it is one toolbar item**: the arrow keys go through its controls line by line as they show, and Tab first enters at the first control on screen. A SegmentedControl inside registers as one item of the toolbar, so the toolbar stays one Tab stop and its arrow keys reach the segments without choosing one (Space chooses; Up and Down choose nothing). When the focused item moves into More, focus goes to More instead of the page. Items of the same kind in one group (the same `label` or `plus` step at the same number) change form at the same time. A SegmentedControl of text alone, or a FitToolbarButton with no icon, refuses its `label` step. There is no new prop.
|
|
272
|
-
- Why: a wrapped group moves with CSS `order`, later on screen than in the DOM, and the browser's Tab order and Base UI's toolbar followed the DOM: focus jumped to the second line and back (WCAG 2.4.3). The SegmentedControl was a second Tab stop whose arrow keys chose as they moved, and Mine and All, with no icons, emptied to nothing at their `label` step; at 780px three filters became icons while Assignee kept its label
|
|
309
|
+
- Why: a wrapped group moves with CSS `order`, later on screen than in the DOM, and the browser's Tab order and Base UI's toolbar followed the DOM: focus jumped to the second line and back (WCAG 2.4.3). The SegmentedControl was a second Tab stop whose arrow keys chose as they moved, and Mine and All, with no icons, emptied to nothing at their `label` step; at 780px three filters became icons while Assignee kept its label.
|
|
273
310
|
- Affects: every FitToolbar and SelectionToolbar: arrow-key order is the screen's, which is the DOM's unless a group has wrapped. A SegmentedControl in a FitToolbar is no longer a Tab stop of its own. Outside a FitToolbar nothing changes.
|
|
274
311
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/#accessibility, https://raft-design.botiverse.build/components/segmented-control/#accessibility
|
|
275
312
|
- **A FitToolbar's scrolling group scrolls only as its last state, and stays where the person scrolled it**: while it can still wrap, it counts as not fitting whenever its items overflow, so its labels give way and it wraps before it scrolls. Each measure keeps its scroll position, only the item that just became active comes into view (on mount the first active one), not every active one with the first winning, and a resize after the person has scrolled leaves it there. There is no new prop.
|
|
276
|
-
- Why: at 780px the filters scrolled on the first line, the last ones hidden past the fade, where giving their labels fit them all; and a count changing or a measure pulled the group back to the active filter under the person's finger
|
|
313
|
+
- Why: at 780px the filters scrolled on the first line, the last ones hidden past the fade, where giving their labels fit them all; and a count changing or a measure pulled the group back to the active filter under the person's finger.
|
|
277
314
|
- Affects: a FitToolbar with `wrap` and an `overflow="scroll"` group that can wrap now gives its labels and wraps sooner. Every scrolling group keeps its scroll position across a measure.
|
|
278
315
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/#behavior
|
|
279
316
|
|
|
280
317
|
### Design System
|
|
281
318
|
|
|
282
|
-
- **A setting that applies at once shows no "Saved" and reserves no line
|
|
319
|
+
- **A setting that applies at once shows no "Saved" and reserves no line**: the control showing its new value is the confirmation; only a failure appears under it, saying what failed, while the control goes back to its old value. Where a section's settings are stored ("Saved on this device") is said once for the section, not on every row. This replaces the way chosen on 2026-10-30 ("Saved" for a moment, on a line kept when empty) and the settings row's centering on its title and description above that line (2026-11-09).
|
|
283
320
|
- Docs: https://raft-design.botiverse.build/patterns/forms/#settings-that-apply-at-once, https://raft-design.botiverse.build/foundations/alignment/#a-control-and-its-label
|
|
284
|
-
- **A section of settings may hold more than one group, split by topic
|
|
321
|
+
- **A section of settings may hold more than one group, split by topic**: in Appearance, the message font size and its preview are one `ListRowGroup` and the display switches another, 12px apart (`stack-y-3`) under the section's one header, with no hairline joining them. `ListRowGroup` is unchanged: it sets no space around itself, and the column of groups sets it.
|
|
285
322
|
- Docs: https://raft-design.botiverse.build/components/list-row/#more-than-one-group-in-a-section, https://raft-design.botiverse.build/patterns/forms/#a-settings-row
|
|
286
323
|
|
|
287
324
|
## [0.83.0](https://github.com/botiverse/raft-design/compare/v0.82.0...v0.83.0) (2026-11-10)
|
|
@@ -289,40 +326,40 @@
|
|
|
289
326
|
### Added
|
|
290
327
|
|
|
291
328
|
- **`placement="top"` on DialogContent**, on a desktop and a tablet (and on DialogSheetContent there): the dialog stays centered across, its top edge at `var(--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 from `size`. The content carries `data-placement="top"`. To anchor the card to something, the app measures it and sets `--dialog-top` in the content's `style`; raft-ui measures nothing. Below 768px it does nothing.
|
|
292
|
-
- 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
|
|
329
|
+
- 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).
|
|
293
330
|
- Affects: nothing until a dialog passes `placement="top"`: `"center"`, the default, renders the same DOM and classes as before.
|
|
294
331
|
- Docs: https://raft-design.botiverse.build/components/dialog/#at-the-top
|
|
295
332
|
- **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-height` below 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 carries `data-soft-keyboard`. A phone sheet doesn't move; it keeps its own keyboard box.
|
|
296
|
-
- 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 (
|
|
333
|
+
- 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 (centered unless the keyboard is open). slock did it with its own `narrow="top"`; that maps to the default now.
|
|
297
334
|
- Affects: every centered `DialogContent` below 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's `narrow="top"`) can drop that code.
|
|
298
335
|
- Docs: https://raft-design.botiverse.build/components/dialog/#the-keyboard
|
|
299
336
|
- **SelectionToolbar** (from `@botiverse/raft-ui/wip`): the floating toolbar for selected items (messages, tasks), in the BatchBar style. `SelectionToolbarDock` floats it 12px above the bottom (plus the safe area) and 20px from the sides; `SelectionToolbarCount` (with the number in `SelectionToolbarCountValue`, Space Mono), `SelectionToolbarHint`, `SelectionToolbarActions` with `SelectionToolbarAction` (`secondary`, `menu`, `moreItem`), `SelectionToolbarMore` with `SelectionToolbarMoreItems` in its menu, `SelectionToolbarItem` for another control such as a select-all checkbox, and `SelectionToolbarClose`. A floating panel's frame (`line-seam` ring, `shadow-float`) as tall as a head: 44px with `md` buttons 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.
|
|
300
|
-
- Why: the toolbar for selected items is one bar shared by message multi-select and task bulk actions, in
|
|
337
|
+
- Why: the toolbar for selected items is one bar shared by message multi-select and task bulk actions, in the BatchBar style: `md` ghost buttons with a 14px icon and a label, no primary; by touch it aligns with the head, so a phone fits the tasks' Status, Assignee and Nudge at 360px.
|
|
301
338
|
- Affects: nothing yet. The stable `MessageMultiSelectToolbar` and `MessageMultiSelectToolbarActions` keep their earlier bar until SelectionToolbar leaves wip; move to it from `@botiverse/raft-ui/wip` when you are ready.
|
|
302
339
|
- Docs: https://raft-design.botiverse.build/components/selection-toolbar/, https://raft-design.botiverse.build/components/#toolbar-for-selected-items
|
|
303
340
|
- **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) and `wrap` (a group to a line of its own, with `wrap`; groups with the same `wrapLine` share one, and a wrapped group gets back what its items gave before it wrapped). Items declare their steps and order with `useFitItem`, or are a `FitToolbarButton`; a `FitToolbarGroup overflow="scroll"` scrolls sideways instead, its `data-active` items kept in view; groups are `FitToolbarGroup` (`firstLine`, `wrapOrder`, `wrapLine`, `collapsed`, `collapseOrder`); More takes `useFitMore` and lists `FitToolbarMoreItems`. It measures its own region's width, never the window's, again when its content changes on its own, and writes each step as `data-fit-<step>`. A SegmentedControl inside it follows its `label` step; its own `iconsOnlyBelow` is off there.
|
|
304
|
-
- 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
|
|
341
|
+
- 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 (Tasks, Members and the like), instead of a breakpoint guess or a hook per app. Its step names and API stay in wip until those toolbars have confirmed them.
|
|
305
342
|
- Docs: https://raft-design.botiverse.build/components/fit-toolbar/
|
|
306
343
|
|
|
307
344
|
### Changed
|
|
308
345
|
|
|
309
346
|
- **DialogSheetContent, DialogCommit and DialogSheetBack are stable**: import them, and their props types, from `@botiverse/raft-ui`. `@botiverse/raft-ui/wip` no longer exports them.
|
|
310
|
-
- Why: slock is moving its own dialogs, its phone sheets among them, onto raft-ui
|
|
347
|
+
- Why: slock is moving its own dialogs, its phone sheets among them, onto raft-ui, and a product builds on the stable entry.
|
|
311
348
|
- Affects: an import from `@botiverse/raft-ui/wip` fails; import from `@botiverse/raft-ui` instead. No app imported them from `/wip` (slock draws its sheets itself today), and the docs' examples have moved.
|
|
312
349
|
- Docs: https://raft-design.botiverse.build/components/dialog/#making-a-dialog-a-sheet
|
|
313
350
|
|
|
314
351
|
### Fixed
|
|
315
352
|
|
|
316
353
|
- **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 to `overflow-wrap: anywhere` breaks where it must, and doesn't decide. There is no new prop.
|
|
317
|
-
- 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 (
|
|
354
|
+
- 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 (the Computer page's Detected Runtimes chip).
|
|
318
355
|
- 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.
|
|
319
356
|
- Docs: https://raft-design.botiverse.build/components/description-list/#labels-go-above-only-when-a-value-doesnt-fit
|
|
320
357
|
- **The server tab's popover keeps one whole server row and opens on the current server**: `MobileNavServerPopoverList` is never shorter than one two-line row. Where the footer actions and the pages would leave it less inside 60% of the screen, `MobileNavServerPopoverContent` grows 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 with `aria-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.
|
|
321
|
-
- 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
|
|
358
|
+
- 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.
|
|
322
359
|
- 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; pass `initialFocus` to 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 of `mobile-nav-server-popover-content` no longer matches them. Mark the current server's row with `aria-current="true"` for the scroll and the focus to find it.
|
|
323
360
|
- Docs: https://raft-design.botiverse.build/components/mobile-nav/#the-server-tab
|
|
324
361
|
- **A settings row's switch centers on its text, not on the SaveStatus line under it**: in a `ListRow variant="plain"` whose `ListRowBody` ends with a `SaveStatus`, 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.
|
|
325
|
-
- 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
|
|
362
|
+
- 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).
|
|
326
363
|
- Affects: in such rows the ends move up by half the reserved line (11px). A row without a `SaveStatus` is 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 30px switch keeps 12px above it, so that row is 76px instead of 70px. A local wrapper that put an invisible line under the switch to compensate (slock's) now lifts it too far: remove it.
|
|
327
364
|
- Docs: https://raft-design.botiverse.build/components/list-row/#anatomy, https://raft-design.botiverse.build/components/save-status/#behavior
|
|
328
365
|
|
|
@@ -331,14 +368,14 @@
|
|
|
331
368
|
### Changed
|
|
332
369
|
|
|
333
370
|
- **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.
|
|
334
|
-
- Why: the previous unrelated otter drawing and sleeping wait pose did not match Ottie or the recent-activity card
|
|
371
|
+
- Why: the previous unrelated otter drawing and sleeping wait pose did not match Ottie or the recent-activity card.
|
|
335
372
|
- 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.
|
|
336
373
|
- Docs: https://raft-design.botiverse.build/components/steps/#ottie-illustration
|
|
337
374
|
|
|
338
375
|
### Breaking
|
|
339
376
|
|
|
340
377
|
- **`ComposerDropOverlayPrompt` takes its text as `children`, 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.
|
|
341
|
-
- Why: the component library carried product copy in English that no app could translate
|
|
378
|
+
- Why: the component library carried product copy in English that no app could translate.
|
|
342
379
|
- 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 uses `ComposerDropOverlayLabel` instead.
|
|
343
380
|
- Docs: https://raft-design.botiverse.build/components/composer/
|
|
344
381
|
|
|
@@ -348,19 +385,19 @@
|
|
|
348
385
|
- `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 and `text-footnote`. It follows the row's tone and disabled state with the title. `ListRowDescription` stays the place for a setting's consequence.
|
|
349
386
|
- ChoiceList items take `meta`: lines of data people compare across rows (a quota, free disk, a count, a status), in `text-footnote` in `foreground-muted` as the description is, after the `description` lines. 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.
|
|
350
387
|
- `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 the `MessageActionCardHeaderDescription` line it is placed in ("Approved by Martin · 10:51").
|
|
351
|
-
- 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's `description`, `MessageActionCardHeaderDescription`) because there was nowhere else for it; these parts give it a place before those parts are kept for explanations
|
|
388
|
+
- 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's `description`, `MessageActionCardHeaderDescription`) because there was nowhere else for it; these parts give it a place before those parts are kept for explanations.
|
|
352
389
|
- Affects: nothing until an app uses them. Data moved into `ListRowSubtitle` gets darker and a size smaller than in `ListRowDescription`; data moved into a ChoiceList item's `meta` looks as it did in `description`; a time moved into `MessageActionCardHeaderMeta` is set in Space Mono at the hint tier.
|
|
353
390
|
- 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
|
|
354
391
|
|
|
355
392
|
### Fixed
|
|
356
393
|
|
|
357
394
|
- **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.
|
|
358
|
-
- 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
|
|
395
|
+
- 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).
|
|
359
396
|
- 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.
|
|
360
397
|
- Docs: https://raft-design.botiverse.build/components/resizable/#behavior
|
|
361
398
|
|
|
362
399
|
- **ScrollArea measures in the frame after mounting, not while React commits**: where a `ResizeObserver` watches the sizes, the first read of the container's `clientHeight` and `scrollHeight` waits for the next animation frame, as every later one already did. Without a `ResizeObserver` it still measures at once.
|
|
363
|
-
- 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
|
|
400
|
+
- 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.
|
|
364
401
|
- 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 `onEdgeChange` is first called in it: code that reads them in a layout effect right after mounting finds them unset, and can call `actionsRef.measure` for a measure at once.
|
|
365
402
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/
|
|
366
403
|
|
|
@@ -369,7 +406,7 @@
|
|
|
369
406
|
### Fixed
|
|
370
407
|
|
|
371
408
|
- **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 its `z-index`, no longer covers the thumb, with sticky and absolute gutters alike, and the scrollbars still never paint over anything outside the area.
|
|
372
|
-
- 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
|
|
409
|
+
- 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. Starting the scrollbar below the stuck heading (`railInset`) fixed only the heading at the top, not the ones scrolling past.
|
|
373
410
|
- 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 now `DragLift` by 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.
|
|
374
411
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/
|
|
375
412
|
- **`DragLift` renders the copy in `document.body` by default**: the lifted copy goes through a portal to the body unless `container` names another element, or `null` keeps it in place; the portal keeps the theme scope it was rendered in. `TaskDragOverlay` follows, as `SortableTabsList` and `SortableComposerAttachments` already did.
|
|
@@ -377,7 +414,7 @@
|
|
|
377
414
|
- Affects: a `DragLift` or `TaskDragOverlay` without `container` now 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. Pass `container={null}` for the old in-place copy. How the copy looks is unchanged.
|
|
378
415
|
- Docs: https://raft-design.botiverse.build/components/drag/#anatomy
|
|
379
416
|
- **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.
|
|
380
|
-
- 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
|
|
417
|
+
- 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.
|
|
381
418
|
- 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.
|
|
382
419
|
- Docs: https://raft-design.botiverse.build/components/drag/#anatomy
|
|
383
420
|
- **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.
|
|
@@ -390,14 +427,14 @@
|
|
|
390
427
|
### Changed
|
|
391
428
|
|
|
392
429
|
- **A panel head is one line from 768px by touch too**: `PanelHeaderContent` puts the `PanelMeta` after 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.
|
|
393
|
-
- 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
|
|
430
|
+
- 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.
|
|
394
431
|
- 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.
|
|
395
432
|
- Docs: https://raft-design.botiverse.build/components/panel/#anatomy
|
|
396
433
|
|
|
397
434
|
### Fixed
|
|
398
435
|
|
|
399
436
|
- **A gallery row of three images sits three across**: `MessageImageGalleryRow` with 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.
|
|
400
|
-
- Why: six images showed as rows of 2, 1, 2, 1 and read as an F
|
|
437
|
+
- Why: six images showed as rows of 2, 1, 2, 1 and read as an F.
|
|
401
438
|
- 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.
|
|
402
439
|
- Docs: https://raft-design.botiverse.build/components/message-attachment/#rules
|
|
403
440
|
|
|
@@ -406,7 +443,7 @@
|
|
|
406
443
|
### Changed
|
|
407
444
|
|
|
408
445
|
- **A sidebar row out of view isn't laid out or painted**: `SidebarItem` sets `content-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 2px `overflow-clip-margin`.
|
|
409
|
-
- Why: a server with hundreds of channels relaid every row whenever the window's width changed, about 115ms per relayout at 300 channels
|
|
446
|
+
- Why: a server with hundreds of channels relaid every row whenever the window's width changed, about 115ms per relayout at 300 channels. On a 300-row sidebar, a width sweep's layout time drops from 95–122ms to 29–33ms (70 width changes, 3 runs each).
|
|
410
447
|
- Affects: nothing visible. The selected row's outer sticker edge, which reaches 1.5px past the row, still paints. A browser without `overflow-clip-margin` keeps laying every row out. Something an app draws past a row's box by more than 2px gets clipped.
|
|
411
448
|
- Docs: https://raft-design.botiverse.build/components/sidebar/#behavior
|
|
412
449
|
|
|
@@ -415,7 +452,7 @@
|
|
|
415
452
|
### Changed
|
|
416
453
|
|
|
417
454
|
- **`raft-ui/prefer-scroll-area` also reads class lists kept in a const and a `className` property**: a native-scrolling class in a `const` of the same file is reported when the const is used as a class there (`const log = "max-h-64 overflow-auto"`, then `className={log}` or `cn(log)`), wherever in the file it is declared; and so is one in a `className`, `class` or `*ClassName` property, such as a recipe slot's option (`content({ className: "overflow-y-auto" })`). The escape comment works on them as before. ([#1078](https://github.com/botiverse/raft-design/pull/1078))
|
|
418
|
-
- Why: apps keep many class lists in consts; Stamp's port of the rule found four native scrollers this way that the rule missed
|
|
455
|
+
- Why: apps keep many class lists in consts; Stamp's port of the rule found four native scrollers this way that the rule missed.
|
|
419
456
|
- 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.
|
|
420
457
|
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
421
458
|
|
|
@@ -424,7 +461,7 @@
|
|
|
424
461
|
### Fixed
|
|
425
462
|
|
|
426
463
|
- **The lint plugin's recommended config keeps the app's own Oxlint plugins**: `recommended.json` sets `"plugins": []`. ([#1076](https://github.com/botiverse/raft-design/pull/1076))
|
|
427
|
-
- Why: an extended config without `plugins` counts as Oxlint's default plugins, so extending it turned `unicorn` back on in an app that lists its plugins without it (36 new warnings in slock
|
|
464
|
+
- Why: an extended config without `plugins` counts as Oxlint's default plugins, so extending it turned `unicorn` back on in an app that lists its plugins without it (36 new warnings in slock). Same on Oxlint 1.85, 1.87 and 1.91.
|
|
428
465
|
- Affects: an app that extends `recommended.json` keeps exactly the `plugins` it lists; an app that lists none keeps Oxlint's defaults, as before. The ESLint config is unchanged.
|
|
429
466
|
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
430
467
|
|
|
@@ -433,7 +470,7 @@
|
|
|
433
470
|
### Changed
|
|
434
471
|
|
|
435
472
|
- **Select, Combobox and ComboboxSelect lists scroll in a ScrollArea**: each list is a `useScrollAreaContainer` container 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](https://github.com/botiverse/raft-design/pull/1074))
|
|
436
|
-
- 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
|
|
473
|
+
- 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.
|
|
437
474
|
- Affects: the scrollbar is the thin overlay thumb on every platform. `SelectList` / `ComboboxList` render a frame `<div>` around the list element; `ref`, `className`, `style` and the other props still go to the list. CSS or tests that expected the list to be the popup's direct child, or waited on `document.getAnimations()` to finish (the thumb runs on a scroll timeline), need updating.
|
|
438
475
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/
|
|
439
476
|
|
|
@@ -442,7 +479,7 @@
|
|
|
442
479
|
### Fixed
|
|
443
480
|
|
|
444
481
|
- **Select and Combobox lists never scroll sideways**: a Select list reaches into the popup's 4px inset and pads it back, so a `SelectSeparator` meets the popup's edge and stays inside the list, and Select and Combobox lists set `overflow-x-hidden`. ([#1070](https://github.com/botiverse/raft-design/pull/1070))
|
|
445
|
-
- 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
|
|
482
|
+
- 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. The list also clipped the line 4px short of the popup's edge.
|
|
446
483
|
- 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.
|
|
447
484
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/
|
|
448
485
|
|
|
@@ -451,14 +488,14 @@
|
|
|
451
488
|
### Changed
|
|
452
489
|
|
|
453
490
|
- **VirtualList writes no `--virtual-list-width`**: the list no longer writes its width as a CSS variable on its scroller, and `refreshListWidth()` on its imperative API does nothing (deprecated; it will be removed). ([#1068](https://github.com/botiverse/raft-design/pull/1068))
|
|
454
|
-
- 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
|
|
491
|
+
- 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.
|
|
455
492
|
- Affects: CSS that read `var(--virtual-list-width)` gets nothing; read the width with the list's `onResize` or a ResizeObserver. Neither raft-ui nor the Raft app read it.
|
|
456
493
|
- Docs: https://raft-design.botiverse.build/components/virtual-list/
|
|
457
494
|
|
|
458
495
|
### Fixed
|
|
459
496
|
|
|
460
497
|
- **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`, `-left` and `-right`) go on the gutters layer, and with `fade` the 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 (`useScrollAreaContainer` on a VirtualList) restyled every message on every frame of a window drag. ([#1068](https://github.com/botiverse/raft-design/pull/1068))
|
|
461
|
-
- 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
|
|
498
|
+
- 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.
|
|
462
499
|
- 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 `fade` look is unchanged.
|
|
463
500
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/
|
|
464
501
|
|
|
@@ -467,14 +504,14 @@
|
|
|
467
504
|
### Changed
|
|
468
505
|
|
|
469
506
|
- **A horizontal DescriptionList puts its labels above their values only when a value doesn't fit**: the list measures its labels and values and keeps each label beside its value while every value fits on one line there; once one doesn't (but would on a line of its own), or the value column would be narrower than 160px, the whole list stacks. It no longer stacks below a fixed 24rem (384px), and is no longer a size container. MessageActionCard's properties follow the same rule. ([#1065](https://github.com/botiverse/raft-design/pull/1065))
|
|
470
|
-
- Why: an agent's Role, Computer and Model stacked in a 370px profile though they fit beside their labels; the switch should follow the content, not a width picked for the longest label
|
|
507
|
+
- Why: an agent's Role, Computer and Model stacked in a 370px profile though they fit beside their labels; the switch should follow the content, not a width picked for the longest label.
|
|
471
508
|
- Affects: short facts in panels from about 300px to 384px, and on a phone, now stand beside their labels. A list whose values are wide still stacks, at the width they stop fitting.
|
|
472
509
|
- Docs: https://raft-design.botiverse.build/components/description-list/#labels-go-above-only-when-a-value-doesnt-fit
|
|
473
510
|
|
|
474
511
|
### Fixed
|
|
475
512
|
|
|
476
513
|
- **Chinese text on Linux uses Noto Sans CJK**: the Simplified Chinese fallback lists `Noto Sans CJK SC`, the name Linux distributions install it under, after `Noto Sans SC`, as the Traditional Chinese list already did with `Noto Sans CJK TC`. ([#1063](https://github.com/botiverse/raft-design/pull/1063))
|
|
477
|
-
- Why: on a Linux machine with only the distribution's CJK font, Chinese and its full-width punctuation fell to the system's last-resort face, whose colon sits centered with space around it (
|
|
514
|
+
- Why: on a Linux machine with only the distribution's CJK font, Chinese and its full-width punctuation fell to the system's last-resort face, whose colon sits centered with space around it (seen in the read line's screenshots from CI).
|
|
478
515
|
- Affects: Linux desktops and CI screenshots; Mac and Windows already matched PingFang SC or Microsoft YaHei first.
|
|
479
516
|
- Docs: https://raft-design.botiverse.build/foundations/typography/
|
|
480
517
|
|
|
@@ -483,21 +520,21 @@
|
|
|
483
520
|
### Added
|
|
484
521
|
|
|
485
522
|
- **Lint rules for apps**: `@botiverse/raft-ui/lint` is a lint plugin with the design system's rules for app code, in the ESLint plugin format, which Oxlint loads as a JS plugin. Its first rule, `raft-ui/prefer-scroll-area`, reports a class or an inline style that makes an element scroll natively (`overflow-auto`, `overflow-scroll` and their `-x-` and `-y-` forms, with any variant) where an app writes it, unless a `// raft-ui: native-scroll <reason>` comment on the line above says which exception it is. The comment covers the string on the next line only, not the rest of a multi-line `cn(...)`, so a class added to the list later is still checked. Turn on every rule at once by extending `./node_modules/@botiverse/raft-ui/dist/lint/recommended.json` from `.oxlintrc.json` (or `configs.recommended` in an ESLint flat config); a rule added later reaches the app with the upgrade. ([#1058](https://github.com/botiverse/raft-design/pull/1058), [#1059](https://github.com/botiverse/raft-design/pull/1059))
|
|
486
|
-
- Why: the design system's rules should reach the apps, and the agents working in them, with raft-ui itself rather than through each repository's instructions; every region that scrolls is a ScrollArea, and nothing outside raft-ui checked it
|
|
523
|
+
- Why: the design system's rules should reach the apps, and the agents working in them, with raft-ui itself rather than through each repository's instructions; every region that scrolls is a ScrollArea, and nothing outside raft-ui checked it.
|
|
487
524
|
- Affects: nothing until an app turns the plugin on. Then each native scroller in its code is reported: make it a ScrollArea (or `PanelScrollViewport` in a Panel), or add the comment with its reason.
|
|
488
525
|
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
489
526
|
|
|
490
527
|
### Changed
|
|
491
528
|
|
|
492
529
|
- **useScrollAreaContainer gives its container the overflow**: the classes in `containerProps.className` make the element scroll in the `orientation`, as ScrollArea's own container does (vertical: `overflow-y: auto` and `overflow-x: hidden`; horizontal: the other way round; both: `auto`), so the element needs no overflow class of its own and nothing for `raft-ui/prefer-scroll-area` to report. `ComposerInput` and `ActivityInboxViewport` no longer carry `overflow-y-auto`. ([#1059](https://github.com/botiverse/raft-design/pull/1059))
|
|
493
|
-
- Why: the hook asked the app to add a native overflow class, which the new lint rule reports
|
|
530
|
+
- Why: the hook asked the app to add a native overflow class, which the new lint rule reports.
|
|
494
531
|
- Affects: apps can drop the overflow class from a `useScrollAreaContainer` element. One they keep still wins (the hook's rule has no specificity), so nothing breaks. A vertical container that had only `overflow-y-auto` now clips sideways (`overflow-x: hidden`) instead of scrolling sideways with no scrollbar; pass `orientation="both"` for content that scrolls both ways.
|
|
495
532
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/#your-own-container
|
|
496
533
|
|
|
497
534
|
### Design System
|
|
498
535
|
|
|
499
536
|
- **A thread's timeline**: conversation.md lists what a thread's timeline shows (the message it started from and every message a person wrote), when it appears, the rail for a mouse, the Timeline entry in the thread's More menu on every device, a half-height sheet on a phone and a popover wider, and how its items are marked. No raft-ui component changed. ([#1057](https://github.com/botiverse/raft-design/pull/1057))
|
|
500
|
-
- Why: a long thread buries the few messages people wrote under agents' work
|
|
537
|
+
- Why: a long thread buries the few messages people wrote under agents' work.
|
|
501
538
|
- Docs: https://raft-design.botiverse.build/patterns/conversation/#a-threads-timeline
|
|
502
539
|
|
|
503
540
|
## [0.73.0](https://github.com/botiverse/raft-design/compare/v0.72.0...v0.73.0) (2026-11-08)
|
|
@@ -505,14 +542,14 @@
|
|
|
505
542
|
### Changed
|
|
506
543
|
|
|
507
544
|
- **Every region that scrolls is a ScrollArea**: `PanelScrollViewport`, `ActivityInboxViewport`, `SearchShellViewport`, `NotificationCenterScroller`, `FilesViewport`, `TasksPanelViewport`, ChoiceList's scroller, `MobileNavServerPopoverList`, `ComposerSuggestionList`, `AnnouncementDialogBody`, `MessageActionCardScopeListViewport` and `DialogNav` show ScrollArea's overlay scrollbar instead of the platform's. Native scrolling stays only in the places scroll-area.md lists, each with its reason, and a test fails on any other. ([#1055](https://github.com/botiverse/raft-design/pull/1055))
|
|
508
|
-
- Why: one look for scrollbars on every platform and theme, and a scrollbar that takes no room from a list's 8px inset
|
|
545
|
+
- Why: one look for scrollbars on every platform and theme, and a scrollbar that takes no room from a list's 8px inset.
|
|
509
546
|
- Affects: **keyboard order**: a `PanelScrollViewport` whose content overflows is now a Tab stop of its own, before the controls inside it (a scrolling region must be reachable by keyboard). Check tests that depend on Tab order when upgrading. `PanelScrollViewport`'s `data-slot` (`panel-scroll-viewport`) replaces ScrollArea's own `scroll-area` on that element, so a selector for `[data-slot=scroll-area]` doesn't match it. On a phone MessageList keeps its native scroller. ChoiceList, DialogNav, the mobile server list and the composer suggestions keep their scroller out of the Tab order (`tabIndex={-1}`), since their rows take focus. `scrollbar-list` and `useScrollbarSize` are now only for a native scroller that can't be a ScrollArea.
|
|
510
547
|
- Docs: https://raft-design.botiverse.build/components/scroll-area/
|
|
511
548
|
|
|
512
549
|
### Fixed
|
|
513
550
|
|
|
514
551
|
- **FilterChipRemove**: on a touch screen its 44px tap area no longer reaches over the chip's label. It grows 10px up and down, 6px toward the label (exactly to its edge) and 14px outward, so a tap on the label's end no longer clears the filter. ([#1054](https://github.com/botiverse/raft-design/pull/1054))
|
|
515
|
-
- Why: the tap area grew 10px on every side, across the 6px gap and 4px into the label
|
|
552
|
+
- Why: the tap area grew 10px on every side, across the 6px gap and 4px into the label.
|
|
516
553
|
- Affects: touch screens. Keep the next control 16px from the chip (was 12), since the tap area now reaches 10px past the chip's end.
|
|
517
554
|
- Docs: https://raft-design.botiverse.build/components/filter-chip/
|
|
518
555
|
- **ArtifactIcon** is pinned to Lucide's `file-code` by a test, so a Lucide upgrade that changes that icon fails instead of letting the rail drift from the Artifact chip. ([#1054](https://github.com/botiverse/raft-design/pull/1054))
|
|
@@ -522,7 +559,7 @@
|
|
|
522
559
|
### Changed
|
|
523
560
|
|
|
524
561
|
- **ArtifactIcon** is Lucide's `file-code`, a page with a folded corner and `< >`: the same icon as an HTML Artifact's link chip in messages, so the rail and the chips use one icon. **FollowArtifactIcon** and **UnfollowArtifactIcon** draw that page with the plus or minus at the bottom-right, at least 1.6 units clear of the page. ([#1052](https://github.com/botiverse/raft-design/pull/1052))
|
|
525
|
-
- Why:
|
|
562
|
+
- Why: the old page-with-lines icon was plain and hard to tell apart, and after several rounds of proposals the chip's icon was chosen.
|
|
526
563
|
- Affects: every place that shows these three icons (the Artifacts rail item, Artifact menus, the mobile server popover). Names, props and the small heavier minus are unchanged.
|
|
527
564
|
- Docs: https://raft-design.botiverse.build/components/icons/
|
|
528
565
|
|
|
@@ -594,11 +631,11 @@
|
|
|
594
631
|
### Added
|
|
595
632
|
|
|
596
633
|
- **SidebarSectionEndChevron**, and `chevronAtEnd` on **SidebarSectionHeader**: the section's chevron as the last of its controls with a mouse on a desktop, as sidebar.md has described since 0.65.0. It is a second trigger of the section (it toggles it and turns with it), out of the tab order and the accessibility tree, and a press leaves focus where it was; the title stays the one toggle for the keyboard and a screen reader. `chevronAtEnd` only hides the chevron before the title with a mouse; on a touch screen it shows as before.
|
|
597
|
-
- Why: the Raft web app drew its own end chevron
|
|
634
|
+
- Why: the Raft web app drew its own end chevron.
|
|
598
635
|
- Affects: nothing until a header opts in.
|
|
599
636
|
- Docs: https://raft-design.botiverse.build/components/sidebar/
|
|
600
637
|
- **WebTopBarRoot**, **WebTopBarStart**, **WebTopBarCenter** and **WebTopBarEnd** (`@botiverse/raft-ui/wip`): the web top bar (page-structure.md, "The web top bar") as a component: 44px with a mouse and 48px on touch (`--frame-item-height` + 2 × `--header-air`), the frame surface with its seam inside that height, its start on the rail's items, its center on the window and its end on the column margin. ([#1021](https://github.com/botiverse/raft-design/pull/1021))
|
|
601
|
-
- Why: the bar was hand-built in the app-shell composition
|
|
638
|
+
- Why: the bar was hand-built in the app-shell composition.
|
|
602
639
|
- Affects: nothing until an app moves its bar onto it.
|
|
603
640
|
|
|
604
641
|
## [0.65.0](https://github.com/botiverse/raft-design/compare/v0.64.0...v0.65.0) (2026-11-05)
|
|
@@ -606,23 +643,23 @@
|
|
|
606
643
|
### Added
|
|
607
644
|
|
|
608
645
|
- **MessageItemTrailing** (`@botiverse/raft-ui/wip`): a slot that ends a message's last line, the body's last child, such as the faces of the agents that read it. It takes no column: the last line keeps room for it and the text wraps under it only when that line is full; under a code block, a table or an image it has a line of its own.
|
|
609
|
-
- Why: the read faces kept a 63-77px column beside every message
|
|
646
|
+
- Why: the read faces kept a 63-77px column beside every message.
|
|
610
647
|
- Docs: https://raft-design.botiverse.build/components/message-item/#trailing
|
|
611
648
|
|
|
612
649
|
### Changed
|
|
613
650
|
|
|
614
651
|
- **Size chain tokens**: control, row, option and header heights are now one chain of variables in `tokens.json` and `foundation.css`, built on the existing `--control-height`, `--control-hit-inset` and `--control-embedded-size`; components read them instead of fixed sizes. New: `--row-height`, `--option-height`, `--control-air`, `--field-gap`, `--header-air`, `--header-height`, `--header-height-short`, `--bar-button-size`, `--bar-button-radius`, `--frame-item-height`, `--switch-width`, `--switch-height`, `--settings-row-inset`, `--section-gap`, `--page-inset`, `--rail-width`, `--rail-item`.
|
|
615
|
-
- Why: the desktop sizing review (
|
|
652
|
+
- Why: the desktop sizing review (sizing option A) set one size per role; a component that hard-coded 32 or 56 drifted from the rest.
|
|
616
653
|
- Affects: nothing visible on its own; #973 and #974 set the desktop and touch values.
|
|
617
654
|
- Docs: https://raft-design.botiverse.build/foundations/spacing/
|
|
618
655
|
- **Desktop density** (with a mouse from 768px; touch is unchanged): one 34px control height for buttons, fields, selects, tabs, menu items and rows; 44px heads (34 + 2 × 5); 28px bar buttons with a 44px hit area; content 16px from a surface's edges (`--page-inset`, `--surface-inset`), 12px under a head (`--page-top`); settings cards 20 inside, 16 apart; sidebar rows and titles on two axes (glyphs at 16, text at 40) with a section's chevron at the end of its header; quick reactions a control high; empty states 16 from their sides. Also visible with a mouse: the rail is 49px wide with 36px items (was 57 and 40; `--rail-width-short`, `--rail-item-short`); a Switch is 38 × 22 (was 50 × 30); an IconTile takes its corner by size (27 chip, 35 control, 51 container) and an interactive badge is 20px high with the sticker corner; the hover toolbar is 42px (34px buttons) and floats half its height above the row; the Lightbox head is the header height.
|
|
619
|
-
- Why:
|
|
656
|
+
- Why: the desktop sizing review; the rules are in design/foundations/desktop-density.md.
|
|
620
657
|
- Affects: every desktop surface with a mouse. Apps that pad inside a raft-ui dialog, drawer or panel section with their own `md:` classes should drop them: the surface inset now replaces a caller's `px-*` again.
|
|
621
658
|
- Migrate: read `--surface-inset` / `--page-inset` instead of `px-5` for a surface's own padding; browser tests that measured 32px controls or 36px toolbars on a desktop now see 34 and 42.
|
|
622
659
|
- New variables: `--page-top`, `--rail-width-short`, `--rail-item-short` (tokens); `--surface-inset`, `--sidebar-glyph-axis`, `--sidebar-text-axis` (styles.css, next to `--column-margin`).
|
|
623
660
|
- Docs: https://raft-design.botiverse.build/foundations/desktop-density/
|
|
624
661
|
- **Touch density** (a phone width, and a touch screen at any width): 44px controls with a 10px radius (`--control-radius`; below 768px a mouse gets it too), denser rich rows (56px Activity, Inbox and person rows), a small ComboboxSelect keeps 8; icon tiles by size on touch.
|
|
625
|
-
- Why: the sizing audit's touch column
|
|
662
|
+
- Why: the sizing audit's touch column.
|
|
626
663
|
- Affects: phone widths and touch screens; a desktop with a mouse from 768px is unchanged.
|
|
627
664
|
- Docs: https://raft-design.botiverse.build/foundations/spacing/#hit-targets
|
|
628
665
|
|
|
@@ -637,18 +674,18 @@
|
|
|
637
674
|
### Added
|
|
638
675
|
|
|
639
676
|
- **ButtonSplit**, **ButtonSplitMain** and **ButtonSplitMenu** (from `@botiverse/raft-ui/wip`): a `primary` or `default` Button with its ▾ menu, one shape in two parts. The parts touch and share one `radius-control` outline, the menu part's start border divides them, and each part hovers, presses, focuses and disables on its own; `disabled` on the group disables both. ([#1009](https://github.com/botiverse/raft-design/pull/1009))
|
|
640
|
-
- Why: Raft Chat's Members header needs Create agent with a ▾ for Take over a local session and Connect an external agent (
|
|
677
|
+
- Why: Raft Chat's Members header needs Create agent with a ▾ for Take over a local session and Connect an external agent (rule in [#1008](https://github.com/botiverse/raft-design/pull/1008)), and raft-ui only had `PanelSplitAction`, the frameless split of a panel header.
|
|
641
678
|
- Affects: nothing until used.
|
|
642
679
|
- Docs: https://raft-design.botiverse.build/components/button/#split-buttons
|
|
643
680
|
- **AppLinkCard** and its parts (from `@botiverse/raft-ui/wip`): an app link's card drawn in the message itself, after the link's paragraph, where the app's answer lists one (protocol version 3: a `card` item with `placement: "block"` in `items`). The link is never replaced: it stays a chip when the answer lists `inline`, and the author's plain link when it doesn't. `AppLinkCardHeader` (with `AppLinkCardIcon`, `AppLinkCardName` and `AppLinkCardHost`) is Raft's row naming the app; `AppLinkCardBody`, `AppLinkCardEyebrow`, `AppLinkCardTitle` and `AppLinkCardMeta` are the item, as on the hover card, and the title holds a link that opens the item; `AppLinkCardActions` holds at most two of the app's buttons and ⋯ for the rest, where an Open link goes; `AppLinkCardPlaceholder` is the card's size while a cached answer loads. 400px wide, the message's width in the phone layout. Navigation's App links now say what each item of an answer draws, in the list's order, with nothing inferred. ([#1010](https://github.com/botiverse/raft-design/pull/1010))
|
|
644
|
-
- Why: a card whose buttons matter, such as a Stamp approval, could be acted on only by hovering its chip on desktop, and not at all on a phone. The app lists every way its link is drawn, a chip, a hover card or a block card
|
|
681
|
+
- Why: a card whose buttons matter, such as a Stamp approval, could be acted on only by hovering its chip on desktop, and not at all on a phone. The app lists every way its link is drawn, a chip, a hover card or a block card (protocol version 3), and Raft always draws the app's name and icon in its own frame.
|
|
645
682
|
- Affects: nothing until a product draws one. PreviewCard is untouched.
|
|
646
683
|
- Docs: https://raft-design.botiverse.build/components/app-link-card/ and https://raft-design.botiverse.build/patterns/navigation/#what-an-apps-answer-lists
|
|
647
684
|
|
|
648
685
|
### Fixed
|
|
649
686
|
|
|
650
687
|
- **CommentMarginItem**: a `CommentMargin` renders its cards where the browser has no `ResizeObserver` (jsdom, some embedded webviews) instead of throwing. Each card keeps the height it had when placed. ([#1007](https://github.com/botiverse/raft-design/pull/1007))
|
|
651
|
-
- Why: the same gap #996 closed in `useCommentMarginFits`; slock's Artifact viewer works around it today by not drawing the margin there
|
|
688
|
+
- Why: the same gap #996 closed in `useCommentMarginFits`; slock's Artifact viewer works around it today by not drawing the margin there.
|
|
652
689
|
- Affects: only environments without `ResizeObserver`; there a card that grows after it is placed doesn't push the cards below it.
|
|
653
690
|
- Docs: https://raft-design.botiverse.build/components/comment/
|
|
654
691
|
|
|
@@ -657,7 +694,7 @@
|
|
|
657
694
|
### Fixed
|
|
658
695
|
|
|
659
696
|
- **Fonts**: Cyrillic text no longer shows with its letters spaced apart. The sans and heading stacks try Arial and Helvetica Neue before the CJK faces. ([#994](https://github.com/botiverse/raft-design/pull/994))
|
|
660
|
-
- Why: Space Grotesk has no Cyrillic, so Russian and Ukrainian fell through to the CJK faces, whose Cyrillic glyphs are fullwidth
|
|
697
|
+
- Why: Space Grotesk has no Cyrillic, so Russian and Ukrainian fell through to the CJK faces, whose Cyrillic glyphs are fullwidth.
|
|
661
698
|
- Affects: only characters Space Grotesk lacks (Cyrillic, Greek and a few symbols such as arrows), on every page language. Latin and CJK text keeps its faces.
|
|
662
699
|
- Docs: https://raft-design.botiverse.build/foundations/typography/
|
|
663
700
|
|
|
@@ -666,7 +703,7 @@
|
|
|
666
703
|
### Added
|
|
667
704
|
|
|
668
705
|
- **InboxItemLine**, **InboxItemStatus**, **InboxItemListGroup** and **InboxItemListHeading** (from `@botiverse/raft-ui/wip`): a compact InboxItem row with no trailing column, and a list of rows grouped by day. ([#999](https://github.com/botiverse/raft-design/pull/999), rules in [#998](https://github.com/botiverse/raft-design/pull/998))
|
|
669
|
-
- Why: the trailing column held the time and task number on their own, wasting the row's width; the time now sits at the end of the first line and the preview takes the full width
|
|
706
|
+
- Why: the trailing column held the time and task number on their own, wasting the row's width; the time now sits at the end of the first line and the preview takes the full width.
|
|
670
707
|
- Affects: nothing until a row uses `InboxItemLine`. Rows built with `InboxItemTrailing` render the same DOM and classes as 0.62.0 (a parity test compares them).
|
|
671
708
|
- Docs: https://raft-design.botiverse.build/components/inbox-item/
|
|
672
709
|
- **InboxItemTimestamp**: `reveal="hover"`, for an older row in a list with `InboxItemLine`: the time is hidden at rest and shows while the row is hovered or holds keyboard focus; never on touch. In a compact row's heading the time can carry a tooltip, and a click on it opens the item.
|
|
@@ -677,7 +714,7 @@
|
|
|
677
714
|
### Changed
|
|
678
715
|
|
|
679
716
|
- **AppShellSidebarTrigger**: its built-in labels are in sentence case: "Collapse sidebar" and "Expand sidebar", instead of "Collapse Sidebar" and "Expand Sidebar". ([#995](https://github.com/botiverse/raft-design/pull/995))
|
|
680
|
-
- Why: every clickable label in the Raft Design System now uses sentence case, with capitals only for the first word and for names ([decision 0044](https://raft-design.botiverse.build/decisions/0044-sentence-case-for-labels/)
|
|
717
|
+
- Why: every clickable label in the Raft Design System now uses sentence case, with capitals only for the first word and for names ([decision 0044](https://raft-design.botiverse.build/decisions/0044-sentence-case-for-labels/)). The docs' rules and examples (buttons, menu items, tabs, links, tooltips, accessible names and the task status In progress) change with it.
|
|
681
718
|
- Affects: the trigger's `aria-label` and `title`. A trigger with your own `aria-label` is unchanged.
|
|
682
719
|
- Migrate: update tests and selectors that find the trigger by "Collapse Sidebar" or "Expand Sidebar" to the new names.
|
|
683
720
|
- Docs: https://raft-design.botiverse.build/components/app-shell/
|
|
@@ -685,7 +722,7 @@
|
|
|
685
722
|
### Fixed
|
|
686
723
|
|
|
687
724
|
- **useCommentMarginFits**: answers `false` where the browser has no `matchMedia` or `ResizeObserver` (jsdom, some embedded webviews) instead of throwing during render. ([#996](https://github.com/botiverse/raft-design/pull/996))
|
|
688
|
-
- Why: a viewer that used the hook crashed on its first render in jsdom, which failed every DOM test of slock's Artifact viewer
|
|
725
|
+
- Why: a viewer that used the hook crashed on its first render in jsdom, which failed every DOM test of slock's Artifact viewer.
|
|
689
726
|
- Affects: only environments without those APIs; there the margin never fits and the viewer keeps its `CommentMarker`s.
|
|
690
727
|
- Docs: https://raft-design.botiverse.build/components/comment/
|
|
691
728
|
|
|
@@ -694,7 +731,7 @@
|
|
|
694
731
|
### Changed
|
|
695
732
|
|
|
696
733
|
- **AvatarGroup** and **AvatarGroupButton**: faces overlap directly, with no ring of the surface color between them; the seam sits on a face's straight edge, never in its corner. The "+N" chip has the faces' ink edge. ([#988](https://github.com/botiverse/raft-design/pull/988))
|
|
697
|
-
- Why: the ring read as a gap between faces, and the app's own stacks (Working now, typing, thread followers) had none, so the same stack looked two ways
|
|
734
|
+
- Why: the ring read as a gap between faces, and the app's own stacks (Working now, typing, thread followers) had none, so the same stack looked two ways.
|
|
698
735
|
- Affects: every AvatarGroup, AvatarGroupButton and CommentMarker stack. Geometry is unchanged: 19px and 14px faces, 5px overlap (the smallest that clears the 4px corner), so fixed widths built on it still hold. Hover on AvatarGroupButton now fills only around the stack.
|
|
699
736
|
- Docs: https://raft-design.botiverse.build/components/avatar/
|
|
700
737
|
|
|
@@ -849,7 +886,7 @@
|
|
|
849
886
|
### Fixed
|
|
850
887
|
|
|
851
888
|
- **SidebarSectionControls**: a section's controls are always shown, with a mouse as on a touch screen. ([#942](https://github.com/botiverse/raft-design/pull/942))
|
|
852
|
-
- Why: shown only on hover, they hid what the header can do, and a long title's room changed as the pointer passed
|
|
889
|
+
- Why: shown only on hover, they hid what the header can do, and a long title's room changed as the pointer passed.
|
|
853
890
|
- Affects: the controls are no longer hidden at rest with a mouse; a collapsed section's attention mark sits before them in the row and hides only while the section is open.
|
|
854
891
|
- Docs: https://raft-design.botiverse.build/components/sidebar/
|
|
855
892
|
- **Edge pull of a header's first and last control** (sidebar section controls, panel and dialog heads, Lightbox actions): it holds while that control's menu is open. ([#942](https://github.com/botiverse/raft-design/pull/942))
|
|
@@ -913,7 +950,7 @@
|
|
|
913
950
|
|
|
914
951
|
### Changed
|
|
915
952
|
|
|
916
|
-
- **Sidebar section header:** its controls rest in the title's hint color and turn body color only on hover, focus or with their menu open; the controls end on the header's line, and the title starts where the rows' content starts
|
|
953
|
+
- **Sidebar section header:** its controls rest in the title's hint color and turn body color only on hover, focus or with their menu open; the controls end on the header's line, and the title starts where the rows' content starts. ([#922](https://github.com/botiverse/raft-design/pull/922))
|
|
917
954
|
|
|
918
955
|
### Fixed
|
|
919
956
|
|
|
@@ -939,7 +976,7 @@
|
|
|
939
976
|
|
|
940
977
|
### Design System
|
|
941
978
|
|
|
942
|
-
- **Expand-type actions (Expand, Focus Mode, Open in New Page) are their own header buttons, left of ⋯, never in it
|
|
979
|
+
- **Expand-type actions (Expand, Focus Mode, Open in New Page) are their own header buttons, left of ⋯, never in it**. ([#917](https://github.com/botiverse/raft-design/pull/917), [#918](https://github.com/botiverse/raft-design/pull/918))
|
|
943
980
|
- Docs: https://raft-design.botiverse.build/components/panel/
|
|
944
981
|
- **Raft Chat:** Drafts after Saved in the sidebar and on phone Home ([#904](https://github.com/botiverse/raft-design/pull/904)); Mark Thread as Unread in the thread's menus ([#853](https://github.com/botiverse/raft-design/pull/853)); English fixes and their zh sync ([#910](https://github.com/botiverse/raft-design/pull/910), [#912](https://github.com/botiverse/raft-design/pull/912)).
|
|
945
982
|
|
|
@@ -951,9 +988,9 @@
|
|
|
951
988
|
|
|
952
989
|
### Design System
|
|
953
990
|
|
|
954
|
-
- **A setting that applies at once shows its result on a reserved line 4px under the control
|
|
991
|
+
- **A setting that applies at once shows its result on a reserved line 4px under the control**. ([#906](https://github.com/botiverse/raft-design/pull/906))
|
|
955
992
|
- Docs: https://raft-design.botiverse.build/patterns/forms/
|
|
956
|
-
- **Activity thread and task rows lead with their own mark; the channel's icon goes in the corner badge
|
|
993
|
+
- **Activity thread and task rows lead with their own mark; the channel's icon goes in the corner badge**. `InboxItemLeadingBadge` holds any one icon. ([#905](https://github.com/botiverse/raft-design/pull/905))
|
|
957
994
|
- Docs: https://raft-design.botiverse.build/components/inbox-item/
|
|
958
995
|
- **Icon meanings:** one meaning per icon on a screen, one Lucide drawing per concept, More is ⋯ in a row of actions and ⋮ for one object's menu, Back is an arrow, success `circle-check` and error `triangle-alert`, Reopen is not restart, outline only. `CreateTaskIcon` is deprecated. ([#903](https://github.com/botiverse/raft-design/pull/903))
|
|
959
996
|
- Docs: https://raft-design.botiverse.build/foundations/iconography/
|
|
@@ -1032,7 +1069,7 @@
|
|
|
1032
1069
|
|
|
1033
1070
|
### Design System
|
|
1034
1071
|
|
|
1035
|
-
- **Usage shows what is used:** a Meter fills with what is used, not what is left, and its text always says it ("62% used · resets in 3h", never a bare "62%"). It turns `warning` from 80% used, the same point as 20% left before, so the same data keeps its colour; used up is a full `warning` bar with "Used up". A remaining amount can go in a tooltip ("378 GB free of 994 GB"). Decided on 2026-10-29
|
|
1072
|
+
- **Usage shows what is used:** a Meter fills with what is used, not what is left, and its text always says it ("62% used · resets in 3h", never a bare "62%"). It turns `warning` from 80% used, the same point as 20% left before, so the same data keeps its colour; used up is a full `warning` bar with "Used up". A remaining amount can go in a tooltip ("378 GB free of 994 GB"). Decided on 2026-10-29. ([#879](https://github.com/botiverse/raft-design/pull/879))
|
|
1036
1073
|
- Docs: https://raft-design.botiverse.build/components/meter/
|
|
1037
1074
|
|
|
1038
1075
|
### Added
|
|
@@ -1336,7 +1373,7 @@
|
|
|
1336
1373
|
|
|
1337
1374
|
### Design System
|
|
1338
1375
|
|
|
1339
|
-
- **Search:** matched words sit on a yellow fill, no edge, no bold
|
|
1376
|
+
- **Search:** matched words sit on a yellow fill, no edge, no bold; a leading @ or # is part of the hit. Principle 3 names the inline "you" highlight as the one full-yellow highlight without an edge. ([#796](https://github.com/botiverse/raft-design/pull/796))
|
|
1340
1377
|
- Docs: https://raft-design.botiverse.build/components/search/
|
|
1341
1378
|
|
|
1342
1379
|
## [0.34.0](https://github.com/botiverse/raft-design/compare/v0.33.0...v0.34.0) (2026-10-24)
|
|
@@ -1351,7 +1388,7 @@
|
|
|
1351
1388
|
|
|
1352
1389
|
### Design System
|
|
1353
1390
|
|
|
1354
|
-
- **Read mark:** a bare check, since the light with an ink check read as a black frame
|
|
1391
|
+
- **Read mark:** a bare check, since the light with an ink check read as a black frame. It is no longer a status light. ([#794](https://github.com/botiverse/raft-design/pull/794))
|
|
1355
1392
|
- Docs: https://raft-design.botiverse.build/components/message-item/
|
|
1356
1393
|
- **Touch screens:** a right swipe on an Activity row marks it read or unread, one button with the menu's icon and word, no toast; under Unread or Mentions the row stays until the person leaves the list or changes the filter. ([#793](https://github.com/botiverse/raft-design/pull/793))
|
|
1357
1394
|
|
|
@@ -1405,7 +1442,7 @@
|
|
|
1405
1442
|
### Design System
|
|
1406
1443
|
|
|
1407
1444
|
- **A row swipe on a phone**: a swipe starts once the finger has gone 10px on the web (twice as far across as up or down), and swipes that start within 24px of the screen edge stay with the browser. A release applies the action past 40% of the row's width, or past 20% with a quick flick. The uncovered layer is soft until a release would apply, then solid `success` with ink, and ticks once on Android as it turns solid. It says "Done"; the menu keeps "Mark as Done". The row leaves at once, with an 8-second Undo. On a phone, Activity's Mark as Done stays in the row as a button only screen readers see. ([#775](https://github.com/botiverse/raft-design/pull/775), [#776](https://github.com/botiverse/raft-design/pull/776))
|
|
1408
|
-
- Why: Activity on a phone moved Mark as Done to the swipe
|
|
1445
|
+
- Why: Activity on a phone moved Mark as Done to the swipe; the swipe must be predictable, reversible, and reachable without swiping.
|
|
1409
1446
|
- Affects: a rule for products.
|
|
1410
1447
|
- Docs: https://raft-design.botiverse.build/patterns/responsive/#touch-screens
|
|
1411
1448
|
|
|
@@ -1424,7 +1461,7 @@
|
|
|
1424
1461
|
### Design System
|
|
1425
1462
|
|
|
1426
1463
|
- **A dialog whose job is to choose keeps an open list**: when choosing is the dialog's main content (a computer to move an agent to, people to add), the choices stay open as a list at any count, not a select: a radio circle for one choice, a checkbox for several. A search field shows always when people look for something they know by name, and from 6 choices when they choose by comparing. Filtering never clears the choice; the chosen rows stay first. A row may have two description lines, and a details button at its end outside the row's label. ([#767](https://github.com/botiverse/raft-design/pull/767), [#772](https://github.com/botiverse/raft-design/pull/772))
|
|
1427
|
-
- Why: a dropdown hides what people came to compare
|
|
1464
|
+
- Why: a dropdown hides what people came to compare.
|
|
1428
1465
|
- Affects: a rule for products; raft-ui adds `ChoiceList` (wip).
|
|
1429
1466
|
- Docs: https://raft-design.botiverse.build/components/choice-list/
|
|
1430
1467
|
|
|
@@ -1438,11 +1475,11 @@
|
|
|
1438
1475
|
### Design System
|
|
1439
1476
|
|
|
1440
1477
|
- **A mention's read mark is the online light with an ink check**: once an agent has read a message that mentions it, the online light (the 8px lime square with its ink edge) shows at the mention's top right with an ink check in it. A light with no check means presence; a light with a check means read. It never covers the name: the mention keeps 10px after it. Nothing shows until the agent has read the message, the hover card still says Read or Not read yet, and on a touch screen the read light alone says it. Colored fills carry ink, never white. ([#770](https://github.com/botiverse/raft-design/pull/770))
|
|
1441
|
-
- Why: the bare gray check was too faint to notice
|
|
1478
|
+
- Why: the bare gray check was too faint to notice, and the corner light is what people knew from the app; the check keeps it apart from presence.
|
|
1442
1479
|
- Affects: a rule for products; `MessageReferenceReadMark` draws the new mark; status and color rules list the read light.
|
|
1443
1480
|
- Docs: https://raft-design.botiverse.build/components/message-item/#read-receipts-on-a-mention
|
|
1444
1481
|
- **On a phone, Activity's Mark as Done is the left swipe and the press-and-hold menu**: no button at rest on a phone; a tablet or a desktop keeps it. A swipe starts only when the finger moves clearly sideways. ([#769](https://github.com/botiverse/raft-design/pull/769))
|
|
1445
|
-
- Why: a list of rows to clear read as a column of buttons; swiping a row away is how people clear lists on a phone
|
|
1482
|
+
- Why: a list of rows to clear read as a column of buttons; swiping a row away is how people clear lists on a phone.
|
|
1446
1483
|
- Affects: a rule for products.
|
|
1447
1484
|
- Docs: https://raft-design.botiverse.build/patterns/responsive/#touch-screens
|
|
1448
1485
|
|
|
@@ -1456,12 +1493,12 @@
|
|
|
1456
1493
|
### Design System
|
|
1457
1494
|
|
|
1458
1495
|
- **Toasts: one at a time, no Dismiss, above the composer on a phone**: a toast shows alone; a new one replaces it at once and the replaced one is closed, not queued. It has no Dismiss: it closes by itself after 5 seconds, or 8 with an action or as an error, and can be swiped away or closed with Esc. It stays at the bottom center: at the bottom of the window on a desktop, and on a phone just above the composer, the tab bar or the keyboard, which the app sets with `--toast-viewport-bottom`. ([#675](https://github.com/botiverse/raft-design/pull/675))
|
|
1459
|
-
- Why: stacked toasts and a × on each one asked people to manage messages that were only receipts; at the very bottom of a phone a toast sat under the composer and the keyboard
|
|
1496
|
+
- Why: stacked toasts and a × on each one asked people to manage messages that were only receipts; at the very bottom of a phone a toast sat under the composer and the keyboard.
|
|
1460
1497
|
- Affects: a rule for products; raft-ui's Toast defaults.
|
|
1461
1498
|
- Docs: https://raft-design.botiverse.build/components/toast/#where-it-appears
|
|
1462
1499
|
|
|
1463
1500
|
- **Pull to refresh looks like Android's own on Android**: on Android web the indicator is Material 3's: a small disc with a shadow slides down over the list, which stays still; inside, a progress arc with an arrowhead grows with the pull, faint until the pull is armed and faint again when it is pulled back (disarmed); while refreshing the arc turns; at the end the disc scales away. iOS and everything else keep the Spinner. The disc is the one round surface besides switches, radios and grab handles, and its turning arc the one other looping animation. ([#765](https://github.com/botiverse/raft-design/pull/765))
|
|
1464
|
-
- Why: a pull on Android should look the way it does in every Android app
|
|
1501
|
+
- Why: a pull on Android should look the way it does in every Android app; armed and disarmed now look different.
|
|
1465
1502
|
- Affects: `PullToRefresh` (wip) on Android; shape, motion and navigation rules record the exception.
|
|
1466
1503
|
- Docs: https://raft-design.botiverse.build/components/pull-to-refresh/#on-android
|
|
1467
1504
|
|