@kushagradhawan/kookie-ui 0.1.37 → 0.1.39

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 (172) hide show
  1. package/components.css +130 -74
  2. package/dist/cjs/components/_internal/base-button.props.d.ts +1 -1
  3. package/dist/cjs/components/_internal/base-button.props.js +1 -1
  4. package/dist/cjs/components/_internal/base-button.props.js.map +2 -2
  5. package/dist/cjs/components/_internal/base-checkbox.props.d.ts +1 -1
  6. package/dist/cjs/components/_internal/base-checkbox.props.js +1 -1
  7. package/dist/cjs/components/_internal/base-checkbox.props.js.map +2 -2
  8. package/dist/cjs/components/avatar.props.d.ts +1 -1
  9. package/dist/cjs/components/avatar.props.js +1 -1
  10. package/dist/cjs/components/avatar.props.js.map +2 -2
  11. package/dist/cjs/components/button.d.ts +40 -2
  12. package/dist/cjs/components/button.d.ts.map +1 -1
  13. package/dist/cjs/components/button.js +1 -1
  14. package/dist/cjs/components/button.js.map +3 -3
  15. package/dist/cjs/components/chatbar.d.ts.map +1 -1
  16. package/dist/cjs/components/chatbar.js +1 -1
  17. package/dist/cjs/components/chatbar.js.map +2 -2
  18. package/dist/cjs/components/checkbox-cards.props.d.ts +2 -2
  19. package/dist/cjs/components/checkbox-cards.props.js +1 -1
  20. package/dist/cjs/components/checkbox-cards.props.js.map +2 -2
  21. package/dist/cjs/components/checkbox-group.d.ts.map +1 -1
  22. package/dist/cjs/components/checkbox-group.js +1 -1
  23. package/dist/cjs/components/checkbox-group.js.map +3 -3
  24. package/dist/cjs/components/checkbox-group.props.d.ts +1 -1
  25. package/dist/cjs/components/dialog.d.ts.map +1 -1
  26. package/dist/cjs/components/dialog.js +1 -1
  27. package/dist/cjs/components/dialog.js.map +3 -3
  28. package/dist/cjs/components/flex.props.d.ts +3 -3
  29. package/dist/cjs/components/grid.props.d.ts +3 -3
  30. package/dist/cjs/components/radio-cards.props.d.ts +2 -2
  31. package/dist/cjs/components/radio-cards.props.js +1 -1
  32. package/dist/cjs/components/radio-cards.props.js.map +2 -2
  33. package/dist/cjs/components/select.d.ts.map +1 -1
  34. package/dist/cjs/components/select.js +1 -1
  35. package/dist/cjs/components/select.js.map +3 -3
  36. package/dist/cjs/components/shell.d.ts.map +1 -1
  37. package/dist/cjs/components/shell.js +1 -1
  38. package/dist/cjs/components/shell.js.map +3 -3
  39. package/dist/cjs/components/sidebar.d.ts +7 -1
  40. package/dist/cjs/components/sidebar.d.ts.map +1 -1
  41. package/dist/cjs/components/sidebar.js +1 -1
  42. package/dist/cjs/components/sidebar.js.map +3 -3
  43. package/dist/cjs/components/sidebar.props.d.ts +6 -0
  44. package/dist/cjs/components/sidebar.props.d.ts.map +1 -1
  45. package/dist/cjs/components/sidebar.props.js +1 -1
  46. package/dist/cjs/components/sidebar.props.js.map +3 -3
  47. package/dist/cjs/components/table.props.d.ts +7 -7
  48. package/dist/cjs/components/text-field.props.d.ts +4 -4
  49. package/dist/cjs/helpers/extract-margin-props.d.ts +7 -7
  50. package/dist/cjs/hooks/index.d.ts +1 -0
  51. package/dist/cjs/hooks/index.d.ts.map +1 -1
  52. package/dist/cjs/hooks/index.js +1 -1
  53. package/dist/cjs/hooks/index.js.map +3 -3
  54. package/dist/cjs/hooks/use-body-pointer-events-cleanup.d.ts +9 -0
  55. package/dist/cjs/hooks/use-body-pointer-events-cleanup.d.ts.map +1 -0
  56. package/dist/cjs/hooks/use-body-pointer-events-cleanup.js +2 -0
  57. package/dist/cjs/hooks/use-body-pointer-events-cleanup.js.map +7 -0
  58. package/dist/cjs/props/gap.props.d.ts +3 -3
  59. package/dist/cjs/props/gap.props.js +1 -1
  60. package/dist/cjs/props/gap.props.js.map +2 -2
  61. package/dist/cjs/props/layout.props.d.ts +7 -7
  62. package/dist/cjs/props/margin.props.d.ts +7 -7
  63. package/dist/cjs/props/margin.props.js +1 -1
  64. package/dist/cjs/props/margin.props.js.map +2 -2
  65. package/dist/cjs/props/padding.props.d.ts +7 -7
  66. package/dist/cjs/props/padding.props.js +1 -1
  67. package/dist/cjs/props/padding.props.js.map +2 -2
  68. package/dist/esm/components/_internal/base-button.props.d.ts +1 -1
  69. package/dist/esm/components/_internal/base-button.props.js +1 -1
  70. package/dist/esm/components/_internal/base-button.props.js.map +2 -2
  71. package/dist/esm/components/_internal/base-checkbox.props.d.ts +1 -1
  72. package/dist/esm/components/_internal/base-checkbox.props.js +1 -1
  73. package/dist/esm/components/_internal/base-checkbox.props.js.map +2 -2
  74. package/dist/esm/components/avatar.props.d.ts +1 -1
  75. package/dist/esm/components/avatar.props.js +1 -1
  76. package/dist/esm/components/avatar.props.js.map +2 -2
  77. package/dist/esm/components/button.d.ts +40 -2
  78. package/dist/esm/components/button.d.ts.map +1 -1
  79. package/dist/esm/components/button.js +1 -1
  80. package/dist/esm/components/button.js.map +3 -3
  81. package/dist/esm/components/chatbar.d.ts.map +1 -1
  82. package/dist/esm/components/chatbar.js +1 -1
  83. package/dist/esm/components/chatbar.js.map +2 -2
  84. package/dist/esm/components/checkbox-cards.props.d.ts +2 -2
  85. package/dist/esm/components/checkbox-cards.props.js +1 -1
  86. package/dist/esm/components/checkbox-cards.props.js.map +2 -2
  87. package/dist/esm/components/checkbox-group.d.ts.map +1 -1
  88. package/dist/esm/components/checkbox-group.js +1 -1
  89. package/dist/esm/components/checkbox-group.js.map +3 -3
  90. package/dist/esm/components/checkbox-group.props.d.ts +1 -1
  91. package/dist/esm/components/dialog.d.ts.map +1 -1
  92. package/dist/esm/components/dialog.js +1 -1
  93. package/dist/esm/components/dialog.js.map +3 -3
  94. package/dist/esm/components/flex.props.d.ts +3 -3
  95. package/dist/esm/components/grid.props.d.ts +3 -3
  96. package/dist/esm/components/radio-cards.props.d.ts +2 -2
  97. package/dist/esm/components/radio-cards.props.js +1 -1
  98. package/dist/esm/components/radio-cards.props.js.map +2 -2
  99. package/dist/esm/components/select.d.ts.map +1 -1
  100. package/dist/esm/components/select.js +1 -1
  101. package/dist/esm/components/select.js.map +3 -3
  102. package/dist/esm/components/shell.d.ts.map +1 -1
  103. package/dist/esm/components/shell.js +1 -1
  104. package/dist/esm/components/shell.js.map +3 -3
  105. package/dist/esm/components/sidebar.d.ts +7 -1
  106. package/dist/esm/components/sidebar.d.ts.map +1 -1
  107. package/dist/esm/components/sidebar.js +1 -1
  108. package/dist/esm/components/sidebar.js.map +3 -3
  109. package/dist/esm/components/sidebar.props.d.ts +6 -0
  110. package/dist/esm/components/sidebar.props.d.ts.map +1 -1
  111. package/dist/esm/components/sidebar.props.js +1 -1
  112. package/dist/esm/components/sidebar.props.js.map +3 -3
  113. package/dist/esm/components/table.props.d.ts +7 -7
  114. package/dist/esm/components/text-field.props.d.ts +4 -4
  115. package/dist/esm/helpers/extract-margin-props.d.ts +7 -7
  116. package/dist/esm/hooks/index.d.ts +1 -0
  117. package/dist/esm/hooks/index.d.ts.map +1 -1
  118. package/dist/esm/hooks/index.js +1 -1
  119. package/dist/esm/hooks/index.js.map +3 -3
  120. package/dist/esm/hooks/use-body-pointer-events-cleanup.d.ts +9 -0
  121. package/dist/esm/hooks/use-body-pointer-events-cleanup.d.ts.map +1 -0
  122. package/dist/esm/hooks/use-body-pointer-events-cleanup.js +2 -0
  123. package/dist/esm/hooks/use-body-pointer-events-cleanup.js.map +7 -0
  124. package/dist/esm/props/gap.props.d.ts +3 -3
  125. package/dist/esm/props/gap.props.js +1 -1
  126. package/dist/esm/props/gap.props.js.map +2 -2
  127. package/dist/esm/props/layout.props.d.ts +7 -7
  128. package/dist/esm/props/margin.props.d.ts +7 -7
  129. package/dist/esm/props/margin.props.js +1 -1
  130. package/dist/esm/props/margin.props.js.map +2 -2
  131. package/dist/esm/props/padding.props.d.ts +7 -7
  132. package/dist/esm/props/padding.props.js +1 -1
  133. package/dist/esm/props/padding.props.js.map +2 -2
  134. package/layout/tokens.css +3 -0
  135. package/layout/utilities.css +1806 -42
  136. package/layout.css +1809 -42
  137. package/package.json +1 -1
  138. package/src/components/_internal/base-button.css +179 -73
  139. package/src/components/_internal/base-button.props.ts +1 -1
  140. package/src/components/_internal/base-checkbox.props.ts +1 -1
  141. package/src/components/avatar.props.tsx +1 -1
  142. package/src/components/button.css +13 -21
  143. package/src/components/button.tsx +79 -2
  144. package/src/components/chatbar.tsx +5 -2
  145. package/src/components/checkbox-cards.props.tsx +1 -1
  146. package/src/components/checkbox-group.tsx +14 -6
  147. package/src/components/dialog.tsx +4 -0
  148. package/src/components/radio-cards.props.tsx +1 -1
  149. package/src/components/select.css +9 -0
  150. package/src/components/select.tsx +11 -1
  151. package/src/components/shell.tsx +34 -3
  152. package/src/components/sidebar.css +15 -3
  153. package/src/components/sidebar.props.tsx +3 -0
  154. package/src/components/sidebar.tsx +27 -0
  155. package/src/hooks/index.ts +2 -1
  156. package/src/hooks/use-body-pointer-events-cleanup.ts +81 -0
  157. package/src/props/gap.props.ts +1 -1
  158. package/src/props/margin.props.ts +1 -1
  159. package/src/props/padding.props.ts +1 -1
  160. package/src/styles/tokens/blur.css +3 -0
  161. package/src/styles/tokens/constants.css +38 -35
  162. package/src/styles/tokens/radius.css +3 -0
  163. package/src/styles/tokens/shadow.css +64 -89
  164. package/src/styles/tokens/space.css +3 -0
  165. package/src/styles/tokens/transition.css +25 -12
  166. package/src/styles/utilities/gap.css +27 -0
  167. package/src/styles/utilities/margin.css +205 -7
  168. package/src/styles/utilities/padding.css +69 -0
  169. package/styles.css +1973 -144
  170. package/tokens/base.css +34 -25
  171. package/tokens.css +37 -28
  172. package/utilities.css +1806 -42
