@botiverse/raft-ui 0.84.0 → 0.85.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cn.d.mts +2 -0
  3. package/dist/cn.mjs +21 -1
  4. package/dist/components/alert-dialog/alert-dialog.mjs +2 -2
  5. package/dist/components/app-shell/app-shell.mjs +2 -2
  6. package/dist/components/avatar/avatar.mjs +1 -1
  7. package/dist/components/button/button-split.mjs +2 -2
  8. package/dist/components/button/button.mjs +2 -2
  9. package/dist/components/choice-list/choice-list.recipe.mjs +1 -1
  10. package/dist/components/code-block/code-block.recipe.mjs +2 -2
  11. package/dist/components/combobox/combobox.mjs +1 -1
  12. package/dist/components/combobox-select/combobox-select.css +1 -1
  13. package/dist/components/comment/comment.recipe.mjs +2 -2
  14. package/dist/components/composer/composer-attachment.recipe.mjs +5 -5
  15. package/dist/components/composer/composer-drop-overlay.mjs +2 -2
  16. package/dist/components/composer/composer-drop-overlay.recipe.mjs +1 -1
  17. package/dist/components/composer/sortable-composer-attachments.mjs +1 -1
  18. package/dist/components/context-menu/context-menu.mjs +1 -1
  19. package/dist/components/conversation-panel/conversation-panel-empty-state.mjs +1 -1
  20. package/dist/components/dialog/dialog.mjs +4 -4
  21. package/dist/components/drag/drag-lift.d.mts +3 -2
  22. package/dist/components/drag/drag-lift.mjs +3 -2
  23. package/dist/components/drag/drag.recipe.mjs +1 -1
  24. package/dist/components/drawer/drawer.recipe.mjs +4 -4
  25. package/dist/components/dropdown-menu/dropdown-menu.mjs +1 -1
  26. package/dist/components/file-preview/file-preview.recipe.mjs +1 -1
  27. package/dist/components/inbox-item/inbox-item.recipe.mjs +7 -7
  28. package/dist/components/lightbox/lightbox.d.mts +2 -2
  29. package/dist/components/lightbox/lightbox.mjs +7 -7
  30. package/dist/components/message-attachment/message-image-gallery.recipe.mjs +1 -1
  31. package/dist/components/message-item/message-item.recipe.mjs +2 -2
  32. package/dist/components/message-list/message-list.recipe.mjs +2 -2
  33. package/dist/components/mobile-nav/mobile-nav.mjs +2 -2
  34. package/dist/components/notification-center/notification-center.mjs +1 -1
  35. package/dist/components/panel/panel-header.recipe.mjs +1 -1
  36. package/dist/components/panel/panel-split-action.recipe.mjs +2 -2
  37. package/dist/components/popover/popover.mjs +1 -1
  38. package/dist/components/preview-card/preview-card.mjs +1 -1
  39. package/dist/components/pull-to-refresh/pull-to-refresh-indicator.mjs +1 -1
  40. package/dist/components/resizable/resizable.recipe.mjs +1 -1
  41. package/dist/components/scroll-area/scroll-area.css +1 -0
  42. package/dist/components/select/select.mjs +2 -2
  43. package/dist/components/selection-toolbar/selection-toolbar.recipe.mjs +1 -1
  44. package/dist/components/sidebar/sidebar.recipe.mjs +1 -1
  45. package/dist/components/tabs/tabs.mjs +4 -4
  46. package/dist/components/task-dnd/task-dnd.recipe.mjs +2 -2
  47. package/dist/components/thread-panel/thread-panel.recipe.mjs +1 -1
  48. package/dist/components/toast/toast.mjs +5 -5
  49. package/dist/components/tooltip/tooltip.mjs +2 -2
  50. package/dist/lib/list-cell.mjs +1 -1
  51. package/dist/lint/no-raw-z-index.mjs +142 -0
  52. package/dist/lint/prefer-scroll-area.mjs +1 -1
  53. package/dist/lint/recommended.json +2 -1
  54. package/dist/lint.d.mts +8 -2
  55. package/dist/lint.mjs +16 -3
  56. package/dist/styles.css +28 -0
  57. package/package.json +1 -1
  58. package/skills/raft-ui-guide/SKILL.md +1 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## [0.85.0](https://github.com/botiverse/raft-design/compare/v0.84.0...v0.85.0) (2026-11-10)
