@botiverse/raft-ui 0.7.0 → 0.7.1

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 CHANGED
@@ -1,3 +1,18 @@
1
+ ## [0.7.1](https://github.com/botiverse/raft-design/compare/v0.7.0...v0.7.1) (2026-10-19)
2
+
3
+ ### Added
4
+
5
+ - `stack-y-*` / `stack-x-*` (including negative and arbitrary values) and `rule-y` / `rule-x` / `rule-<colour>`: space and rules between siblings, written as the owl `> * + *` at zero specificity. Use them instead of Tailwind's `space-*` / `divide-*`. ([#534](https://github.com/botiverse/raft-design/pull/534))
6
+
7
+ ### Fixed
8
+
9
+ - **iOS scroll and open/close jank from structural selectors.** Tailwind v4 compiles `space-*` / `divide-*` to `:where(.x > :not(:last-child))`, which WebKit tests against every element; appending or removing a last child anywhere then restyled the previous last child's whole subtree (180–430ms under a long chat timeline). The package and its docs no longer use them, and `no-unanchored-structural.test.ts` keeps any rule from ending in an unanchored structural pseudo-class. Consumers should move their own `space-*` / `divide-*` to `stack-*` / `rule-*`. ([#534](https://github.com/botiverse/raft-design/pull/534))
10
+ - `codemods/package-name.mjs` keeps a deliberate `raft-ui` npm alias instead of rewriting it. ([#522](https://github.com/botiverse/raft-design/pull/522))
11
+
12
+ ### Changed
13
+
14
+ - The design-system pages and component guides describe what the product ships. ([#523](https://github.com/botiverse/raft-design/pull/523), [#524](https://github.com/botiverse/raft-design/pull/524), [#526](https://github.com/botiverse/raft-design/pull/526), [#528](https://github.com/botiverse/raft-design/pull/528)–[#530](https://github.com/botiverse/raft-design/pull/530), [#532](https://github.com/botiverse/raft-design/pull/532))
15
+
1
16
  ## [0.7.0](https://github.com/botiverse/raft-design/compare/v0.6.1...v0.7.0) (2026-10-18)
2
17
 
3
18
  ### Breaking Changes
@@ -83,7 +83,9 @@ export function renameDependency(json, version) {
83
83
  "optionalDependencies",
84
84
  ]) {
85
85
  const deps = json[field];
86
- if (!deps || !("raft-ui" in deps)) continue;
86
+ // `"raft-ui": "npm:@botiverse/raft-ui@…"` is an alias kept on purpose, for a dependency
87
+ // that peers on the unscoped name: leave it.
88
+ if (!deps || !("raft-ui" in deps) || deps["raft-ui"].startsWith(`npm:${NEW}@`)) continue;
87
89
  const next = {};
88
90
  for (const [name, range] of Object.entries(deps)) {
89
91
  if (name === "raft-ui")
@@ -9,9 +9,9 @@
9
9
  (unordered axis — names say where the token is used)
10
10
  intensity = (bare default) > muted > hint for foreground;
11
11
  hairline < seam < strong for line
12
- layer = places on the elevation ladder; fill = opaque neutral interaction
13
- fills (resting/hover/active/disabled surfaces) — the opaque counterpart
14
- of the translucent ink washes.
12
+ layer = places on the elevation ladder; fill = neutral interaction fills
13
+ (resting/hover/active/disabled surfaces): fill-muted and fill-strong are
14
+ opaque, the -pressed overlays are translucent ink laid over a surface.
15
15
  Families: {family}(-{slot})
16
16
  family = brand | attention | info | success | warning | danger | inactive
17
17
  slot = -strong (deep shade: readable text, rings) > bare (solid fill)
@@ -20,8 +20,8 @@
20
20
  | -hover / -active (derived interaction pair, opt-in per family;
21
21
  an opted-in family always gets both)
22
22
  Washes: ink-{alpha%} (2..40) — neutral overlays, hairlines, hover fills.
23
- Opacity rule: layer and fill tokens are opaque in every theme; -backdrop is
24
- the only translucent layer token (an overlay by contract).
23
+ Opacity rule: layer tokens are opaque in every theme, except -backdrop (an
24
+ overlay by contract); among fills only the -pressed overlays are translucent.
25
25
  Interaction fills: transparent resting → ink-N directly; solid family fill →
26
26
  the family's -hover/-active pair; neutral or tinted surface → step up its ladder.
27
27
  Escape hatches: ink ramp for one-off washes; inline oklch() literals for
package/dist/index.d.mts CHANGED
@@ -906,8 +906,8 @@ type SidebarItemRootProps = useRender.ComponentProps<"button"> & {
906
906
  type SidebarItemProps = SidebarItemRootProps & {
907
907
  /** For a row whose content is two lines (a name and a second line): the row
908
908
  * grows from its fixed height and its content aligns to the first line. The
909
- * name itself stays on one line and truncates, in every group (decision
910
- * 0030); never use this to let a long name wrap. */
909
+ * name itself stays on one line and truncates, in every group (Sidebar's
910
+ * rules); never use this to let a long name wrap. */
911
911
  wrap?: boolean;
912
912
  /** Something waits for you (a 700 name), the place is muted (an ink-2
913
913
  * name), or it is away: offline or not joined (an ink-3 name). */
@@ -3925,7 +3925,7 @@ declare const notificationCenter: import("tailwind-variants/lite").TVReturnType<
3925
3925
  title: string[];
3926
3926
  count: string[];
3927
3927
  scroller: string[];
3928
- list: string[];
3928
+ list: never[];
3929
3929
  item: string[];
3930
3930
  itemRow: string[];
3931
3931
  itemIcon: string[];
@@ -3984,7 +3984,7 @@ declare const notificationCenter: import("tailwind-variants/lite").TVReturnType<
3984
3984
  title: string[];
3985
3985
  count: string[];
3986
3986
  scroller: string[];
3987
- list: string[];
3987
+ list: never[];
3988
3988
  item: string[];
3989
3989
  itemRow: string[];
3990
3990
  itemIcon: string[];
@@ -4043,7 +4043,7 @@ declare const notificationCenter: import("tailwind-variants/lite").TVReturnType<
4043
4043
  title: string[];
4044
4044
  count: string[];
4045
4045
  scroller: string[];
4046
- list: string[];
4046
+ list: never[];
4047
4047
  item: string[];
4048
4048
  itemRow: string[];
4049
4049
  itemIcon: string[];
package/dist/index.mjs CHANGED
@@ -5984,7 +5984,7 @@ const avatar = tv({
5984
5984
  ],
5985
5985
  group: [
5986
5986
  "group/avatar-group flex items-center",
5987
- "space-x-[-5px]",
5987
+ "stack-x-[-5px]",
5988
5988
  "*:data-[slot=avatar]:shadow-[0_0_0_1.5px_var(--sticker-edge),0_0_0_3px_var(--layer-canvas)]",
5989
5989
  "*:data-[slot=avatar-group-count]:shadow-[0_0_0_1.5px_var(--sticker-edge),0_0_0_3px_var(--layer-canvas)]"
5990
5990
  ]
@@ -6643,7 +6643,7 @@ const messageItem = tv({
6643
6643
  ],
6644
6644
  searchMatch: searchHit,
6645
6645
  embedded: ["min-w-0", "mt-2"],
6646
- attachments: ["space-y-2", "mt-2"],
6646
+ attachments: ["stack-y-2", "mt-2"],
6647
6647
  footer: ["mt-1.5 flex flex-wrap items-center gap-1.5"],
6648
6648
  toolbar: [
6649
6649
  "absolute z-20 flex items-center",
@@ -7903,8 +7903,8 @@ const messageActionCardContent = tv({ slots: {
7903
7903
  property: ["contents"],
7904
7904
  propertyLabel: ["max-w-40 min-w-0 wrap-break-word", "text-footnote font-medium text-foreground-hint"],
7905
7905
  propertyValue: ["m-0 min-w-0 wrap-break-word", "min-h-8 py-1.5 text-callout text-foreground"],
7906
- scopeList: ["mt-0 space-y-1.5"],
7907
- scopeListViewport: ["overflow-y-auto overscroll-contain", "max-h-[164px] space-y-1.5 pr-1"],
7906
+ scopeList: ["mt-0 stack-y-1.5"],
7907
+ scopeListViewport: ["overflow-y-auto overscroll-contain", "max-h-[164px] stack-y-1.5 pr-1"],
7908
7908
  scopeListLabel: ["text-footnote font-medium text-foreground-hint"],
7909
7909
  scope: ["min-w-0 border p-1.5", "rounded-control border-line-hairline bg-layer-card px-2.5 py-2"],
7910
7910
  scopeName: ["break-all font-mono", "text-mono-meta font-normal text-foreground"],
@@ -8374,7 +8374,7 @@ function MessageAttachmentAction({ className, render, ...props }) {
8374
8374
  //#region src/components/message-attachment/message-image-gallery.recipe.ts
8375
8375
  const messageImageGallery = tv({
8376
8376
  slots: {
8377
- root: ["max-w-[min(360px,calc(100vw-112px))] space-y-1 md:max-w-[360px]"],
8377
+ root: ["max-w-[min(360px,calc(100vw-112px))] stack-y-1 md:max-w-[360px]"],
8378
8378
  row: ["grid", "gap-1"],
8379
8379
  item: [
8380
8380
  "group/img relative overflow-hidden text-left",
@@ -10308,17 +10308,17 @@ function TableColumn({ className, render, ...props }) {
10308
10308
  const filePreview = tv({
10309
10309
  slots: {
10310
10310
  root: ["relative flex h-10 w-10 shrink-0 items-center justify-center"],
10311
- document: ["relative h-10 w-8 space-y-1.5 rounded-md rounded-tr-[15%] p-1.5 ring-1", "bg-layer-card shadow-none ring-line-hairline"],
10311
+ document: ["relative h-10 w-8 stack-y-1.5 rounded-md rounded-tr-[15%] p-1.5 ring-1", "bg-layer-card shadow-none ring-line-hairline"],
10312
10312
  file: ["relative flex h-10 w-8 items-center justify-center rounded-md rounded-tr-[15%] ring-1", "bg-layer-card shadow-none ring-line-hairline"],
10313
10313
  code: ["relative flex h-10 w-8 items-center justify-center rounded-md rounded-tr-[15%] ring-1", "bg-layer-card shadow-none ring-line-hairline"],
10314
10314
  fileZip: ["flex h-7 items-center justify-center"],
10315
10315
  fileZipStack: ["relative -translate-y-0.5 flex flex-col items-center"],
10316
- fileZipTeeth: ["space-y-px"],
10316
+ fileZipTeeth: ["stack-y-px"],
10317
10317
  fileZipRow: ["flex overflow-hidden rounded-full"],
10318
10318
  fileZipDark: ["size-0.5", "bg-foreground/40"],
10319
10319
  fileZipLight: ["size-0.5", "bg-foreground/15"],
10320
10320
  fileZipPull: ["mt-0.5 h-1 w-2.5 rounded-sm border", "border-foreground/40 bg-foreground/10"],
10321
- codeStack: ["-translate-y-0.5 space-y-px"],
10321
+ codeStack: ["-translate-y-0.5 stack-y-px"],
10322
10322
  codeRow: ["flex items-center gap-0.5 leading-none"],
10323
10323
  codeRowIndent: ["flex items-center gap-0.5 pl-1.5 leading-none"],
10324
10324
  codeTextRow: ["block pl-3"],
@@ -11988,7 +11988,7 @@ const notificationCenter = tv({
11988
11988
  title: ["font-sans text-headline text-foreground"],
11989
11989
  count: ["ml-auto", "font-mono text-mono-meta text-foreground-hint"],
11990
11990
  scroller: ["min-h-0 flex-1 overflow-y-auto overscroll-contain"],
11991
- list: ["divide-y-0"],
11991
+ list: [],
11992
11992
  item: [
11993
11993
  "group/notification-center-item relative px-4 py-3",
11994
11994
  "before:pointer-events-none before:absolute before:top-0 before:right-0 before:left-4 before:hidden before:border-t before:border-line-hairline",
@@ -14801,7 +14801,7 @@ function createTaskDndOverStore() {
14801
14801
  //#endregion
14802
14802
  //#region src/components/task-list/task-list-section.recipe.ts
14803
14803
  const taskListSection = tv({ slots: {
14804
- root: ["space-y-2"],
14804
+ root: ["stack-y-2"],
14805
14805
  trigger: [
14806
14806
  "group flex w-full touch-manipulation items-center text-left outline-none",
14807
14807
  "disabled:cursor-default",
@@ -14825,7 +14825,7 @@ const taskListSection = tv({ slots: {
14825
14825
  "group-hover:text-foreground"
14826
14826
  ],
14827
14827
  panel: ["data-ending-style:hidden"],
14828
- items: ["space-y-2"],
14828
+ items: ["stack-y-2"],
14829
14829
  empty: [
14830
14830
  "px-3",
14831
14831
  "flex min-h-16 items-center justify-center py-3",
@@ -14838,7 +14838,7 @@ const taskListSection = tv({ slots: {
14838
14838
  function TaskListRoot({ className, render, ...props }) {
14839
14839
  const defaultProps = {
14840
14840
  "data-slot": "task-list-root",
14841
- className: cn("space-y-6", className)
14841
+ className: cn("stack-y-6", className)
14842
14842
  };
14843
14843
  return useRender({
14844
14844
  defaultTagName: "div",
@@ -15247,14 +15247,14 @@ const legacyTaskPanel = tv({
15247
15247
  "[&_svg]:size-3.5"
15248
15248
  ],
15249
15249
  title: ["flex flex-col items-start gap-0.5 overflow-visible whitespace-normal"],
15250
- body: ["space-y-4 overflow-y-auto px-5 py-4"],
15250
+ body: ["stack-y-4 overflow-y-auto px-5 py-4"],
15251
15251
  status: ["flex flex-wrap items-center gap-2"],
15252
- section: ["p-4", "space-y-3 rounded-container border border-line-hairline bg-layer-card shadow-none"],
15252
+ section: ["p-4", "stack-y-3 rounded-container border border-line-hairline bg-layer-card shadow-none"],
15253
15253
  field: [],
15254
15254
  fieldLabel: ["mb-0.5 font-sans text-footnote font-medium text-foreground-hint"],
15255
15255
  fieldTitle: ["m-0 wrap-break-word", "text-body font-semibold text-foreground"],
15256
15256
  fieldText: ["m-0 whitespace-pre-wrap wrap-break-word", "text-subhead text-foreground-muted"],
15257
- meta: ["p-4 text-sm", "space-y-0 rounded-container border border-line-hairline bg-layer-card px-4 py-2 shadow-none"],
15257
+ meta: ["p-4 text-sm", "rounded-container border border-line-hairline bg-layer-card px-4 py-2 shadow-none"],
15258
15258
  metaRow: ["flex justify-between gap-3", "min-h-8 items-center"],
15259
15259
  metaLabel: ["font-sans text-footnote font-medium text-foreground-hint"],
15260
15260
  metaValue: ["text-right", "text-callout text-foreground"]
@@ -16254,7 +16254,7 @@ const searchThread = tv({ slots: {
16254
16254
  ],
16255
16255
  threadTimestamp: ["normal-case", "ml-auto"],
16256
16256
  threadTitle: ["font-sans text-callout font-semibold text-foreground"],
16257
- threadMessages: ["flex flex-col [&>*:first-child]:border-t-0", "border-t-0 bg-transparent"]
16257
+ threadMessages: ["flex flex-col", "border-t-0 bg-transparent"]
16258
16258
  } });
16259
16259
  //#endregion
16260
16260
  //#region src/components/search/search-thread.tsx
package/dist/styles.css CHANGED
@@ -389,3 +389,56 @@ body {
389
389
  @utility press-overlay-sticker {
390
390
  background-image: linear-gradient(var(--sticker-pressed), var(--sticker-pressed));
391
391
  }
392
+
393
+ /* Space and rules between siblings: stack-y-* / stack-x-* and rule-y / rule-x
394
+ / rule-<colour>. Use these, never Tailwind's space-* and divide-*.
395
+
396
+ Tailwind v4 compiles space-* and divide-* to `:where(.x > :not(:last-child))`.
397
+ Its rightmost part has no class, so WebKit (every iOS browser) tests it
398
+ against every element and marks every parent as depending on which child is
399
+ last. Appending or removing a last child anywhere then restyles the previous
400
+ last child's whole subtree: 180-430ms when a chat's bottom controls mount
401
+ after its timeline. Chromium does not pay this, so it only shows on iOS.
402
+ The owl `> * + *` depends only on the previous sibling, so appending at the
403
+ end restyles nothing. Zero specificity, like Tailwind's, so a child's own
404
+ margin or border class still wins. */
405
+ @utility stack-y-* {
406
+ :where(& > * + *) {
407
+ margin-block-start: calc(var(--spacing) * --value(number));
408
+ margin-block-start: --value([length]);
409
+ }
410
+ }
411
+ @utility stack-y-px {
412
+ :where(& > * + *) {
413
+ margin-block-start: 1px;
414
+ }
415
+ }
416
+ @utility stack-x-* {
417
+ :where(& > * + *) {
418
+ margin-inline-start: calc(var(--spacing) * --value(number));
419
+ margin-inline-start: --value([length]);
420
+ }
421
+ }
422
+ @utility -stack-x-* {
423
+ :where(& > * + *) {
424
+ margin-inline-start: calc(var(--spacing) * --value(number) * -1);
425
+ margin-inline-start: calc(--value([length]) * -1);
426
+ }
427
+ }
428
+ @utility rule-y {
429
+ :where(& > * + *) {
430
+ border-block-start-width: 1px;
431
+ border-block-start-style: solid;
432
+ }
433
+ }
434
+ @utility rule-x {
435
+ :where(& > * + *) {
436
+ border-inline-start-width: 1px;
437
+ border-inline-start-style: solid;
438
+ }
439
+ }
440
+ @utility rule-* {
441
+ :where(& > * + *) {
442
+ border-color: --value(--color-*, [color]);
443
+ }
444
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "React components and CSS foundations for the Raft Design System.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -11,7 +11,7 @@ The design guidance (which component to use, how components combine into screens
11
11
 
12
12
  - **Index:** https://raft-design.botiverse.build/llms.txt lists every page with a one-line summary.
13
13
  - **Components:** https://raft-design.botiverse.build/components.json lists every component with its status, other names and doc URLs. Each component page, `https://raft-design.botiverse.build/components/<component>.md`, has when to use it, its parts, rules and an API generated from the code.
14
- - **Choosing a component:** https://raft-design.botiverse.build/patterns/choosing-components.md.
14
+ - **Choosing a component:** https://raft-design.botiverse.build/components/overview.md#how-to-choose-a-component.
15
15
 
16
16
  This skill keeps what is specific to the code: the [adoption](./rules/adoption.md) order, [looking up](./rules/lookup.md) props and tokens, and the [technical overview](./rules/technical-overview.md) (package contents, stylesheets, providers). Do not register or install skills in a consumer project unless requested.
17
17
 
@@ -68,8 +68,8 @@ The guideline rules this skill used to carry are pages of the design system now.
68
68
 
69
69
  | For | Read |
70
70
  | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
71
- | Which component, which feedback, which overlay | https://raft-design.botiverse.build/patterns/choosing-components.md |
72
- | Combining parts, things built from existing parts | https://raft-design.botiverse.build/patterns/composites.md |
71
+ | Which component, which feedback, which overlay | https://raft-design.botiverse.build/components/overview.md#how-to-choose-a-component |
72
+ | Combining parts, things built from existing parts | https://raft-design.botiverse.build/components/overview.md#built-from-existing-parts |
73
73
  | Forms and settings | https://raft-design.botiverse.build/patterns/forms.md |
74
74
  | Feedback, loading, empty and error states | https://raft-design.botiverse.build/patterns/feedback.md |
75
75
  | Shell, panels, headers, navigation | https://raft-design.botiverse.build/patterns/page-structure.md, https://raft-design.botiverse.build/patterns/navigation.md |
@@ -7,8 +7,8 @@ description: "Moved to the Raft Design System docs: combining compound parts, ov
7
7
 
8
8
  This guidance is part of the Raft Design System docs now, which are its single source. Each page is Markdown; read only the ones the task needs.
9
9
 
10
- - Composites: what is built from existing parts: https://raft-design.botiverse.build/patterns/composites.md
11
- - Choosing a component: https://raft-design.botiverse.build/patterns/choosing-components.md
10
+ - Built from existing parts: https://raft-design.botiverse.build/components/overview.md#built-from-existing-parts
11
+ - Choosing a component: https://raft-design.botiverse.build/components/overview.md#how-to-choose-a-component
12
12
  - The component's own page, for its parts: https://raft-design.botiverse.build/components/<component>.md
13
13
 
14
14
  Check every prop, part and token you use against [lookup.md](./lookup.md).
@@ -8,6 +8,6 @@ description: "Moved to the Raft Design System docs: banners, toasts, loading, em
8
8
  This guidance is part of the Raft Design System docs now, which are its single source. Each page is Markdown; read only the ones the task needs.
9
9
 
10
10
  - Feedback: https://raft-design.botiverse.build/patterns/feedback.md
11
- - Choosing a component: telling people something, and waiting: https://raft-design.botiverse.build/patterns/choosing-components.md#telling-people-something
11
+ - Choosing a component: telling people something, and waiting: https://raft-design.botiverse.build/components/overview.md#telling-people-something
12
12
 
13
13
  Check every prop, part and token you use against [lookup.md](./lookup.md).
@@ -8,6 +8,6 @@ description: "Moved to the Raft Design System docs: choosing and composing form
8
8
  This guidance is part of the Raft Design System docs now, which are its single source. Each page is Markdown; read only the ones the task needs.
9
9
 
10
10
  - Forms and settings: https://raft-design.botiverse.build/patterns/forms.md
11
- - Choosing a component: picking something: https://raft-design.botiverse.build/patterns/choosing-components.md#picking-something
11
+ - Choosing a component: picking something: https://raft-design.botiverse.build/components/overview.md#picking-something
12
12
 
13
13
  Check every prop, part and token you use against [lookup.md](./lookup.md).
@@ -7,7 +7,7 @@ description: "Moved to the Raft Design System docs: dialogs, drawers, popovers,
7
7
 
8
8
  This guidance is part of the Raft Design System docs now, which are its single source. Each page is Markdown; read only the ones the task needs.
9
9
 
10
- - Choosing a component: overlays: https://raft-design.botiverse.build/patterns/choosing-components.md#overlays
10
+ - Choosing a component: overlays: https://raft-design.botiverse.build/components/overview.md#overlays
11
11
  - Elevation: which things float, and how popups are positioned: https://raft-design.botiverse.build/foundations/elevation.md
12
12
 
13
13
  Check every prop, part and token you use against [lookup.md](./lookup.md).
@@ -77,4 +77,4 @@ Custom fonts use `--heading-font`, `--sans-font`, and `--mono-font`.
77
77
  - Base UI overlays portal to `document.body`. An app root with `isolation: isolate` keeps its stacking contexts below those surfaces. Embedded previews can use `portalProps.container` (toasts: `ToastProvider portalContainer`) to keep overlays inside their scope.
78
78
  - SSR apps with persisted mode need to restore it before first paint to avoid a theme flash.
79
79
 
80
- What floats and how popups are positioned is in [Elevation](https://raft-design.botiverse.build/foundations/elevation.md); which overlay to use is in [Choosing a component](https://raft-design.botiverse.build/patterns/choosing-components.md#overlays).
80
+ What floats and how popups are positioned is in [Elevation](https://raft-design.botiverse.build/foundations/elevation.md); which overlay to use is in [Choosing a component](https://raft-design.botiverse.build/components/overview.md#overlays).