@@ -5,6 +5,40 @@ import { BaseButton } from './_internal/base-button.js';
5
5
  import { Tooltip } from './tooltip.js';
6
6
  import type { BaseButtonProps } from './_internal/base-button.js';
7
7
 
8
+ /**
9
+ * Styles that can be overridden for a particular interaction state
10
+ */
11
+ interface ButtonOverrideStateStyles {
12
+ color?: string;
13
+ background?: string;
14
+ backgroundColor?: string;
15
+ boxShadow?: string;
16
+ filter?: string;
17
+ outline?: string;
18
+ outlineOffset?: string;
19
+ opacity?: string | number;
20
+ }
21
+
22
+ /**
23
+ * Full set of override styles keyed by interaction state
24
+ */
25
+ interface ButtonOverrideStyles {
26
+ /** Default/idle state styles */
27
+ normal?: ButtonOverrideStateStyles;
28
+ /** Hover state styles */
29
+ hover?: ButtonOverrideStateStyles;
30
+ /** Active (mouse down) state styles */
31
+ active?: ButtonOverrideStateStyles;
32
+ /** Toggle pressed state styles (data-state="on") */
33
+ pressed?: ButtonOverrideStateStyles;
34
+ /** Open state styles (e.g., when used as a trigger) */
35
+ open?: ButtonOverrideStateStyles;
36
+ /** Disabled state styles */
37
+ disabled?: ButtonOverrideStateStyles;
38
+ /** Focus-visible outline styles */
39
+ focus?: Pick<ButtonOverrideStateStyles, 'outline' | 'outlineOffset'>;
40
+ }
41
+
8
42
  type ButtonElement = React.ElementRef<typeof BaseButton>;