2
+
3
+ ### Added
4
+
5
+ - **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.
6
+ - 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 (#proj-raft-ui:#218, step 1, approved by Artea).
7
+ - 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 (#218 step 2). 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` in step 2.
8
+ - Docs: https://raft-design.botiverse.build/foundations/elevation/#layer-order, https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
9
+
10
+ ### Changed
11
+
12
+ - **raft-ui's in-page layers take the scale's values**: where a component's z-index moved to a name with another number, it renders the same inside the component, but its number against an app's own layers changes. Overlays keep theirs (50, 60, 70, 80, 999).
13
+ - Raised parts 10 → 1 (`z-raised`): Button's and Toast's label and loading indicator, Avatar's badge, InboxItem's lead, badge, selection, text, trail and corner (its trigger is `z-base`, 0), ChoiceList's control column, Comment's cover and active comment, CodeBlock's actions, ComposerAttachment's veils, FilePreview's corner badge, MessageImageGallery's "+N" veil, MessageItem's trailing meta, MobileNav's icons, Select's scroll arrows, Tabs' tab content and drop placeholder, Tooltip's kbd, Lightbox's arrows, info dock and counter, ConversationPanelEmptyState's illustration. 20 → 1: CodeBlock's corners, ComposerAttachment's progress bar (over the veil by DOM order), ComposerDropOverlay's second icon (and its first, 10 → 1), Lightbox's close, a dragged TaskDraggable. MessageList's end fade 5 → 1.
14
+ - 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`).
15
+ - 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`).
16
+ - 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.
17
+ - 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) (#proj-raft-ui:#218).
18
+ - 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.
19
+ - Docs: https://raft-design.botiverse.build/foundations/elevation/#in-page-layers
20
+
1
21
  ## [0.84.0](https://github.com/botiverse/raft-design/compare/v0.83.0...v0.84.0) (2026-11-10)
2
22
 
3
23
  ### Added
package/dist/cn.d.mts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { ClassValue } from "cnfast";
2
2
  //#region src/lib/cn.d.ts
3
+ /** The `--z-index-*` names of styles.css, in order (design/foundations/elevation.md#layer-order). */
4
+ export declare const Z_INDEX_NAMES: string[];
3
5
  export declare function cn(...inputs: ClassValue[]): string;
4
6
  //#endregion
5
7
  export type { ClassValue };
package/dist/cn.mjs CHANGED
@@ -1,6 +1,25 @@
1
1
  import { clsx } from "cnfast";
2
2
  import { extendTailwindMerge } from "tailwind-merge";
3
3
  //#region src/lib/cn.ts
4
+ /** The `--z-index-*` names of styles.css, in order (design/foundations/elevation.md#layer-order). */
5
+ const Z_INDEX_NAMES = [
6
+ "below",
7
+ "base",
8
+ "raised",
9
+ "control",
10
+ "sticky",
11
+ "float",
12
+ "chrome",
13
+ "cover",
14
+ "app",
15
+ "scrollbar",
16
+ "popup",
17
+ "dialog",
18
+ "dialog-layer",
19
+ "lightbox",
20
+ "toast",
21
+ "drag"
22
+ ];
4
23
  const twMerge = extendTailwindMerge({ extend: {
5
24
  theme: { spacing: [
6
25
  "control",
@@ -59,6 +78,7 @@ const twMerge = extendTailwindMerge({ extend: {
59
78
  "x"
60
79
  ] }],
61
80
  opacity: [{ opacity: ["dimmed"] }],
81
+ z: [{ z: Z_INDEX_NAMES }],
62
82
  "bg-image": [{ overlay: [
63
83
  "subtle",
64
84
  "medium",
@@ -71,4 +91,4 @@ function cn(...inputs) {
71
91
  return twMerge(clsx(inputs));
72
92
  }
73
93
  //#endregion
74
- export { cn };
94
+ export { Z_INDEX_NAMES, cn };
@@ -13,9 +13,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
13
13
  //#region src/components/alert-dialog/alert-dialog.tsx
14
14
  const alertDialog = tv({
15
15
  slots: {
16
- overlay: ["fixed inset-0 z-50 bg-layer-backdrop"],
16
+ overlay: ["fixed inset-0 z-dialog bg-layer-backdrop"],
17
17
  content: [
18
- "fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] -translate-1/2 outline-none",
18
+ "fixed top-1/2 left-1/2 z-dialog grid w-[calc(100%-2rem)] -translate-1/2 outline-none",
19
19
  decisionWidth,
20
20
  "overflow-clip rounded-dialog bg-layer-popover text-foreground",
21
21
  "shadow-float ring-1 ring-line-seam"
@@ -19,7 +19,7 @@ const appShell = tv({
19
19
  sidebarSlot: ["hidden min-h-0 shrink-0 flex-col md:flex"],
20
20
  mainSlot: ["flex min-h-0 min-w-0 flex-1 flex-col"],
21
21
  panelSlot: ["hidden min-h-0 min-w-0 flex-col md:flex"],
22
- overlaySlot: ["absolute inset-0 z-20 flex min-h-0 min-w-0"]
22
+ overlaySlot: ["absolute inset-0 z-cover flex min-h-0 min-w-0"]
23
23
  },
24
24
  variants: { overlay: {
25
25
  false: {},
@@ -135,7 +135,7 @@ function AppShellSidebarSlot({ className, id: idProp, render, ...props }) {
135
135
  id,
136
136
  inert: collapsed ? true : void 0,
137
137
  className: sidebarSlot({ className: cn(className, sidebar?.collapsible && [
138
- "group-data-[state=expanded]/app-shell:relative group-data-[state=expanded]/app-shell:z-1",
138
+ "group-data-[state=expanded]/app-shell:relative group-data-[state=expanded]/app-shell:z-raised",
139
139
  "group-data-[state=collapsed]/app-shell:pointer-events-none",
140
140
  "group-data-[state=collapsed]/app-shell:opacity-0"
141
141
  ]) })
@@ -37,7 +37,7 @@ const avatar = tv({
37
37
  "[[data-size='3xs']>&]:[&>svg]:size-2"
38
38
  ],
39
39
  badge: [
40
- "absolute z-10 select-none",
40
+ "absolute z-raised select-none",
41
41
  "size-[11px] -right-1 -bottom-1 rounded-[2px]",
42
42
  "outline-2 outline-solid outline-layer-canvas",
43
43
  "group-data-[size=xl]/avatar:size-5 group-data-[size=xl]/avatar:-right-2 group-data-[size=xl]/avatar:-bottom-2",
@@ -10,11 +10,11 @@ const buttonSplit = tv({ base: [
10
10
  "data-sticker-edge:ring-[1.5px]",
11
11
  "data-[size=lg]:rounded-(--control-radius)"
12
12
  ] });
13
- const buttonSplitMain = tv({ base: ["rounded-e-none border-e-0", "ring-0 focus-visible:z-1"] });
13
+ const buttonSplitMain = tv({ base: ["rounded-e-none border-e-0", "ring-0 focus-visible:z-raised"] });
14
14
  const buttonSplitMenu = tv({
15
15
  base: [
16
16
  "min-w-0 rounded-s-none px-0",
17
- "ring-0 focus-visible:z-1",
17
+ "ring-0 focus-visible:z-raised",
18
18
  "after:inset-s-0"
19
19
  ],
20
20
  variants: {
@@ -38,8 +38,8 @@ const buttonVariants = tv({
38
38
  "disabled:border-line-hairline disabled:bg-fill-muted disabled:bg-none disabled:text-foreground-disabled disabled:ring-0",
39
39
  "aria-disabled:border-line-hairline aria-disabled:bg-fill-muted aria-disabled:bg-none aria-disabled:text-foreground-disabled aria-disabled:ring-0"
40
40
  ],
41
- content: "relative z-10 inline-flex min-w-0 max-w-full items-center justify-center gap-[inherit] data-[loading=true]:invisible",
42
- loadingIndicator: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center"
41
+ content: "relative z-raised inline-flex min-w-0 max-w-full items-center justify-center gap-[inherit] data-[loading=true]:invisible",
42
+ loadingIndicator: "pointer-events-none absolute inset-0 z-raised flex items-center justify-center"
43
43
  },
44
44
  variants: {
45
45
  variant: {
@@ -24,7 +24,7 @@ const choiceList = tv({
24
24
  description: ["block truncate font-sans text-footnote font-normal text-foreground-muted", "in-data-disabled:text-foreground-disabled"],
25
25
  meta: ["block truncate font-sans text-footnote font-normal text-foreground-muted", "in-data-disabled:text-foreground-disabled"],
26
26
  action: [
27
- "relative z-10 col-start-2 flex h-lh items-center self-start pr-1",
27
+ "relative z-raised col-start-2 flex h-lh items-center self-start pr-1",
28
28
  "mt-option-inset pointer-coarse:mt-[calc((--spacing(11)-1lh)/2)]",
29
29
  "font-sans text-subhead"
30
30
  ],
@@ -16,11 +16,11 @@ const codeBlock = tv({ slots: {
16
16
  actions: [
17
17
  "ml-auto flex items-center gap-1",
18
18
  "[[data-slot=code-block-body]>&]:col-start-2 [[data-slot=code-block-body]>&]:row-start-1",
19
- "[[data-slot=code-block-body]>&]:z-10 [[data-slot=code-block-body]>&]:ml-3",
19
+ "[[data-slot=code-block-body]>&]:z-raised [[data-slot=code-block-body]>&]:ml-3",
20
20
  "[[data-slot=code-block-body]>&]:my-[calc(var(--control-embedded-inset)-0.5rem-1px)] [[data-slot=code-block-body]>&]:mr-[calc(var(--control-embedded-inset)-0.75rem-1px)]"
21
21
  ],
22
22
  corner: [
23
- "pointer-events-none absolute z-20 size-1",
23
+ "pointer-events-none absolute z-raised size-1",
24
24
  "data-[y=top]:top-0 data-[y=bottom]:bottom-0",
25
25
  "data-[x=left]:left-0 data-[x=right]:right-0",
26
26
  "hidden"
@@ -60,7 +60,7 @@ const combobox = tv({
60
60
  "first:rounded-t-container has-focus-visible:keyboard:inset-ring-2 has-focus-visible:keyboard:inset-ring-foreground"
61
61
  ],
62
62
  input: ["w-full outline-none"],
63
- positioner: ["isolate z-50 outline-none"],
63
+ positioner: ["isolate z-popup outline-none"],
64
64
  content: [
65
65
  "h-auto max-h-(--available-height) min-w-[220px] overflow-hidden",
66
66
  "max-w-[min(var(--available-width),max(var(--anchor-width),24rem))]",
@@ -161,7 +161,7 @@
161
161
  box-shadow: inset 0 0 0 2px var(--foreground);
162
162
  }
163
163
  .r-combobox-select__positioner {
164
- z-index: 50;
164
+ z-index: var(--z-index-popup);
165
165
  isolation: isolate;
166
166
  outline: 0;
167
167
  }
@@ -7,7 +7,7 @@ const comment = tv({
7
7
  "absolute inset-x-0 top-0",
8
8
  "data-animate:transition-transform data-animate:duration-150 data-animate:ease-out",
9
9
  "motion-reduce:transition-none",
10
- "data-active:z-10"
10
+ "data-active:z-raised"
11
11
  ],
12
12
  card: [
13
13
  "relative flex min-w-0 flex-col gap-2",
@@ -16,7 +16,7 @@ const comment = tv({
16
16
  "data-active:border-[1.5px] data-active:border-line-strong data-active:p-[11.5px] data-active:shadow-float"
17
17
  ],
18
18
  cardTrigger: [
19
- "absolute -inset-px z-10 cursor-pointer",
19
+ "absolute -inset-px z-raised cursor-pointer",
20
20
  "rounded-container border-0 bg-transparent p-0",
21
21
  "hover:bg-fill-subtle active:bg-fill-medium",
22
22
  "outline-none focus-visible:shadow-focus"
@@ -20,7 +20,7 @@ const composerAttachment = tv({
20
20
  "pe-1.5"
21
21
  ],
22
22
  attachmentsNav: [
23
- "absolute top-1/2 z-30 -translate-y-1/2",
23
+ "absolute top-1/2 z-control -translate-y-1/2",
24
24
  "size-8 rounded-chip shadow-none",
25
25
  "[&_svg:not([class*='size-'])]:size-4.5"
26
26
  ],
@@ -42,26 +42,26 @@ const composerAttachment = tv({
42
42
  attachmentTitle: ["block truncate", "font-sans text-footnote font-semibold text-foreground"],
43
43
  attachmentMeta: ["block w-full truncate", "text-left font-mono text-mono-meta text-foreground-hint"],
44
44
  attachmentUploadingOverlay: [
45
- "absolute inset-0 z-10 flex flex-col items-center justify-center px-1",
45
+ "absolute inset-0 z-raised flex flex-col items-center justify-center px-1",
46
46
  "text-center",
47
47
  "rounded-control bg-layer-veil",
48
48
  "font-mono text-mono-meta font-bold text-foreground"
49
49
  ],
50
50
  attachmentUploadProgressBar: [
51
- "absolute z-20 h-1 overflow-hidden",
51
+ "absolute z-raised h-1 overflow-hidden",
52
52
  "right-1.5 bottom-1.5 left-1.5 w-auto rounded-[2px]",
53
53
  "bg-layer-card"
54
54
  ],
55
55
  attachmentUploadProgressBarIndicator: ["block h-full", "rounded-[inherit] bg-info"],
56
56
  attachmentFailedOverlay: [
57
- "absolute inset-0 z-10 flex flex-col items-center justify-center px-1",
57
+ "absolute inset-0 z-raised flex flex-col items-center justify-center px-1",
58
58
  "[border-width:var(--ca-overlay-border,2px)]",
59
59
  "text-center",
60
60
  "rounded-control border-danger-strong bg-layer-veil",
61
61
  "font-sans text-caption font-semibold text-danger-strong"
62
62
  ],
63
63
  attachmentRemove: [
64
- "absolute z-20 flex items-center justify-center",
64
+ "absolute z-control flex items-center justify-center",
65
65
  "transition-opacity group-hover/composer-attachment:opacity-100",
66
66
  "[@media(hover:none)]:opacity-100",
67
67
  "[&_svg]:shrink-0",
@@ -42,11 +42,11 @@ function ComposerDropOverlayPrompt({ children, ...props }) {
42
42
  children: /* @__PURE__ */ jsx(FileText, { className: "text-foreground-muted" })
43
43
  }),
44
44
  /* @__PURE__ */ jsx("span", {
45
- className: dropOverlayIcon({ className: "absolute z-10" }),
45
+ className: dropOverlayIcon({ className: "absolute z-raised" }),
46
46
  children: /* @__PURE__ */ jsx(Upload, { className: "text-foreground-muted" })
47
47
  }),
48
48
  /* @__PURE__ */ jsx("span", {
49
- className: dropOverlayIcon({ className: "absolute z-20 translate-x-7 translate-y-1 rotate-12" }),
49
+ className: dropOverlayIcon({ className: "absolute z-raised translate-x-7 translate-y-1 rotate-12" }),
50
50
  children: /* @__PURE__ */ jsx(Image, { className: "text-foreground-muted" })
51
51
  })
52
52
  ]
@@ -2,7 +2,7 @@ import { tv } from "../../lib/tv.mjs";
2
2
  //#region src/components/composer/composer-drop-overlay.recipe.ts
3
3
  const composerDropOverlay = tv({ slots: {
4
4
  dropOverlay: [
5
- "absolute inset-0 z-30 flex items-center justify-center",
5
+ "absolute inset-0 z-float flex items-center justify-center",
6
6
  "rounded-container border-[1.5px] border-dashed border-line-seam",
7
7
  "bg-layer-card bg-[linear-gradient(var(--info-soft),var(--info-soft))]"
8
8
  ],
@@ -225,7 +225,7 @@ function SortableComposerAttachment({ id, label, children }) {
225
225
  const style = {
226
226
  transform: isDragging ? void 0 : CSS.Translate.toString(transform),
227
227
  transition: isDragging ? void 0 : transition,
228
- zIndex: isDragging ? 0 : void 0
228
+ zIndex: isDragging ? "var(--z-index-base)" : void 0
229
229
  };
230
230
  const place = isDragging && !home;
231
231
  return /* @__PURE__ */ jsxs("div", {
@@ -7,7 +7,7 @@ import { ContextMenu } from "@base-ui/react";
7
7
  //#region src/components/context-menu/context-menu.tsx
8
8
  const contextMenu = tv({ slots: {
9
9
  trigger: ["select-none"],
10
- positioner: ["isolate z-50 outline-none"],
10
+ positioner: ["isolate z-popup outline-none"],
11
11
  content: [
12
12
  "flex flex-col max-h-(--available-height) overflow-hidden",
13
13
  "outline-none",
@@ -12,7 +12,7 @@ const conversationPanelEmptyState = tv({ slots: {
12
12
  brand: ["shrink-0", "mb-1 inline-flex rounded-none p-0 text-foreground-hint"],
13
13
  brandMark: [
14
14
  "relative flex items-center justify-center",
15
- "[&>svg]:relative [&>svg]:z-10",
15
+ "[&>svg]:relative [&>svg]:z-raised",
16
16
  "size-9 overflow-visible rounded-none",
17
17
  "[&>svg]:size-9 [&>svg]:stroke-2"
18
18
  ]
@@ -22,9 +22,9 @@ import { createPortal } from "react-dom";
22
22
  //#region src/components/dialog/dialog.tsx
23
23
  const dialog = tv({
24
24
  slots: {
25
- overlay: ["fixed inset-0 z-50 bg-layer-backdrop"],
25
+ overlay: ["fixed inset-0 z-dialog bg-layer-backdrop"],
26
26
  content: [
27
- "fixed top-1/2 left-1/2 z-50 flex w-[calc(100%-2rem)] -translate-1/2 flex-col",
27
+ "fixed top-1/2 left-1/2 z-dialog flex w-[calc(100%-2rem)] -translate-1/2 flex-col",
28
28
  "max-h-[calc(100dvh-2rem)] outline-none",
29
29
  "overflow-clip rounded-dialog bg-layer-popover text-foreground",
30
30
  "shadow-float ring-1 ring-line-seam"
@@ -83,8 +83,8 @@ const dialog = tv({
83
83
  content: []
84
84
  },
85
85
  1: {
86
- overlay: ["z-60"],
87
- content: ["z-60"]
86
+ overlay: ["z-dialog-layer"],
87
+ content: ["z-dialog-layer"]
88
88
  }
89
89
  },
90
90
  nested: {
@@ -49,8 +49,9 @@ export declare function keepGrabInside(proxyWidth: number, inset?: number): Modi
49
49
  */
50
50
  export declare function cancelDrag(win?: Window): void;
51
51
  /**
52
- * Above every raft-ui layer (popovers 50, a stacked dialog 60, toasts and the
53
- * lightbox 70) and an app's own top layers, among the layers rendered in
52
+ * `--z-index-drag` (`z-drag`) as a number: above every raft-ui layer (popups
53
+ * and dialogs 50, a `layer={1}` dialog 60, the lightbox 70, toasts 80) and an
54
+ * app's own top layers, among the layers rendered in
54
55
  * `document.body`, where the copy goes by default. dnd-kit's own default.
55
56
  */
56
57
  export declare const DRAG_LIFT_Z_INDEX = 999;
@@ -109,8 +109,9 @@ function cancelDrag(win = window) {
109
109
  win.dispatchEvent(new Event("resize"));
110
110
  }
111
111
  /**
112
- * Above every raft-ui layer (popovers 50, a stacked dialog 60, toasts and the
113
- * lightbox 70) and an app's own top layers, among the layers rendered in
112
+ * `--z-index-drag` (`z-drag`) as a number: above every raft-ui layer (popups
113
+ * and dialogs 50, a `layer={1}` dialog 60, the lightbox 70, toasts 80) and an
114
+ * app's own top layers, among the layers rendered in
114
115
  * `document.body`, where the copy goes by default. dnd-kit's own default.
115
116
  */
116
117
  const DRAG_LIFT_Z_INDEX = 999;
@@ -18,7 +18,7 @@ const drag = tv({
18
18
  "border-[1.5px] border-dashed border-line-strong bg-fill-subtle"
19
19
  ],
20
20
  hint: [
21
- "pointer-events-none fixed top-0 left-0 z-999 max-w-80",
21
+ "pointer-events-none fixed top-0 left-0 z-drag max-w-80",
22
22
  "rounded-chip px-2 py-1",
23
23
  "font-sans text-caption",
24
24
  "bg-layer-hud text-foreground-on-hud shadow-float ring-1 ring-line-seam"
@@ -66,13 +66,13 @@ const drawer = tv({
66
66
  extend: drawerMotion,
67
67
  slots: {
68
68
  overlay: [
69
- "fixed inset-0 z-50 min-h-dvh select-none",
69
+ "fixed inset-0 z-dialog min-h-dvh select-none",
70
70
  "supports-[-webkit-touch-callout:none]:absolute",
71
71
  "bg-layer-backdrop"
72
72
  ],
73
- viewport: ["pointer-events-none fixed inset-0 z-50 select-none"],
73
+ viewport: ["pointer-events-none fixed inset-0 z-dialog select-none"],
74
74
  popup: [
75
- "group/drawer-popup pointer-events-auto fixed z-50 flex h-(--drawer-content-height) flex-col outline-none",
75
+ "group/drawer-popup pointer-events-auto fixed z-dialog flex h-(--drawer-content-height) flex-col outline-none",
76
76
  "[--drawer-inset:12px] [--drawer-content-height:var(--drawer-height,auto)]",
77
77
  "data-nested-drawer-open:overflow-hidden",
78
78
  "bg-layer-popover text-foreground",
@@ -80,7 +80,7 @@ const drawer = tv({
80
80
  ],
81
81
  content: ["flex min-h-0 flex-1 flex-col rounded-[inherit] select-text", "group-data-swiping/drawer-popup:select-none"],
82
82
  swipeHandle: [
83
- "relative z-10 shrink-0 cursor-grab select-none active:cursor-grabbing",
83
+ "relative z-control shrink-0 cursor-grab select-none active:cursor-grabbing",
84
84
  "before:absolute before:content-['']",
85
85
  "before:rounded-full before:bg-line-seam"
86
86
  ],
@@ -9,7 +9,7 @@ import { Menu } from "@base-ui/react/menu";
9
9
  //#region src/components/dropdown-menu/dropdown-menu.tsx
10
10
  const dropdownMenu = tv({ slots: {
11
11
  trigger: ["select-none"],
12
- positioner: ["isolate z-50 outline-none"],
12
+ positioner: ["isolate z-popup outline-none"],
13
13
  content: [
14
14
  "max-h-(--available-height) overflow-hidden",
15
15
  "outline-none",
@@ -30,7 +30,7 @@ const filePreview = tv({
30
30
  media: ["relative h-10 w-8 overflow-hidden rounded-md ring-1", "bg-fill-muted shadow-none ring-line-hairline"]
31
31
  },
32
32
  variants: { placement: {
33
- corner: { badge: "absolute -right-1.5 bottom-0.5 z-10" },
33
+ corner: { badge: "absolute -right-1.5 bottom-0.5 z-raised" },
34
34
  inline: { badge: "inline-flex h-3.5 w-7 shrink-0 items-center justify-center px-0 py-0" }
35
35
  } },
36
36
  defaultVariants: { placement: "corner" }
@@ -18,17 +18,17 @@ const inboxItem = tv({
18
18
  "has-[[data-slot=inbox-item-open]:focus-visible]:keyboard:before:inset-ring-2 has-[[data-slot=inbox-item-open]:focus-visible]:keyboard:before:inset-ring-foreground",
19
19
  "not-data-[density=compact]:has-data-[slot=inbox-item-trailing]:[--inbox-item-content-display:grid] not-data-[density=compact]:has-data-[slot=inbox-item-trailing]:[--inbox-item-content-end:-1]"
20
20
  ],
21
- itemTrigger: ["absolute inset-0 z-0 outline-none"],
22
- itemLeading: ["pointer-events-none relative z-10 flex size-9 shrink-0 items-center justify-center"],
21
+ itemTrigger: ["absolute inset-0 z-base outline-none"],
22
+ itemLeading: ["pointer-events-none relative z-raised flex size-9 shrink-0 items-center justify-center"],
23
23
  itemLeadingBadge: [
24
- "absolute -right-0.5 -bottom-0.5 z-10 inline-flex size-3.5 items-center justify-center",
24
+ "absolute -right-0.5 -bottom-0.5 z-raised inline-flex size-3.5 items-center justify-center",
25
25
  "rounded-[3px] border border-sticker-ink outline-2 outline-layer-canvas outline-solid",
26
26
  "bg-layer-canvas bg-[linear-gradient(var(--info-soft),var(--info-soft))] text-foreground",
27
27
  "[&_svg]:size-2.5 [&_svg]:shrink-0"
28
28
  ],
29
- itemSelection: ["relative z-10 flex size-5 shrink-0 items-center justify-center"],
29
+ itemSelection: ["relative z-raised flex size-5 shrink-0 items-center justify-center"],
30
30
  itemContent: [
31
- "pointer-events-none relative z-10 min-w-0 flex-1 text-left",
31
+ "pointer-events-none relative z-raised min-w-0 flex-1 text-left",
32
32
  "-col-start-3 col-end-(--inbox-item-content-end) row-start-1",
33
33
  "[display:var(--inbox-item-content-display,block)] grid-cols-subgrid"
34
34
  ],
@@ -57,7 +57,7 @@ const inboxItem = tv({
57
57
  "[&>svg]:text-foreground-hint"
58
58
  ],
59
59
  itemTrailing: [
60
- "pointer-events-none relative z-10 col-[-2/-1] row-start-1",
60
+ "pointer-events-none relative z-raised col-[-2/-1] row-start-1",
61
61
  "flex shrink-0",
62
62
  "mt-0 flex-col items-end gap-2 pt-px",
63
63
  "max-md:flex-col max-md:items-end max-md:gap-2",
@@ -138,7 +138,7 @@ const inboxItem = tv({
138
138
  } },
139
139
  reveal: { hover: {
140
140
  itemActions: [
141
- "absolute top-0 right-0 z-10",
141
+ "absolute top-0 right-0 z-raised",
142
142
  "pointer-events-none opacity-0 transition-opacity",
143
143
  "group-hover/inbox-item:pointer-events-auto group-hover/inbox-item:opacity-100",
144
144
  "group-focus-within/inbox-item:pointer-events-auto group-focus-within/inbox-item:opacity-100",
@@ -39,10 +39,10 @@ export type LightboxTriggerProps = Dialog.Trigger.Props;
39
39
  export declare function LightboxTrigger(props: LightboxTriggerProps): import("react").JSX.Element;
40
40
  export type LightboxPortalProps = StyledProps<Dialog.Portal.Props>;
41
41
  /**
42
- * The lightbox's own stacking context, at `z-70` above the page's dialogs
42
+ * The lightbox's own stacking context, at `z-lightbox` (70) above the page's dialogs
43
43
  * and popups: the scrim, the content, and every Base UI popup opened from
44
44
  * inside, which Base UI portals into this element by default, in DOM order.
45
- * A popup's own `z-50` then counts inside the lightbox, so it is drawn above
45
+ * A popup's own `z-popup` then counts inside the lightbox, so it is drawn above
46
46
  * the content instead of under the scrim, and a press on it is a press inside
47
47
  * the lightbox.
48
48
  */
@@ -15,11 +15,11 @@ import { useHotkey } from "@tanstack/react-hotkeys";
15
15
  //#region src/components/lightbox/lightbox.tsx
16
16
  const lightbox = tv({
17
17
  slots: {
18
- portal: ["relative isolate z-70"],
18
+ portal: ["relative isolate z-lightbox"],
19
19
  backdrop: ["fixed inset-0 bg-layer-backdrop data-closed:hidden"],
20
20
  content: [
21
21
  "pointer-events-none fixed inset-0 flex flex-col data-closed:hidden",
22
- "*:data-lightbox-close:pointer-events-auto *:data-lightbox-close:absolute *:data-lightbox-close:z-20",
22
+ "*:data-lightbox-close:pointer-events-auto *:data-lightbox-close:absolute *:data-lightbox-close:z-raised",
23
23
  "*:data-lightbox-close:top-[calc(16px+env(safe-area-inset-top,0px))] *:data-lightbox-close:right-[calc(16px+env(safe-area-inset-right,0px))]"
24
24
  ],
25
25
  header: [
@@ -61,7 +61,7 @@ const lightbox = tv({
61
61
  stage: ["pointer-events-none relative flex min-h-0 flex-1 items-center justify-center overflow-hidden"],
62
62
  media: ["pointer-events-auto max-h-[80vh] max-w-[86vw] border-0 bg-transparent shadow-none"],
63
63
  navigationButton: [
64
- "pointer-events-auto absolute z-10 inline-flex items-center justify-center",
64
+ "pointer-events-auto absolute z-raised inline-flex items-center justify-center",
65
65
  "transition-[background-color,color,opacity,box-shadow,transform] duration-150",
66
66
  "outline-[1px] outline-offset-2 outline-transparent",
67
67
  "data-[disabled=true]:cursor-default",
@@ -71,7 +71,7 @@ const lightbox = tv({
71
71
  "focus-visible:outline-transparent focus-visible:shadow-focus",
72
72
  "[&_svg]:size-5.5"
73
73
  ],
74
- infoDock: ["pointer-events-none absolute right-6 bottom-20 left-6 z-10 flex justify-center"],
74
+ infoDock: ["pointer-events-none absolute right-6 bottom-20 left-6 z-raised flex justify-center"],
75
75
  infoBar: [
76
76
  "pointer-events-auto flex items-center",
77
77
  "w-[clamp(144px,20vw,224px)] max-w-[calc(100vw-48px)] min-w-36",
@@ -87,7 +87,7 @@ const lightbox = tv({
87
87
  "*:data-[slot=button]:after:inset-x-[-3px] *:data-[slot=button]:first:after:left-[-9.5px] *:data-[slot=button]:last:after:right-[-9.5px]",
88
88
  "pointer-coarse:*:data-[slot=button]:after:inset-y-[-9.5px]"
89
89
  ],
90
- thumbnailStrip: ["pointer-events-none absolute right-0 bottom-4 left-0 z-10", "flex items-center justify-center gap-1.5"],
90
+ thumbnailStrip: ["pointer-events-none absolute right-0 bottom-4 left-0 z-raised", "flex items-center justify-center gap-1.5"],
91
91
  thumbnail: [
92
92
  "group/lightbox-thumb pointer-events-auto relative size-12 overflow-hidden",
93
93
  "transition-[border-color,opacity,box-shadow] duration-150",
@@ -155,10 +155,10 @@ function LightboxTrigger(props) {
155
155
  });
156
156
  }
157
157
  /**
158
- * The lightbox's own stacking context, at `z-70` above the page's dialogs
158
+ * The lightbox's own stacking context, at `z-lightbox` (70) above the page's dialogs
159
159
  * and popups: the scrim, the content, and every Base UI popup opened from
160
160
  * inside, which Base UI portals into this element by default, in DOM order.
161
- * A popup's own `z-50` then counts inside the lightbox, so it is drawn above
161
+ * A popup's own `z-popup` then counts inside the lightbox, so it is drawn above
162
162
  * the content instead of under the scrim, and a press on it is a press inside
163
163
  * the lightbox.
164
164
  */
@@ -13,7 +13,7 @@ const messageImageGallery = tv({
13
13
  "outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground"
14
14
  ],
15
15
  action: [
16
- "absolute z-10 hidden items-center justify-center",
16
+ "absolute z-raised hidden items-center justify-center",
17
17
  "focus:flex",
18
18
  "group-hover/img:flex",
19
19
  "right-[calc(var(--control-embedded-inset)-1px)] bottom-[calc(var(--control-embedded-inset)-1px)] size-8 rounded-chip border-[1.5px] border-line-strong",
@@ -81,7 +81,7 @@ const messageItem = tv({
81
81
  "dark:shadow-[0_0_0_3px_var(--mi-row-fill,transparent),0_0_0_3px_var(--layer-canvas)]"
82
82
  ],
83
83
  edited: ["ms-1 whitespace-nowrap", "text-caption text-foreground-hint"],
84
- trailing: ["absolute end-0 bottom-0 z-10 flex h-lh items-center", "select-none! **:select-none!"],
84
+ trailing: ["absolute end-0 bottom-0 z-raised flex h-lh items-center", "select-none! **:select-none!"],
85
85
  body: [
86
86
  "wrap-break-word select-text",
87
87
  "**:select-text",
@@ -93,7 +93,7 @@ const messageItem = tv({
93
93
  attachments: ["stack-y-2", "mt-2"],
94
94
  footer: ["mt-1.5 flex flex-wrap items-center gap-1.5"],
95
95
  toolbar: [
96
- "absolute z-20 flex items-center",
96
+ "absolute z-control flex items-center",
97
97
  "opacity-(--mi-toolbar-opacity,0)",
98
98
  "[pointer-events:var(--mi-toolbar-pe,none)]",
99
99
  "top-[-18px] md:pointer-fine:top-[calc(-1*(var(--control-height)+8px)/2)] right-3 gap-1 overflow-visible rounded-container p-1",
@@ -14,7 +14,7 @@ const messageList = tv({
14
14
  dateDividerRule: ["flex-1", "min-w-3 border-t border-line-hairline"],
15
15
  dateDividerChip: ["inline-flex items-center", "font-mono text-mono-meta whitespace-nowrap text-foreground-hint"],
16
16
  endFade: [
17
- "pointer-events-none absolute inset-x-0 bottom-0 z-5",
17
+ "pointer-events-none absolute inset-x-0 bottom-0 z-raised",
18
18
  "h-timeline-section",
19
19
  "bg-linear-to-t from-layer-canvas to-transparent"
20
20
  ],
@@ -35,7 +35,7 @@ const messageList = tv({
35
35
  });
36
36
  const messageListJump = tv({
37
37
  slots: {
38
- controls: ["absolute inset-x-0 z-10 mx-auto", "flex w-fit max-w-[calc(100%-1.5rem)] flex-wrap items-center justify-center gap-x-2 gap-y-3"],
38
+ controls: ["absolute inset-x-0 z-float mx-auto", "flex w-fit max-w-[calc(100%-1.5rem)] flex-wrap items-center justify-center gap-x-2 gap-y-3"],
39
39
  group: ["inline-flex shrink-0 items-stretch", "*:data-[slot=message-list-jump-pill]:rounded-e-none"],
40
40
  dismiss: [
41
41
  "rounded-s-none border-s-0",
@@ -17,8 +17,8 @@ const mobileNav = tv({ slots: {
17
17
  "[&_svg]:shrink-0",
18
18
  "isolate min-w-0 flex-col justify-start gap-[7px] border-0 px-0 pt-1 pb-0",
19
19
  "font-sans text-[10px]/3 font-semibold text-foreground-hint",
20
- "*:relative *:z-10 [&_svg]:size-[22px] [&_svg]:stroke-2",
21
- "before:pointer-events-none before:absolute before:top-0 before:left-1/2 before:z-0 before:-translate-x-1/2",
20
+ "*:relative *:z-raised [&_svg]:size-[22px] [&_svg]:stroke-2",
21
+ "before:pointer-events-none before:absolute before:top-0 before:left-1/2 before:z-base before:-translate-x-1/2",
22
22
  "before:h-[30px] before:w-14 before:rounded-control before:border-[1.5px] before:border-transparent before:content-['']",
23
23
  "active:before:bg-fill-medium",
24
24
  "data-[selected=true]:font-bold data-[selected=true]:text-foreground",
@@ -162,7 +162,7 @@ function NotificationCenterPopup({ viewport = "desktop", size = "md", className,
162
162
  side,
163
163
  sideOffset,
164
164
  sticky,
165
- className: "isolate z-50 outline-none",
165
+ className: "isolate z-popup outline-none",
166
166
  children: /* @__PURE__ */ jsx(Popover.Popup, {
167
167
  "aria-label": props["aria-label"] ?? "Notification center",
168
168
  "data-slot": "notification-center-popup",
@@ -61,7 +61,7 @@ const panelHeader = tv({
61
61
  "flex h-(--shell-header-height,var(--header-height)) shrink-0 items-center gap-3 max-md:gap-2",
62
62
  panelHeadEdges,
63
63
  "border-b border-line-seam bg-layer-canvas text-foreground",
64
- "max-md:sticky max-md:top-0 max-md:z-30"
64
+ "max-md:sticky max-md:top-0 max-md:z-chrome"
65
65
  ]
66
66
  }, {
67
67
  slots: ["sectionHeader"],
@@ -21,7 +21,7 @@ const panelSplitAction = tv({ base: [
21
21
  const splitActionMainParts = [
22
22
  "pointer-events-auto rounded-e-none",
23
23
  "after:end-0",
24
- "focus-visible:z-1 aria-pressed:z-1 data-pressed:z-1",
24
+ "focus-visible:z-raised aria-pressed:z-raised data-pressed:z-raised",
25
25
  "bg-(--split-action-main-tint,transparent)",
26
26
  "group-hover/split-action:bg-fill-subtle",
27
27
  "hover:bg-fill-medium active:bg-fill-medium"
@@ -40,7 +40,7 @@ const panelSplitActionMenu = tv({
40
40
  "pointer-events-auto min-w-0 rounded-s-none px-0",
41
41
  "w-6 me-5 md:pointer-fine:w-5 md:pointer-fine:me-0",
42
42
  "after:start-0 after:inset-e-[-21.5px] md:pointer-fine:after:-end-[calc(var(--spacing-control-hit)+1.5px)]",
43
- "focus-visible:z-1",
43
+ "focus-visible:z-raised",
44
44
  "[[data-split-action-main][data-pressed]~&]:[--split-action-menu-tint:var(--fill-subtle)]",
45
45
  "[[data-split-action-main][aria-pressed=true]~&]:[--split-action-menu-tint:var(--fill-subtle)]",
46
46
  "bg-(--split-action-menu-tint,transparent)",
@@ -9,7 +9,7 @@ import { Popover } from "@base-ui/react/popover";
9
9
  const popover = tv({
10
10
  slots: {
11
11
  trigger: ["select-none"],
12
- positioner: ["isolate z-50 outline-none"],
12
+ positioner: ["isolate z-popup outline-none"],
13
13
  content: [
14
14
  "max-h-(--available-height) min-w-[220px] overflow-hidden",
15
15
  "outline-none",
@@ -10,7 +10,7 @@ import { PreviewCard } from "@base-ui/react/preview-card";
10
10
  const previewCard = tv({
11
11
  slots: {
12
12
  trigger: [],
13
- positioner: ["isolate z-50 outline-none"],
13
+ positioner: ["isolate z-popup outline-none"],
14
14
  popup: [
15
15
  "max-h-(--available-height) min-w-[220px] overflow-hidden",
16
16
  "outline-none",
@@ -45,7 +45,7 @@ const pullToRefreshIndicator = tv({
45
45
  surface: ["size-full"]
46
46
  },
47
47
  android: {
48
- root: ["sticky top-0 z-10 h-0 overflow-visible"],
48
+ root: ["sticky top-0 z-sticky h-0 overflow-visible"],
49
49
  surface: [
50
50
  "absolute inset-x-0 top-0 mx-auto size-10 rounded-full bg-layer-popover shadow-float",
51
51
  "translate-y-[calc(var(--pull-distance)-100%)]",
@@ -4,7 +4,7 @@ const resizable = tv({ slots: {
4
4
  group: ["flex size-full min-h-0 min-w-0", "data-[orientation=vertical]:flex-col"],
5
5
  panel: ["min-h-0 min-w-0"],
6
6
  handle: [
7
- "right-0 z-10 cursor-ew-resize touch-none select-none",
7
+ "right-0 z-control cursor-ew-resize touch-none select-none",
8
8
  "relative -mr-3 flex w-3 shrink-0 items-center justify-center pointer-coarse:-mr-6 pointer-coarse:w-6",
9
9
  "before:pointer-events-none before:absolute before:transition-colors before:content-['']",
10
10
  "focus-visible:outline-none focus-visible:before:outline-2 focus-visible:before:outline-offset-2 focus-visible:before:outline-foreground",
@@ -469,6 +469,7 @@
469
469
  .r-scroll-area > .r-scroll-area__gutters {
470
470
  pointer-events: none;
471
471
  user-select: none;
472
+ /* raft-ui: z-index the gutters paint over every layer inside the area (its root is isolate) until slock's layers inside its ScrollAreas are named (#218 step 2); then var(--z-index-scrollbar). */
472
473
  z-index: 2147483647;
473
474
  }
474
475
  /*
@@ -39,7 +39,7 @@ const select = tv({
39
39
  "[&>svg]:size-4 [&>svg]:stroke-2 [&>svg_*]:stroke-2"
40
40
  ],
41
41
  value: ["flex min-w-0 flex-1 truncate text-left"],
42
- positioner: ["isolate z-50 outline-none"],
42
+ positioner: ["isolate z-popup outline-none"],
43
43
  content: [
44
44
  "relative max-h-(--available-height) min-w-[max(var(--anchor-width),220px)] overflow-hidden",
45
45
  "max-w-[min(var(--available-width),max(var(--anchor-width),24rem))]",
@@ -92,7 +92,7 @@ const select = tv({
92
92
  groupLabel: ["py-2", "px-2.5 pt-2 pb-1 text-caption font-bold text-foreground-hint"],
93
93
  separator: ["h-px shrink-0 border-0", "-mx-1 my-1 bg-line-hairline"],
94
94
  scrollArrow: [
95
- "absolute inset-x-0 z-10 flex h-6 w-full cursor-default items-center justify-center",
95
+ "absolute inset-x-0 z-raised flex h-6 w-full cursor-default items-center justify-center",
96
96
  "outline-none transition-colors",
97
97
  "data-[direction=up]:top-0 data-[direction=down]:bottom-0",
98
98
  "[&_svg]:pointer-events-none [&_svg]:shrink-0",
@@ -3,7 +3,7 @@ import { tv } from "../../lib/tv.mjs";
3
3
  const selectionToolbar = tv({
4
4
  slots: {
5
5
  dock: [
6
- "pointer-events-none absolute inset-x-0 bottom-0 z-10 flex justify-center",
6
+ "pointer-events-none absolute inset-x-0 bottom-0 z-float flex justify-center",
7
7
  "px-5 pb-[calc(--spacing(3)+env(safe-area-inset-bottom))]",
8
8
  "*:pointer-events-auto"
9
9
  ],
@@ -16,7 +16,7 @@ const sidebarRecipe = tv({ slots: {
16
16
  headEdgePull,
17
17
  "[@media(max-height:600px)]:h-(--header-height-short)"
18
18
  ],
19
- sidebarHeaderMask: ["pointer-events-none absolute inset-x-0 top-0 -z-10 hidden"],
19
+ sidebarHeaderMask: ["pointer-events-none absolute inset-x-0 top-0 z-below hidden"],
20
20
  sidebarHeaderMaskLayer: ["absolute inset-x-0 top-0"],
21
21
  sidebarHeaderTitle: ["truncate font-bold", "font-sans text-headline text-foreground max-md:text-foreground-on-frame"],
22
22
  sidebarGroupLabel: ["px-5 pt-4 pb-0 md:pointer-fine:px-(--sidebar-glyph-axis) md:pointer-fine:pt-2", "font-sans text-footnote font-bold normal-case tracking-normal text-foreground-hint"],
@@ -22,7 +22,7 @@ const tabs = tv({
22
22
  ],
23
23
  background: ["", "hidden"],
24
24
  tab: [
25
- "relative z-10 flex shrink-0 cursor-pointer touch-manipulation select-none [-webkit-touch-callout:none] items-center gap-1.5 whitespace-nowrap outline-none",
25
+ "relative isolate flex shrink-0 cursor-pointer touch-manipulation select-none [-webkit-touch-callout:none] items-center gap-1.5 whitespace-nowrap outline-none",
26
26
  "data-[reorderable=true]:cursor-pointer data-[dragging-source=true]:cursor-grabbing",
27
27
  "font-sans text-callout font-semibold text-foreground-muted",
28
28
  "transition-[background-color,box-shadow,color] duration-100 ease-out",
@@ -31,7 +31,7 @@ const tabs = tv({
31
31
  "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:stroke-2 [&_svg:not([class*='size-'])]:size-4",
32
32
  "data-disabled:pointer-events-none data-disabled:text-foreground-disabled"
33
33
  ],
34
- content: ["relative z-10 inline-flex items-center gap-[inherit]"],
34
+ content: ["relative z-raised inline-flex items-center gap-[inherit]"],
35
35
  label: [""],
36
36
  panel: ["outline-none"],
37
37
  indicator: ["", "hidden"]
@@ -416,7 +416,7 @@ function SortableTabsTab({ value, ...props }) {
416
416
  ...baseStyle,
417
417
  transform: translate === void 0 ? void 0 : vertical ? `translate3d(0, ${translate}px, 0)` : `translate3d(${translate}px, 0, 0)`,
418
418
  transition,
419
- zIndex: isDragging ? 20 : baseStyle?.zIndex,
419
+ zIndex: isDragging ? "var(--z-index-raised)" : baseStyle?.zIndex,
420
420
  cursor: isDragging ? "grabbing" : "pointer",
421
421
  ...isDragging && !home ? PLACE_STYLE : null
422
422
  }), [
@@ -483,7 +483,7 @@ function TabsTabButton({ value, sortableRef, dropPlace = false, className, child
483
483
  "data-slot": "tabs-tab-content",
484
484
  className: content({ className: dropPlace ? "invisible" : void 0 }),
485
485
  children
486
- }), dropPlace ? /* @__PURE__ */ jsx(DropPlaceholder, { className: "absolute inset-0 z-10 rounded-control" }) : null]
486
+ }), dropPlace ? /* @__PURE__ */ jsx(DropPlaceholder, { className: "absolute inset-0 z-raised rounded-control" }) : null]
487
487
  });
488
488
  }
489
489
  function TabsLabel({ className, ...props }) {
@@ -2,9 +2,9 @@ import { tv } from "../../lib/tv.mjs";
2
2
  //#region src/components/task-dnd/task-dnd.recipe.ts
3
3
  const taskDnd = tv({ slots: {
4
4
  draggable: [
5
- "group/task-draggable relative cursor-grab touch-manipulation select-none [-webkit-touch-callout:none] active:cursor-grabbing",
5
+ "group/task-draggable relative isolate cursor-grab touch-manipulation select-none [-webkit-touch-callout:none] active:cursor-grabbing",
6
6
  "outline-none",
7
- "data-[dragging=true]:z-20 data-[dragging=true]:will-change-transform",
7
+ "data-[dragging=true]:z-raised data-[dragging=true]:will-change-transform",
8
8
  "data-[disabled=true]:cursor-default data-[disabled=true]:touch-auto",
9
9
  "rounded-container",
10
10
  "focus-visible:shadow-focus"
@@ -17,7 +17,7 @@ const threadPanel = tv({ slots: {
17
17
  titleMeta: ["font-normal text-foreground-hint"],
18
18
  actions: panelHeadActions,
19
19
  search: [
20
- "relative z-20 flex shrink-0 items-center gap-1.5 py-2",
20
+ "relative z-chrome flex shrink-0 items-center gap-1.5 py-2",
21
21
  "[&>[data-slot=input-group]]:min-w-0 *:data-[slot=input-group]:flex-1",
22
22
  "border-b border-line-hairline bg-layer-canvas px-(--surface-inset)"
23
23
  ],
@@ -10,8 +10,8 @@ import { Toast } from "@base-ui/react/toast";
10
10
  //#region src/components/toast/toast.tsx
11
11
  const toastStyles = tv({
12
12
  slots: {
13
- viewport: ["pointer-events-none isolate z-80 w-[min(24rem,calc(100vw-2rem))]", "outline-none"],
14
- positioner: ["isolate z-[calc(80-var(--toast-index))] outline-none"],
13
+ viewport: ["pointer-events-none isolate z-toast w-[min(24rem,calc(100vw-2rem))]", "outline-none"],
14
+ positioner: ["isolate z-[calc(var(--z-index-toast)-var(--toast-index))] outline-none"],
15
15
  root: [
16
16
  "group/toast pointer-events-auto",
17
17
  "select-text",
@@ -101,7 +101,7 @@ const toastStyles = tv({
101
101
  layout: {
102
102
  stacked: { root: [
103
103
  "absolute inset-x-0 bottom-0 mx-auto h-(--toast-stack-height) origin-bottom",
104
- "z-[calc(70-var(--toast-index))]",
104
+ "z-[calc(var(--z-index-toast)-var(--toast-index))]",
105
105
  "before:pointer-events-auto before:absolute before:inset-x-0 before:top-full before:h-[calc(var(--toast-gap)+3px)] before:content-['']",
106
106
  "[--toast-peek-dir:-1]",
107
107
  "transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-peek)*var(--toast-peek-dir))))_scale(var(--toast-scale))]",
@@ -185,8 +185,8 @@ const toastAction = tv({ slots: {
185
185
  "data-[loading=true]:cursor-wait data-[loading=true]:opacity-100",
186
186
  "[&_svg]:pointer-events-none [&_svg]:shrink-0 **:data-icon:size-[1em] [&_svg:not([class*='size-'])]:size-3"
187
187
  ],
188
- content: "relative z-10 inline-flex items-center justify-center gap-[inherit] data-[loading=true]:invisible",
189
- loadingIndicator: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center"
188
+ content: "relative z-raised inline-flex items-center justify-center gap-[inherit] data-[loading=true]:invisible",
189
+ loadingIndicator: "pointer-events-none absolute inset-0 z-raised flex items-center justify-center"
190
190
  } });
191
191
  const createToastManager = Toast.createToastManager;
192
192
  const useToastManager = Toast.useToastManager;
@@ -9,7 +9,7 @@ import { Tooltip } from "@base-ui/react/tooltip";
9
9
  const tooltip = tv({
10
10
  slots: {
11
11
  trigger: ["select-none"],
12
- positioner: ["isolate z-50 pointer-events-none outline-none"],
12
+ positioner: ["isolate z-popup pointer-events-none outline-none"],
13
13
  content: [
14
14
  "pointer-events-none relative inline-block max-w-[min(20rem,var(--available-width,20rem))] min-w-0 px-2.5 py-1",
15
15
  "origin-(--transform-origin) outline-none",
@@ -25,7 +25,7 @@ const tooltip = tv({
25
25
  ],
26
26
  body: [
27
27
  "block min-w-0 text-footnote whitespace-normal wrap-break-word",
28
- "**:data-[slot=kbd]:relative **:data-[slot=kbd]:z-10 **:data-[slot=kbd]:ml-1.5",
28
+ "**:data-[slot=kbd]:relative **:data-[slot=kbd]:z-raised **:data-[slot=kbd]:ml-1.5",
29
29
  "**:data-[slot=kbd-group]:ml-1.5 [&_[data-slot=kbd-group]_[data-slot=kbd]]:ml-0",
30
30
  "**:data-[slot=kbd]:h-4 **:data-[slot=kbd]:min-w-4 **:data-[slot=kbd]:px-1 **:data-[slot=kbd]:py-0",
31
31
  "[&_[data-slot=kbd][data-glyph=icon]]:px-0",
@@ -5,7 +5,7 @@ const listCell = [
5
5
  "md:pointer-fine:[--cell-pad-x:calc(var(--page-inset)-var(--cell-inset-x))]",
6
6
  "[--cell-inset-end:max(0px,calc(var(--cell-inset-x)-var(--scrollbar-size,0px)))]",
7
7
  "pr-[calc(var(--cell-inset-end)+var(--cell-pad-x))] pl-[calc(var(--cell-inset-x)+var(--cell-pad-x))]",
8
- "before:pointer-events-none before:absolute before:-z-1 before:block before:content-['']",
8
+ "before:pointer-events-none before:absolute before:z-below before:block before:content-['']",
9
9
  "before:inset-y-0 before:right-(--cell-inset-end) before:left-(--cell-inset-x)",
10
10
  "before:rounded-(--cell-radius) before:border-0",
11
11
  "before:transition-colors before:duration-150 before:ease-out"
@@ -0,0 +1,142 @@
1
+ import { classContext, constDeclarator, propertyKey, stringValue } from "./prefer-scroll-area.mjs";
2
+ //#region src/lint/no-raw-z-index.ts
3
+ /**
4
+ * raft-ui/no-raw-z-index: every z-index is a named layer of the scale
5
+ * (design/foundations/elevation.md#layer-order), so a number of the app's own,
6
+ * which ranks its layer above or below layers it doesn't know about, is
7
+ * reported where an app writes it.
8
+ *
9
+ * Reported:
10
+ * - a Tailwind z-index class with a number or an arbitrary value (`z-10`,
11
+ * `-z-1`, `z-[60]`, `z-(--my-z)`, `[z-index:5]`, with any variant prefix or
12
+ * `!`) in a string or template literal that is a class, found as
13
+ * `raft-ui/prefer-scroll-area` finds one (a `className`, `class` or
14
+ * `*ClassName` attribute or property, an argument of `cn`, `tv` and the
15
+ * like, or a `const` of the same file used as a class). `z-auto` and the
16
+ * named layers (`z-raised`, `max-md:z-chrome`) are not reported;
17
+ * - a `zIndex` property whose value is a number or a numeric string
18
+ * (`style={{ zIndex: 10 }}`, `const s = { zIndex: open ? 20 : "5" }`), a
19
+ * `zIndex={n}` JSX attribute, and an assignment `el.style.zIndex = "n"`.
20
+ * A `var(--z-index-…)` string is a name, and is not reported.
21
+ *
22
+ * Allowed with a `raft-ui: z-index <reason>` comment, line or block, on the
23
+ * line above the reported string or property (or at the end of its line), as
24
+ * for `raft-ui/prefer-scroll-area`. A comment without a reason doesn't count.
25
+ */
26
+ const RAW_Z_CLASS = /(?<![\w-])!?-?z-(?:\d+|\[[^\]\s]*\]|\([^)\s]*\))!?(?![\w-])/;
27
+ const RAW_Z_ARBITRARY = /\[z-index:[^\]]*\]/;
28
+ const NUMERIC = /^\s*[-+]?\d+(?:\.\d+)?\s*$/;
29
+ const ESCAPE = /\braft-ui:\s*z-index\s+\S/;
30
+ /** Whether a class string has a z-index class with a number or an arbitrary value. */
31
+ function hasRawZIndexClass(value) {
32
+ if (!value.includes("z-")) return false;
33
+ return RAW_Z_CLASS.test(value) || RAW_Z_ARBITRARY.test(value);
34
+ }
35
+ /** The numeric literals a `zIndex` value can be: `10`, `-1`, `"10"`, `a ? 20 : undefined`. */
36
+ function numericLiterals(node, out) {
37
+ if (node.type === "ConditionalExpression") {
38
+ numericLiterals(node.consequent, out);
39
+ numericLiterals(node.alternate, out);
40
+ } else if (node.type === "LogicalExpression") {
41
+ numericLiterals(node.left, out);
42
+ numericLiterals(node.right, out);
43
+ } else if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "ParenthesizedExpression" || node.type === "JSXExpressionContainer") numericLiterals(node.expression, out);
44
+ else if (node.type === "UnaryExpression" && (node.operator === "-" || node.operator === "+")) {
45
+ if (numericLiterals(node.argument, []).length > 0) out.push(node);
46
+ } else if (node.type === "Literal" && typeof node.value === "number") out.push(node);
47
+ else {
48
+ const text = stringValue(node);
49
+ if (text !== void 0 && NUMERIC.test(text)) out.push(node);
50
+ }
51
+ return out;
52
+ }
53
+ const noRawZIndex = {
54
+ meta: {
55
+ type: "suggestion",
56
+ docs: {
57
+ description: "A z-index is a named layer of the Raft Design System's scale (z-raised, z-sticky, ...), not a number.",
58
+ url: "https://raft-design.botiverse.build/foundations/elevation/#layer-order"
59
+ },
60
+ messages: { noRawZIndex: "Use a named layer instead of a z-index number: z-below, z-base, z-raised (inside an `isolate` root), z-control, z-sticky, z-float, z-chrome, z-cover, z-app; a popup, dialog, lightbox, toast or drag lift takes raft-ui's own component and no z-index. If this number must stay, say why in a `// raft-ui: z-index <reason>` comment on the line above. https://raft-design.botiverse.build/foundations/elevation/#layer-order" },
61
+ schema: []
62
+ },
63
+ create(context) {
64
+ const sourceCode = context.sourceCode ?? context.getSourceCode?.();
65
+ let escapeLines;
66
+ function line(index) {
67
+ return sourceCode.getLocFromIndex(index).line;
68
+ }
69
+ /** The lines an escape comment covers: the one it ends on and the next. */
70
+ function escaped() {
71
+ if (escapeLines) return escapeLines;
72
+ escapeLines = /* @__PURE__ */ new Set();
73
+ for (const comment of sourceCode?.getAllComments() ?? []) {
74
+ if (!ESCAPE.test(comment.value)) continue;
75
+ const end = comment.loc?.end.line ?? line(comment.range[1]);
76
+ escapeLines.add(end);
77
+ escapeLines.add(end + 1);
78
+ }
79
+ return escapeLines;
80
+ }
81
+ function startLine(node) {
82
+ return node.loc?.start.line ?? line(node.range[0]);
83
+ }
84
+ /** Reports `node` unless an escape comment covers the line `anchor` starts on. */
85
+ function report(node, anchor) {
86
+ const lines = escaped();
87
+ if (lines.size > 0 && lines.has(startLine(anchor))) return;
88
+ context.report({
89
+ node,
90
+ messageId: "noRawZIndex"
91
+ });
92
+ }
93
+ const constStrings = /* @__PURE__ */ new Map();
94
+ const classNames = /* @__PURE__ */ new Set();
95
+ function checkClassString(node, value) {
96
+ if (!hasRawZIndexClass(value)) return;
97
+ if (classContext(node)) return report(node, node);
98
+ const declarator = constDeclarator(node);
99
+ if (!declarator) return;
100
+ const name = declarator.id.name;
101
+ constStrings.set(name, [...constStrings.get(name) ?? [], node]);
102
+ }
103
+ function checkZIndexValue(value, anchor) {
104
+ for (const literal of numericLiterals(value, [])) report(literal, anchor);
105
+ }
106
+ return {
107
+ Literal(node) {
108
+ if (typeof node.value === "string") checkClassString(node, node.value);
109
+ },
110
+ TemplateLiteral(node) {
111
+ checkClassString(node, node.quasis.map((quasi) => quasi.value.raw).join(" "));
112
+ },
113
+ Identifier(node) {
114
+ const parent = node.parent;
115
+ if (parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand) return;
116
+ if (classContext(node)) classNames.add(node.name);
117
+ },
118
+ Property(node) {
119
+ if (propertyKey(node) !== "zIndex") return;
120
+ checkZIndexValue(node.value, node);
121
+ },
122
+ JSXAttribute(node) {
123
+ const name = node.name;
124
+ if (name?.type !== "JSXIdentifier" || name.name !== "zIndex") return;
125
+ const value = node.value;
126
+ if (value) checkZIndexValue(value, node);
127
+ },
128
+ AssignmentExpression(node) {
129
+ const target = node.left;
130
+ if (target.type !== "MemberExpression") return;
131
+ const property = target.property;
132
+ if ((target.computed ? stringValue(property) : property.type === "Identifier" ? property.name : void 0) !== "zIndex") return;
133
+ checkZIndexValue(node.right, node);
134
+ },
135
+ "Program:exit"() {
136
+ for (const name of classNames) for (const node of constStrings.get(name) ?? []) report(node, node);
137
+ }
138
+ };
139
+ }
140
+ };
141
+ //#endregion
142
+ export { hasRawZIndexClass, noRawZIndex };
@@ -186,4 +186,4 @@ const preferScrollArea = {
186
186
  }
187
187
  };
188
188
  //#endregion
189
- export { hasNativeScrollClass, preferScrollArea };
189
+ export { attributeName, classContext, constDeclarator, hasNativeScrollClass, preferScrollArea, propertyKey, stringValue };
@@ -2,6 +2,7 @@
2
2
  "plugins": [],
3
3
  "jsPlugins": ["@botiverse/raft-ui/lint"],
4
4
  "rules": {
5
- "raft-ui/prefer-scroll-area": "error"
5
+ "raft-ui/prefer-scroll-area": "error",
6
+ "raft-ui/no-raw-z-index": "off"
6
7
  }
7
8
  }
package/dist/lint.d.mts CHANGED
@@ -13,8 +13,14 @@ export interface RaftUiLintPlugin {
13
13
  configs: Record<string, RaftUiLintConfig>;
14
14
  }
15
15
  export declare const rules: Record<string, RuleModule>;
16
- /** Every rule of the plugin with its severity, as `recommended.json` lists them. */
17
- export declare const recommendedRules: Record<string, "error">;
16
+ /**
17
+ * Every rule of the plugin with its severity, as `recommended.json` lists them.
18
+ * A rule an app can't pass yet ships `off`, so an app that lints with
19
+ * `--max-warnings=0` (slock) stays green on the upgrade; an app turns it on in
20
+ * its own `rules` to clear what it reports, and it turns `error` here once the
21
+ * app has.
22
+ */
23
+ export declare const recommendedRules: Record<string, "error" | "warn" | "off">;
18
24
  declare const plugin: RaftUiLintPlugin;
19
25
  /** ESLint flat config: `export default [raftUi.configs.recommended]`. */
20
26
  export declare const recommended: RaftUiLintConfig;
package/dist/lint.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { preferScrollArea } from "./lint/prefer-scroll-area.mjs";
2
+ import { noRawZIndex } from "./lint/no-raw-z-index.mjs";
2
3
  //#region src/lint/index.ts
3
4
  /**
4
5
  * raft-ui's lint plugin: the Raft Design System's rules for the code of an app
@@ -10,9 +11,21 @@ import { preferScrollArea } from "./lint/prefer-scroll-area.mjs";
10
11
  * rule added in a later release reaches the app with the upgrade. See
11
12
  * design/about/use-raft-ui.md#lint-rules.
12
13
  */
13
- const rules = { "prefer-scroll-area": preferScrollArea };
14
- /** Every rule of the plugin with its severity, as `recommended.json` lists them. */
15
- const recommendedRules = { "raft-ui/prefer-scroll-area": "error" };
14
+ const rules = {
15
+ "prefer-scroll-area": preferScrollArea,
16
+ "no-raw-z-index": noRawZIndex
17
+ };
18
+ /**
19
+ * Every rule of the plugin with its severity, as `recommended.json` lists them.
20
+ * A rule an app can't pass yet ships `off`, so an app that lints with
21
+ * `--max-warnings=0` (slock) stays green on the upgrade; an app turns it on in
22
+ * its own `rules` to clear what it reports, and it turns `error` here once the
23
+ * app has.
24
+ */
25
+ const recommendedRules = {
26
+ "raft-ui/prefer-scroll-area": "error",
27
+ "raft-ui/no-raw-z-index": "off"
28
+ };
16
29
  const plugin = {
17
30
  meta: { name: "raft-ui" },
18
31
  rules,
package/dist/styles.css CHANGED
@@ -200,6 +200,34 @@
200
200
  }
201
201
  }
202
202
 
203
+ /* Raft Design System layer order (design/foundations/elevation.md#layer-order).
204
+ Each name is an ordinary Tailwind `z-*` utility (`z-raised`, `max-md:z-chrome`,
205
+ `before:z-below`) and a variable for CSS (`var(--z-index-sticky)`). In-page
206
+ layers stay below 50, where the overlays begin; nothing sits above toasts or
207
+ the drag lift. `z-below`, `z-base` and `z-raised` are local: the component's
208
+ root is `isolate`, so they rank parts inside it only. A component that needs
209
+ a third local layer orders it by DOM order inside its own `isolate`.
210
+ Static, so every variable exists for an app's own CSS whether or not a
211
+ utility uses it. `z-index-named.test.ts` keeps the order. */
212
+ @theme static {
213
+ --z-index-below: -1;
214
+ --z-index-base: 0;
215
+ --z-index-raised: 1;
216
+ --z-index-control: 10;
217
+ --z-index-sticky: 20;
218
+ --z-index-float: 30;
219
+ --z-index-chrome: 35;
220
+ --z-index-cover: 40;
221
+ --z-index-app: 45;
222
+ --z-index-scrollbar: 49;
223
+ --z-index-popup: 50;
224
+ --z-index-dialog: 50;
225
+ --z-index-dialog-layer: 60;
226
+ --z-index-lightbox: 70;
227
+ --z-index-toast: 80;
228
+ --z-index-drag: 999;
229
+ }
230
+
203
231
  @theme inline {
204
232
  /* A drag settling: the drop and cancel glide, siblings sliding apart, a
205
233
  layout reflowing on a drop (design/foundations/motion.md). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "description": "React components and CSS foundations for the Raft Design System.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -91,6 +91,7 @@ When reviewing or editing code that uses raft-ui, check:
91
91
  - [ ] Product state (upload, permissions, read/unread) lives in the app; the component renders parts, not a `state` prop
92
92
  - [ ] No shadow written as `shadow-[var(--…)]`: named utility or fully literal
93
93
  - [ ] Every region that scrolls is a `ScrollArea` (or `PanelScrollViewport` in a Panel), not `overflow-auto` / `overflow-scroll`; a native scroller kept on purpose has a `// raft-ui: native-scroll <reason>` comment. The `raft-ui/prefer-scroll-area` lint rule checks it: https://raft-design.botiverse.build/about/use-raft-ui.md#lint-rules
94
+ - [ ] Every z-index is a layer name (`z-raised`, `z-control`, `z-sticky`, `z-float`, `z-chrome`, `z-cover`, `z-app`; `var(--z-index-…)` in CSS), never a number; popups, dialogs, lightboxes, toasts and drag lifts take raft-ui's components and no z-index. A number kept on purpose has a `// raft-ui: z-index <reason>` comment. The `raft-ui/no-raw-z-index` lint rule checks it: https://raft-design.botiverse.build/foundations/elevation.md#layer-order
94
95
 
95
96
  ## Stability
96
97