9
43
 
10
44
  /**
@@ -28,7 +62,14 @@ interface ButtonTooltipProps {
28
62
  * Core Button props excluding the 'as' prop for polymorphic behavior
29
63
  * Combines BaseButton props with tooltip functionality
30
64
  */
31
- type ButtonOwnProps = Omit<BaseButtonProps, 'as'> & ButtonTooltipProps;
65
+ type ButtonOwnProps = Omit<BaseButtonProps, 'as'> &
66
+ ButtonTooltipProps & {
67
+ /**
68
+ * When using variant="override", provide token-based styles per state.
69
+ * We propagate these into CSS variables consumed by the override variant.
70
+ */
71
+ overrideStyles?: ButtonOverrideStyles;
72
+ };
32
73
 
33
74
  /**
34
75
  * Polymorphic Button props that support rendering as different HTML elements
@@ -79,6 +120,7 @@ const Button = React.forwardRef(
79
120
  tooltipAlign = 'center',
80
121
  tooltipDelayDuration,
81
122
  tooltipDisableHoverableContent,
123
+ overrideStyles,
82
124
  ...props
83
125
  }: ButtonProps,
84
126
  forwardedRef: React.ForwardedRef<ButtonElement>,
@@ -94,12 +136,47 @@ const Button = React.forwardRef(
94
136
  );
95
137
 
96
138
  // Create the base button element with tooltip accessibility props
139
+ // Map overrideStyles to CSS variables consumed by the override variant rules
140
+ const overrideVars = React.useMemo(() => {
141
+ if (!overrideStyles) return undefined;
142
+ const vars: Record<string, string | number> = {};
143
+ const setVar = (key: string, value: string | number | undefined) => {
144
+ if (value !== undefined) vars[key] = value;
145
+ };
146
+ const apply = (prefix: string, s?: ButtonOverrideStateStyles) => {
147
+ if (!s) return;
148
+ setVar(`--button-override-${prefix}color`, s.color);
149
+ setVar(`--button-override-${prefix}background`, s.background);
150
+ setVar(`--button-override-${prefix}background-color`, s.backgroundColor);
151
+ setVar(`--button-override-${prefix}box-shadow`, s.boxShadow);
152
+ setVar(`--button-override-${prefix}filter`, s.filter);
153
+ setVar(`--button-override-${prefix}outline`, s.outline);
154
+ setVar(`--button-override-${prefix}outline-offset`, s.outlineOffset);
155
+ setVar(`--button-override-${prefix}opacity`, s.opacity as any);
156
+ };
157
+
158
+ apply('', overrideStyles.normal);
159
+ apply('hover-', overrideStyles.hover);
160
+ apply('active-', overrideStyles.active);
161
+ apply('pressed-', overrideStyles.pressed);
162
+ apply('open-', overrideStyles.open);
163
+ apply('disabled-', overrideStyles.disabled);
164
+
165
+ if (overrideStyles.focus) {
166
+ setVar('--button-override-focus-outline', overrideStyles.focus.outline);
167
+ setVar('--button-override-focus-outline-offset', overrideStyles.focus.outlineOffset);
168
+ }
169
+
170
+ return vars as unknown as React.CSSProperties;
171
+ }, [overrideStyles]);
172
+
97
173
  const button = (
98
174
  <BaseButton
99
175
  {...props}
100
176
  {...tooltipAccessibilityProps}
101
177
  ref={forwardedRef}
102
178
  className={classNames('rt-Button', className)}
179
+ style={overrideVars ? { ...overrideVars, ...(props as any).style } : (props as any).style}
103
180
  />
104
181
  );
105
182
 
@@ -128,4 +205,4 @@ const Button = React.forwardRef(
128
205
  Button.displayName = 'Button';
129
206
 
130
207
  export { Button };
131
- export type { ButtonProps };
208
+ export type { ButtonProps, ButtonOverrideStyles, ButtonOverrideStateStyles };
@@ -253,11 +253,14 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
253
253
  const textareaRef = React.useRef<HTMLTextAreaElement>(null);
254
254
 
255
255
  // Attachments state
256
- const isAttachmentsControlled = attachmentsProp != null;
256
+ // Treat `attachments` as controlled if the prop is provided, even if its value is `undefined`.
257
+ // This avoids switching between controlled and uncontrolled when a consumer sets
258
+ // `attachments={undefined}` to clear attachments. In that case we normalize to an empty array.
259
+ const isAttachmentsControlled = 'attachments' in props;
257
260
  const [attachmentsUncontrolled, setAttachmentsUncontrolled] =
258
261
  React.useState<ChatbarAttachment[]>(defaultAttachments);
259
262
  const attachments = isAttachmentsControlled
260
- ? (attachmentsProp as ChatbarAttachment[])
263
+ ? ((attachmentsProp ?? []) as ChatbarAttachment[])
261
264
  : attachmentsUncontrolled;
262
265
 
263
266
  // Track generated object URLs for cleanup
@@ -5,7 +5,7 @@ import { gridPropDefs } from './grid.props.js';
5
5
 
6
6
  import type { PropDef } from '../props/prop-def.js';
7
7
 
8
- const sizes = ['1', '2', '3', '4', '5'] as const;
8
+ const sizes = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] as const;
9
9
  const variants = ['outline', 'classic', 'ghost', 'soft', 'surface'] as const;
10
10
  const panelBackgrounds = ['solid', 'translucent'] as const;
11
11
 
@@ -47,7 +47,7 @@ const CheckboxGroupRoot = React.forwardRef<CheckboxGroupRootElement, CheckboxGro
47
47
  variant = checkboxGroupRootPropDefs.variant.default,
48
48
  ...props
49
49
  }: ScopedProps<CheckboxGroupRootProps>,
50
- forwardedRef
50
+ forwardedRef,
51
51
  ) => {
52
52
  const { __scopeCheckboxGroup, className, ...rootProps } = extractProps(props, marginPropDefs);
53
53
  const checkboxGroupScope = useCheckboxGroupScope(__scopeCheckboxGroup);
@@ -67,7 +67,7 @@ const CheckboxGroupRoot = React.forwardRef<CheckboxGroupRootElement, CheckboxGro
67
67
  />
68
68
  </CheckboxGroupProvider>
69
69
  );
70
- }
70
+ },
71
71
  );
72
72
  CheckboxGroupRoot.displayName = 'CheckboxGroup.Root';
73
73
 
@@ -80,13 +80,21 @@ const CheckboxGroupItem = React.forwardRef<CheckboxGroupItemElement, CheckboxGro
80
80
  const { __scopeCheckboxGroup, children, className, style, ...props } = _props;
81
81
  const { size } = useCheckboxGroupContext('CheckboxGroupItem', __scopeCheckboxGroup);
82
82
 
83
+ // Helper to safely convert size to Text-compatible size
84
+ const getTextSize = (sizeValue: any): '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
85
+ if (typeof sizeValue === 'string' && /^[1-9]$/.test(sizeValue)) {
86
+ return sizeValue as any;
87
+ }
88
+ return '3';
89
+ };
90
+
83
91
  // Render `<Text as="label">` if children are provided, otherwise render
84
92
  // the solo checkbox to allow building out your custom layouts with it.
85
93
  if (children) {
86
94
  return (
87
95
  <Text
88
96
  as="label"
89
- size={size}
97
+ size={getTextSize(size)}
90
98
  className={classNames('rt-CheckboxGroupItem', className)}
91
99
  style={style}
92
100
  >
@@ -109,7 +117,7 @@ const CheckboxGroupItem = React.forwardRef<CheckboxGroupItemElement, CheckboxGro
109
117
  style={style}
110
118
  />
111
119
  );
112
- }
120
+ },
113
121
  );
114
122
  CheckboxGroupItem.displayName = 'CheckboxGroup.Item';
115
123
 
@@ -125,7 +133,7 @@ const CheckboxGroupItemCheckbox = React.forwardRef<
125
133
  const { color, className } = extractProps(
126
134
  { ...props, ...context },
127
135
  checkboxGroupRootPropDefs,
128
- marginPropDefs
136
+ marginPropDefs,
129
137
  );
130
138
  return (
131
139
  <CheckboxGroupPrimitive.Item
@@ -137,7 +145,7 @@ const CheckboxGroupItemCheckbox = React.forwardRef<
137
145
  'rt-reset',
138
146
  'rt-BaseCheckboxRoot',
139
147
  'rt-CheckboxGroupItemCheckbox',
140
- className
148
+ className,
141
149
  )}
142
150
  >
143
151
  <CheckboxGroupPrimitive.Indicator
@@ -8,6 +8,7 @@ import { Text } from './text.js';
8
8
  import { Theme } from './theme.js';
9
9
  import { extractProps } from '../helpers/extract-props.js';
10
10
  import { requireReactElement } from '../helpers/require-react-element.js';
11
+ import { useBodyPointerEventsCleanup } from '../hooks/use-body-pointer-events-cleanup.js';
11
12
 
12
13
  import type { DialogContentOwnProps } from './dialog.props.js';
13
14
  import type {
@@ -93,6 +94,9 @@ const DialogContent = React.forwardRef<DialogContentElement, DialogContentProps>
93
94
  [forwardedRef],
94
95
  );
95
96
 
97
+ // Cleanup stuck pointer-events on body
98
+ useBodyPointerEventsCleanup();
99
+
96
100
  // Focus trap effect
97
101
  React.useEffect(() => {
98
102
  // SSR safety - only run on client
@@ -5,7 +5,7 @@ import { gridPropDefs } from './grid.props.js';
5
5
 
6
6
  import type { PropDef } from '../props/prop-def.js';
7
7
 
8
- const sizes = ['1', '2', '3', '4', '5'] as const;
8
+ const sizes = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] as const;
9
9
  const variants = ['outline', 'classic', 'ghost', 'soft', 'surface'] as const;
10
10
  const panelBackgrounds = ['solid', 'translucent'] as const;
11
11
 
@@ -116,6 +116,15 @@
116
116
  &:where([data-disabled]) {
117
117
  cursor: default;
118
118
  }
119
+
120
+ /* Allow rich content to expand height */
121
+ &:where(.rt-SelectItemRich) {
122
+ height: auto;
123
+ min-height: var(--select-item-height);
124
+ align-items: flex-start;
125
+ padding-top: var(--space-2);
126
+ padding-bottom: var(--space-2);
127
+ }
119
128
  }
120
129
 
121
130
  .rt-SelectItemIndicator {
@@ -189,12 +189,22 @@ interface SelectItemProps
189
189
  extends ComponentPropsWithout<typeof SelectPrimitive.Item, RemovedProps> {}
190
190
  const SelectItem = React.forwardRef<SelectItemElement, SelectItemProps>((props, forwardedRef) => {
191
191
  const { className, children, ...itemProps } = props;
192
+
193
+ // Detect if this is rich content (not just a string)
194
+ const isRichContent = typeof children !== 'string';
195
+
192
196
  return (
193
197
  <SelectPrimitive.Item
194
198
  {...itemProps}
195
199
  asChild={false}
196
200
  ref={forwardedRef}
197
- className={classNames('rt-reset', 'rt-BaseMenuItem', 'rt-SelectItem', className)}
201
+ className={classNames(
202
+ 'rt-reset',
203
+ 'rt-BaseMenuItem',
204
+ 'rt-SelectItem',
205
+ { 'rt-SelectItemRich': isRichContent },
206
+ className,
207
+ )}
198
208
  >
199
209
  <SelectPrimitive.ItemIndicator className="rt-SelectItemIndicator">
200
210
  <ThickCheckIcon className="rt-SelectItemIndicatorIcon" />
@@ -34,6 +34,9 @@ import { ChevronDownIcon } from './icons.js';
34
34
  import { inert } from '../helpers/inert.js';
35
35
  import * as Sheet from './sheet.js';
36
36
  import { VisuallyHidden } from './visually-hidden.js';
37
+ import { ShellSidebarSectionContext } from './sidebar.js';
38
+
39
+ import type { ShellSidebarSectionContextValue } from './sidebar.js';
37
40
 
38
41
  /** Logical document direction. Derived from document root unless `rtl` is passed. */
39
42
  type ShellDirection = 'ltr' | 'rtl';
@@ -508,7 +511,21 @@ const Rail = Object.assign(
508
511
  [shell, side],
509
512
  );
510
513
 
511
- return <RailContext.Provider value={railContext}>{children}</RailContext.Provider>;
514
+ const sidebarSectionContext = React.useMemo<ShellSidebarSectionContextValue>(
515
+ () => ({
516
+ side,
517
+ section: 'rail',
518
+ }),
519
+ [side],
520
+ );
521
+
522
+ return (
523
+ <RailContext.Provider value={railContext}>
524
+ <ShellSidebarSectionContext.Provider value={sidebarSectionContext}>
525
+ {children}
526
+ </ShellSidebarSectionContext.Provider>
527
+ </RailContext.Provider>
528
+ );
512
529
  }),
513
530
  { displayName: 'Shell.Sidebar.Rail', __shellSlot: 'rail' as const },
514
531
  );
@@ -530,7 +547,21 @@ const Panel = Object.assign(
530
547
  [shell.activeToolBySide, shell.activeContextBySide, side],
531
548
  );
532
549
 
533
- return <PanelContext.Provider value={panelContext}>{children}</PanelContext.Provider>;
550
+ const sidebarSectionContext = React.useMemo<ShellSidebarSectionContextValue>(
551
+ () => ({
552
+ side,
553
+ section: 'panel',
554
+ }),
555
+ [side],
556
+ );
557
+
558
+ return (
559
+ <PanelContext.Provider value={panelContext}>
560
+ <ShellSidebarSectionContext.Provider value={sidebarSectionContext}>
561
+ {children}
562
+ </ShellSidebarSectionContext.Provider>
563
+ </PanelContext.Provider>
564
+ );
534
565
  }),
535
566
  { displayName: 'Shell.Sidebar.Panel', __shellSlot: 'panel' as const },
536
567
  );
@@ -935,7 +966,7 @@ const Trigger = React.forwardRef<React.ElementRef<typeof IconButton>, GlobalTrig
935
966
  <IconButton
936
967
  {...props}
937
968
  ref={ref}
938
- variant="soft"
969
+ variant="ghost"
939
970
  aria-controls={controlsId}
940
971
  aria-expanded={expanded}
941
972
  onClick={(e) => {
@@ -3,15 +3,20 @@
3
3
  @import './_internal/base-sidebar-menu.css';
4
4
 
5
5
  /*
6
- Compact rail presentation
7
- - Pattern B (split): scope to .rt-ShellSidebarRail
8
- - Pattern A (single): scope to Shell region in rail state
6
+ Layout-specific presentation
7
+ - Rail layout: compact, icon-focused
8
+ - Panel layout: full-width with text labels
9
+ - Context-aware: works with Shell.Sidebar or standalone
9
10
  */
11
+
12
+ /* Rail Layout Styling */
13
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarContent),
10
14
  .rt-ShellSidebarRail :where(.rt-SidebarContent),
11
15
  :where(.rt-ShellSidebar[data-state='rail'] .rt-SidebarContent) {
12
16
  padding: var(--space-2);
13
17
  }
14
18
 
19
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarMenuButton),
15
20
  .rt-ShellSidebarRail :where(.rt-SidebarMenuButton),
16
21
  :where(.rt-ShellSidebar[data-state='rail'] .rt-SidebarMenuButton) {
17
22
  justify-content: center;
@@ -21,6 +26,8 @@
21
26
  padding: var(--space-2) var(--space-1);
22
27
  }
23
28
 
29
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarMenuBadge),
30
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarMenuShortcut),
24
31
  .rt-ShellSidebarRail :where(.rt-SidebarMenuBadge),
25
32
  .rt-ShellSidebarRail :where(.rt-SidebarMenuShortcut),
26
33
  :where(.rt-ShellSidebar[data-state='rail'] .rt-SidebarMenuBadge),
@@ -28,6 +35,7 @@
28
35
  display: none;
29
36
  }
30
37
 
38
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarGroupLabel),
31
39
  .rt-ShellSidebarRail :where(.rt-SidebarGroupLabel),
32
40
  :where(.rt-ShellSidebar[data-state='rail'] .rt-SidebarGroupLabel) {
33
41
  display: block;
@@ -39,11 +47,13 @@
39
47
  }
40
48
 
41
49
  /* Hide submenu chevrons and flatten sublists in rail */
50
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarMenuSubTriggerIcon),
42
51
  .rt-ShellSidebarRail :where(.rt-SidebarMenuSubTriggerIcon),
43
52
  :where(.rt-ShellSidebar[data-state='rail'] .rt-SidebarMenuSubTriggerIcon) {
44
53
  display: none;
45
54
  }
46
55
 
56
+ :where(.rt-SidebarContainer.rt-layout-rail .rt-SidebarMenuSubList),
47
57
  .rt-ShellSidebarRail :where(.rt-SidebarMenuSubList),
48
58
  :where(.rt-ShellSidebar[data-state='rail'] .rt-SidebarMenuSubList) {
49
59
  padding-left: 0;
@@ -51,6 +61,8 @@
51
61
  margin-left: 0;
52
62
  }
53
63
 
64
+ /* Panel Layout Styling (default) - inherits existing styles */
65
+
54
66
  /* Note: label styling omitted to avoid type-selector-specificity; use Text component for fine control */
55
67
 
56
68
  /* Container Variants */
@@ -19,6 +19,7 @@ const menuVariants = ['solid', 'soft'] as const;
19
19
  const types = ['sidebar', 'floating'] as const;
20
20
  const sides = ['left', 'right'] as const;
21
21
  const collapsibleModes = ['offcanvas', 'icon', 'none'] as const;
22
+ const layouts = ['rail', 'panel'] as const;
22
23
 
23
24
  const sidebarPropDefs = {
24
25
  ...asChildPropDef,
@@ -38,6 +39,7 @@ const sidebarPropDefs = {
38
39
  values: collapsibleModes,
39
40
  default: 'offcanvas',
40
41
  },
42
+ layout: { type: 'enum', className: 'rt-layout', values: layouts, default: undefined },
41
43
  panelBackground: { type: 'enum', values: ['solid', 'translucent'], default: undefined },
42
44
  ...colorPropDef,
43
45
  ...highContrastPropDef,
@@ -48,6 +50,7 @@ const sidebarPropDefs = {
48
50
  type: PropDef<(typeof types)[number]>;
49
51
  side: PropDef<(typeof sides)[number]>;
50
52
  collapsible: PropDef<(typeof collapsibleModes)[number]>;
53
+ layout: PropDef<(typeof layouts)[number]>;
51
54
  panelBackground: PropDef<'solid' | 'translucent'>;
52
55
  };
53
56
 
@@ -39,6 +39,22 @@ function useSidebarVisual() {
39
39
  return React.useContext(SidebarVisualContext);
40
40
  }
41
41
 
42
+ // Context detection for Shell.Sidebar integration
43
+ type ShellSidebarSectionContextValue = {
44
+ side: 'start' | 'end';
45
+ section: 'rail' | 'panel';
46
+ };
47
+
48
+ // Create a context that Shell.Sidebar can provide
49
+ const ShellSidebarSectionContext = React.createContext<ShellSidebarSectionContextValue | null>(
50
+ null,
51
+ );
52
+
53
+ // This context comes from Shell.Sidebar when Sidebar is used within Shell
54
+ function useShellSidebarSection(): ShellSidebarSectionContextValue | null {
55
+ return React.useContext(ShellSidebarSectionContext);
56
+ }
57
+
42
58
  // Main Sidebar component
43
59
  type SidebarOwnProps = GetPropDefTypes<typeof sidebarPropDefs>;
44
60
  interface SidebarProps extends ComponentPropsWithout<'div', RemovedProps>, SidebarOwnProps {}
@@ -50,6 +66,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, SidebarProps>((props, forwarded
50
66
  size = sidebarPropDefs.size.default,
51
67
  variant = sidebarPropDefs.variant.default,
52
68
  menuVariant = sidebarPropDefs.menuVariant.default,
69
+ layout = sidebarPropDefs.layout.default,
53
70
  // type = sidebarPropDefs.type.default,
54
71
  // side = sidebarPropDefs.side.default,
55
72
  // collapsible = sidebarPropDefs.collapsible.default,
@@ -62,6 +79,10 @@ const Sidebar = React.forwardRef<HTMLDivElement, SidebarProps>((props, forwarded
62
79
  const { asChild: _, panelBackground: __, ...safeRootProps } = rootProps; // Remove asChild and panelBackground from DOM props
63
80
  const resolvedColor = color || themeContext.accentColor;
64
81
 
82
+ // Detect Shell.Sidebar context to auto-resolve layout
83
+ const shellSection = useShellSidebarSection();
84
+ const resolvedLayout = layout || shellSection?.section || 'panel'; // Default to 'panel' if no context
85
+
65
86
  // Update context with current props - we'll pass the resolved values
66
87
  const resolvedSize = typeof size === 'object' ? size.initial || '2' : size;
67
88
  return (
@@ -78,10 +99,12 @@ const Sidebar = React.forwardRef<HTMLDivElement, SidebarProps>((props, forwarded
78
99
  `rt-variant-${variant}`,
79
100
  `rt-r-size-${resolvedSize}`,
80
101
  `rt-menu-variant-${menuVariant}`,
102
+ resolvedLayout && `rt-layout-${resolvedLayout}`,
81
103
  )}
82
104
  data-accent-color={resolvedColor}
83
105
  data-high-contrast={highContrast || undefined}
84
106
  data-panel-background={panelBackground}
107
+ data-layout={resolvedLayout}
85
108
  >
86
109
  {children}
87
110
  </div>
@@ -592,4 +615,8 @@ export type {
592
615
  SidebarHeaderProps as HeaderProps,
593
616
  SidebarFooterProps as FooterProps,
594
617
  BadgeConfig,
618
+ ShellSidebarSectionContextValue,
595
619
  };
620
+
621
+ // Export context for Shell.Sidebar integration
622
+ export { ShellSidebarSectionContext };
@@ -1 +1,2 @@
1
- export { useLiveAnnouncer } from './use-live-announcer.js';
1
+ export { useLiveAnnouncer } from './use-live-announcer.js';
2
+ export { useBodyPointerEventsCleanup } from './use-body-pointer-events-cleanup.js';
@@ -0,0 +1,81 @@
1
+ import * as React from 'react';
2
+
3
+ let bodyCleanupInstalled = false;
4
+
5
+ /**
6
+ * Hook to cleanup stuck pointer-events: none on document.body
7
+ *
8
+ * This addresses an issue where react-remove-scroll (used by Radix UI Dialog)
9
+ * sometimes fails to restore body pointer-events after dialog closes,
10
+ * leaving the page unclickable.
11
+ */
12
+ export function useBodyPointerEventsCleanup() {
13
+ React.useEffect(() => {
14
+ if (typeof document === 'undefined') return;
15
+
16
+ let timeoutId: number | undefined;
17
+
18
+ const hasOpenModal = (): boolean => {
19
+ // Detect any open modal dialogs/alertdialogs
20
+ return Boolean(
21
+ document.querySelector(
22
+ '[role="dialog"][aria-modal="true"], [role="alertdialog"][aria-modal="true"]',
23
+ ),
24
+ );
25
+ };
26
+
27
+ const cleanup = () => {
28
+ if (document.body.style.pointerEvents === 'none' && !hasOpenModal()) {
29
+ document.body.style.pointerEvents = '';
30
+ }
31
+ };
32
+
33
+ const scheduleCleanup = (delay = 50) => {
34
+ if (timeoutId) window.clearTimeout(timeoutId);
35
+ timeoutId = window.setTimeout(cleanup, delay);
36
+ };
37
+
38
+ // Initial run to catch already-stuck state
39
+ scheduleCleanup(100);
40
+
41
+ // If already installed globally, don't re-register listeners/observers
42
+ if (bodyCleanupInstalled) {
43
+ return () => {
44
+ if (timeoutId) window.clearTimeout(timeoutId);
45
+ };
46
+ }
47
+
48
+ bodyCleanupInstalled = true;
49
+
50
+ const onPointer = () => scheduleCleanup(50);
51
+ const onKey = (event: KeyboardEvent) => {
52
+ if (event.key === 'Escape' || event.key === 'Enter' || event.key === ' ') scheduleCleanup(50);
53
+ };
54
+ const onVisibility = () => {
55
+ if (!document.hidden) scheduleCleanup(50);
56
+ };
57
+ const onTransitionEnd = () => scheduleCleanup(0);
58
+ const onAnimationEnd = () => scheduleCleanup(0);
59
+
60
+ // Listen for common interactions that close overlays/menus
61
+ document.addEventListener('pointerup', onPointer, true);
62
+ document.addEventListener('click', onPointer, true);
63
+ document.addEventListener('keydown', onKey, true);
64
+ document.addEventListener('keyup', onKey, true);
65
+ document.addEventListener('visibilitychange', onVisibility);
66
+ document.addEventListener('transitionend', onTransitionEnd, true);
67
+ document.addEventListener('animationend', onAnimationEnd, true);
68
+
69
+ // Observe body style changes (where pointer-events is applied) and DOM mutations
70
+ const bodyObserver = new MutationObserver(() => scheduleCleanup(0));
71
+ bodyObserver.observe(document.body, { attributes: true, attributeFilter: ['style'] });
72
+
73
+ const domObserver = new MutationObserver(() => scheduleCleanup(0));
74
+ domObserver.observe(document, { childList: true, subtree: true });
75
+
76
+ // Keep listeners/observers for the app lifetime to ensure cleanup even after overlays unmount
77
+ return () => {
78
+ if (timeoutId) window.clearTimeout(timeoutId);
79
+ };
80
+ }, []);
81
+ }
@@ -1,6 +1,6 @@
1
1
  import type { PropDef } from './prop-def.js';
2
2
 
3
- const gapValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] as const;
3
+ const gapValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] as const;
4
4
 
5
5
  const gapPropDefs = {
6
6
  /**
@@ -1,7 +1,7 @@
1
1
  import type { PropDef, GetPropDefTypes } from './prop-def.js';
2
2
 
3
3
  // prettier-ignore
4
- const marginValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9'] as const;
4
+ const marginValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10', '-11', '-12'] as const;
5
5
 
6
6
  const marginPropDefs = {
7
7
  /**
@@ -1,6 +1,6 @@
1
1
  import type { GetPropDefTypes, PropDef } from './prop-def.js';
2
2
 
3
- const paddingValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] as const;
3
+ const paddingValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] as const;
4
4
 
5
5
  const paddingPropDefs = {
6
6
  /**
@@ -9,6 +9,9 @@
9
9
  --blur-7: calc(28px * var(--scaling) * var(--blur-factor));
10
10
  --blur-8: calc(36px * var(--scaling) * var(--blur-factor));
11
11
  --blur-9: calc(44px * var(--scaling) * var(--blur-factor));
12
+ --blur-10: calc(52px * var(--scaling) * var(--blur-factor));
13
+ --blur-11: calc(60px * var(--scaling) * var(--blur-factor));
14
+ --blur-12: calc(68px * var(--scaling) * var(--blur-factor));
12
15
  }
13
16
 
14
17
  [data-blur='none'] {