@kushagradhawan/kookie-ui 0.1.70 → 0.1.72

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 (164) hide show
  1. package/README.md +4 -0
  2. package/components.css +63 -380
  3. package/dist/cjs/components/_internal/base-button.d.ts.map +1 -1
  4. package/dist/cjs/components/_internal/base-button.js +1 -1
  5. package/dist/cjs/components/_internal/base-button.js.map +3 -3
  6. package/dist/cjs/components/_internal/shell-bottom.d.ts +2 -21
  7. package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -1
  8. package/dist/cjs/components/_internal/shell-bottom.js +1 -1
  9. package/dist/cjs/components/_internal/shell-bottom.js.map +3 -3
  10. package/dist/cjs/components/_internal/shell-inspector.d.ts +10 -21
  11. package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -1
  12. package/dist/cjs/components/_internal/shell-inspector.js +1 -1
  13. package/dist/cjs/components/_internal/shell-inspector.js.map +3 -3
  14. package/dist/cjs/components/_internal/shell-prop-helpers.d.ts +7 -0
  15. package/dist/cjs/components/_internal/shell-prop-helpers.d.ts.map +1 -0
  16. package/dist/cjs/components/_internal/shell-prop-helpers.js +2 -0
  17. package/dist/cjs/components/_internal/shell-prop-helpers.js.map +7 -0
  18. package/dist/cjs/components/_internal/shell-sidebar.d.ts +4 -21
  19. package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -1
  20. package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
  21. package/dist/cjs/components/_internal/shell-sidebar.js.map +3 -3
  22. package/dist/cjs/components/button.d.ts.map +1 -1
  23. package/dist/cjs/components/button.js +1 -1
  24. package/dist/cjs/components/button.js.map +3 -3
  25. package/dist/cjs/components/chatbar.d.ts +11 -2
  26. package/dist/cjs/components/chatbar.d.ts.map +1 -1
  27. package/dist/cjs/components/chatbar.js +1 -1
  28. package/dist/cjs/components/chatbar.js.map +3 -3
  29. package/dist/cjs/components/icon-button.d.ts.map +1 -1
  30. package/dist/cjs/components/icon-button.js +2 -2
  31. package/dist/cjs/components/icon-button.js.map +3 -3
  32. package/dist/cjs/components/schemas/shell.schema.d.ts +70 -70
  33. package/dist/cjs/components/shell.context.d.ts +1 -0
  34. package/dist/cjs/components/shell.context.d.ts.map +1 -1
  35. package/dist/cjs/components/shell.context.js.map +2 -2
  36. package/dist/cjs/components/shell.d.ts +6 -26
  37. package/dist/cjs/components/shell.d.ts.map +1 -1
  38. package/dist/cjs/components/shell.hooks.d.ts +19 -2
  39. package/dist/cjs/components/shell.hooks.d.ts.map +1 -1
  40. package/dist/cjs/components/shell.hooks.js +1 -1
  41. package/dist/cjs/components/shell.hooks.js.map +3 -3
  42. package/dist/cjs/components/shell.js +1 -1
  43. package/dist/cjs/components/shell.js.map +3 -3
  44. package/dist/cjs/components/shell.types.d.ts +21 -0
  45. package/dist/cjs/components/shell.types.d.ts.map +1 -1
  46. package/dist/cjs/components/shell.types.js +1 -1
  47. package/dist/cjs/components/shell.types.js.map +2 -2
  48. package/dist/cjs/components/toggle-button.d.ts.map +1 -1
  49. package/dist/cjs/components/toggle-button.js +1 -1
  50. package/dist/cjs/components/toggle-button.js.map +3 -3
  51. package/dist/cjs/components/toggle-icon-button.d.ts.map +1 -1
  52. package/dist/cjs/components/toggle-icon-button.js +1 -1
  53. package/dist/cjs/components/toggle-icon-button.js.map +3 -3
  54. package/dist/cjs/hooks/index.d.ts +2 -0
  55. package/dist/cjs/hooks/index.d.ts.map +1 -1
  56. package/dist/cjs/hooks/index.js +1 -1
  57. package/dist/cjs/hooks/index.js.map +3 -3
  58. package/dist/cjs/hooks/use-live-announcer.d.ts.map +1 -1
  59. package/dist/cjs/hooks/use-live-announcer.js +2 -2
  60. package/dist/cjs/hooks/use-live-announcer.js.map +3 -3
  61. package/dist/cjs/hooks/use-toggle-state.d.ts +37 -0
  62. package/dist/cjs/hooks/use-toggle-state.d.ts.map +1 -0
  63. package/dist/cjs/hooks/use-toggle-state.js +2 -0
  64. package/dist/cjs/hooks/use-toggle-state.js.map +7 -0
  65. package/dist/cjs/hooks/use-tooltip-wrapper.d.ts +29 -0
  66. package/dist/cjs/hooks/use-tooltip-wrapper.d.ts.map +1 -0
  67. package/dist/cjs/hooks/use-tooltip-wrapper.js +2 -0
  68. package/dist/cjs/hooks/use-tooltip-wrapper.js.map +7 -0
  69. package/dist/esm/components/_internal/base-button.d.ts.map +1 -1
  70. package/dist/esm/components/_internal/base-button.js +1 -1
  71. package/dist/esm/components/_internal/base-button.js.map +3 -3
  72. package/dist/esm/components/_internal/shell-bottom.d.ts +2 -21
  73. package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -1
  74. package/dist/esm/components/_internal/shell-bottom.js +1 -1
  75. package/dist/esm/components/_internal/shell-bottom.js.map +3 -3
  76. package/dist/esm/components/_internal/shell-inspector.d.ts +10 -21
  77. package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -1
  78. package/dist/esm/components/_internal/shell-inspector.js +1 -1
  79. package/dist/esm/components/_internal/shell-inspector.js.map +3 -3
  80. package/dist/esm/components/_internal/shell-prop-helpers.d.ts +7 -0
  81. package/dist/esm/components/_internal/shell-prop-helpers.d.ts.map +1 -0
  82. package/dist/esm/components/_internal/shell-prop-helpers.js +2 -0
  83. package/dist/esm/components/_internal/shell-prop-helpers.js.map +7 -0
  84. package/dist/esm/components/_internal/shell-sidebar.d.ts +4 -21
  85. package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -1
  86. package/dist/esm/components/_internal/shell-sidebar.js +1 -1
  87. package/dist/esm/components/_internal/shell-sidebar.js.map +3 -3
  88. package/dist/esm/components/button.d.ts.map +1 -1
  89. package/dist/esm/components/button.js +1 -1
  90. package/dist/esm/components/button.js.map +3 -3
  91. package/dist/esm/components/chatbar.d.ts +11 -2
  92. package/dist/esm/components/chatbar.d.ts.map +1 -1
  93. package/dist/esm/components/chatbar.js +1 -1
  94. package/dist/esm/components/chatbar.js.map +3 -3
  95. package/dist/esm/components/icon-button.d.ts.map +1 -1
  96. package/dist/esm/components/icon-button.js +2 -2
  97. package/dist/esm/components/icon-button.js.map +3 -3
  98. package/dist/esm/components/schemas/shell.schema.d.ts +70 -70
  99. package/dist/esm/components/shell.context.d.ts +1 -0
  100. package/dist/esm/components/shell.context.d.ts.map +1 -1
  101. package/dist/esm/components/shell.context.js.map +2 -2
  102. package/dist/esm/components/shell.d.ts +6 -26
  103. package/dist/esm/components/shell.d.ts.map +1 -1
  104. package/dist/esm/components/shell.hooks.d.ts +19 -2
  105. package/dist/esm/components/shell.hooks.d.ts.map +1 -1
  106. package/dist/esm/components/shell.hooks.js +1 -1
  107. package/dist/esm/components/shell.hooks.js.map +3 -3
  108. package/dist/esm/components/shell.js +1 -1
  109. package/dist/esm/components/shell.js.map +3 -3
  110. package/dist/esm/components/shell.types.d.ts +21 -0
  111. package/dist/esm/components/shell.types.d.ts.map +1 -1
  112. package/dist/esm/components/shell.types.js.map +2 -2
  113. package/dist/esm/components/toggle-button.d.ts.map +1 -1
  114. package/dist/esm/components/toggle-button.js +1 -1
  115. package/dist/esm/components/toggle-button.js.map +3 -3
  116. package/dist/esm/components/toggle-icon-button.d.ts.map +1 -1
  117. package/dist/esm/components/toggle-icon-button.js +1 -1
  118. package/dist/esm/components/toggle-icon-button.js.map +3 -3
  119. package/dist/esm/hooks/index.d.ts +2 -0
  120. package/dist/esm/hooks/index.d.ts.map +1 -1
  121. package/dist/esm/hooks/index.js +1 -1
  122. package/dist/esm/hooks/index.js.map +3 -3
  123. package/dist/esm/hooks/use-live-announcer.d.ts.map +1 -1
  124. package/dist/esm/hooks/use-live-announcer.js +2 -2
  125. package/dist/esm/hooks/use-live-announcer.js.map +3 -3
  126. package/dist/esm/hooks/use-toggle-state.d.ts +37 -0
  127. package/dist/esm/hooks/use-toggle-state.d.ts.map +1 -0
  128. package/dist/esm/hooks/use-toggle-state.js +2 -0
  129. package/dist/esm/hooks/use-toggle-state.js.map +7 -0
  130. package/dist/esm/hooks/use-tooltip-wrapper.d.ts +29 -0
  131. package/dist/esm/hooks/use-tooltip-wrapper.d.ts.map +1 -0
  132. package/dist/esm/hooks/use-tooltip-wrapper.js +2 -0
  133. package/dist/esm/hooks/use-tooltip-wrapper.js.map +7 -0
  134. package/package.json +4 -4
  135. package/schemas/base-button.json +1 -1
  136. package/schemas/button.json +1 -1
  137. package/schemas/icon-button.json +1 -1
  138. package/schemas/index.json +6 -6
  139. package/schemas/toggle-button.json +1 -1
  140. package/schemas/toggle-icon-button.json +1 -1
  141. package/src/components/_internal/base-button.css +136 -614
  142. package/src/components/_internal/base-button.tsx +15 -13
  143. package/src/components/_internal/shell-bottom.tsx +305 -321
  144. package/src/components/_internal/shell-inspector.tsx +310 -320
  145. package/src/components/_internal/shell-prop-helpers.ts +53 -0
  146. package/src/components/_internal/shell-sidebar.tsx +370 -384
  147. package/src/components/button.tsx +13 -42
  148. package/src/components/chatbar.tsx +7 -3
  149. package/src/components/icon-button.tsx +20 -44
  150. package/src/components/image.css +10 -8
  151. package/src/components/shell.context.tsx +1 -0
  152. package/src/components/shell.hooks.ts +67 -2
  153. package/src/components/shell.tsx +199 -209
  154. package/src/components/shell.types.ts +23 -0
  155. package/src/components/toggle-button.tsx +30 -59
  156. package/src/components/toggle-icon-button.tsx +29 -51
  157. package/src/hooks/index.ts +2 -0
  158. package/src/hooks/use-live-announcer.ts +34 -7
  159. package/src/hooks/use-toggle-state.ts +72 -0
  160. package/src/hooks/use-tooltip-wrapper.ts +28 -0
  161. package/src/styles/tokens/color.css +11 -1
  162. package/styles.css +70 -381
  163. package/tokens/base.css +7 -1
  164. package/tokens.css +7 -1
@@ -3,6 +3,7 @@ import classNames from 'classnames';
3
3
 
4
4
  import { BaseButton } from './_internal/base-button.js';
5
5
  import { Tooltip } from './tooltip.js';
6
+ import { useTooltipWrapper } from '../hooks/use-tooltip-wrapper.js';
6
7
  import type { BaseButtonProps } from './_internal/base-button.js';
7
8
 
8
9
  /**
@@ -84,9 +85,7 @@ type ButtonProps<C extends React.ElementType = 'button'> = ButtonOwnProps & {
84
85
  * Button component type that supports polymorphic rendering
85
86
  * @template C - The element type to render as
86
87
  */
87
- type ButtonComponent = <C extends React.ElementType = 'button'>(
88
- props: ButtonProps<C> & { ref?: React.ForwardedRef<ButtonElement> },
89
- ) => React.ReactElement | null;
88
+ type ButtonComponent = <C extends React.ElementType = 'button'>(props: ButtonProps<C> & { ref?: React.ForwardedRef<ButtonElement> }) => React.ReactElement | null;
90
89
 
91
90
  /**
92
91
  * Button component for triggering actions throughout your interface
@@ -113,27 +112,11 @@ type ButtonComponent = <C extends React.ElementType = 'button'>(
113
112
  */
114
113
  const Button = React.forwardRef(
115
114
  (
116
- {
117
- className,
118
- tooltip,
119
- tooltipSide = 'top',
120
- tooltipAlign = 'center',
121
- tooltipDelayDuration,
122
- tooltipDisableHoverableContent,
123
- overrideStyles,
124
- ...props
125
- }: ButtonProps,
115
+ { className, style, tooltip, tooltipSide = 'top', tooltipAlign = 'center', tooltipDelayDuration, tooltipDisableHoverableContent, overrideStyles, ...props }: ButtonProps,
126
116
  forwardedRef: React.ForwardedRef<ButtonElement>,
127
117
  ) => {
128
- // Generate unique ID for tooltip accessibility
129
- const tooltipId = React.useId();
130
- const hasTooltip = Boolean(tooltip);
131
-
132
- // Prepare accessibility props for tooltip integration
133
- const tooltipAccessibilityProps = React.useMemo(
134
- () => (hasTooltip ? { 'aria-describedby': tooltipId } : {}),
135
- [hasTooltip, tooltipId],
136
- );
118
+ // Use shared tooltip wrapper hook for accessibility props
119
+ const { tooltipId, hasTooltip, accessibilityProps: tooltipAccessibilityProps } = useTooltipWrapper(tooltip);
137
120
 
138
121
  // Create the base button element with tooltip accessibility props
139
122
  // Map overrideStyles to CSS variables consumed by the override variant rules
@@ -152,7 +135,7 @@ const Button = React.forwardRef(
152
135
  setVar(`--button-override-${prefix}filter`, s.filter);
153
136
  setVar(`--button-override-${prefix}outline`, s.outline);
154
137
  setVar(`--button-override-${prefix}outline-offset`, s.outlineOffset);
155
- setVar(`--button-override-${prefix}opacity`, s.opacity as any);
138
+ setVar(`--button-override-${prefix}opacity`, s.opacity);
156
139
  };
157
140
 
158
141
  apply('', overrideStyles.normal);
@@ -167,35 +150,23 @@ const Button = React.forwardRef(
167
150
  setVar('--button-override-focus-outline-offset', overrideStyles.focus.outlineOffset);
168
151
  }
169
152
 
170
- return vars as unknown as React.CSSProperties;
153
+ return vars as React.CSSProperties;
171
154
  }, [overrideStyles]);
172
155
 
173
- const button = (
174
- <BaseButton
175
- {...props}
176
- {...tooltipAccessibilityProps}
177
- ref={forwardedRef}
178
- className={classNames('rt-Button', className)}
179
- style={overrideVars ? { ...overrideVars, ...(props as any).style } : (props as any).style}
180
- />
181
- );
156
+ // Combine override styles with user-provided styles
157
+ const combinedStyle = React.useMemo(() => (overrideVars ? { ...overrideVars, ...style } : style), [overrideVars, style]);
158
+
159
+ const button = <BaseButton {...props} {...tooltipAccessibilityProps} ref={forwardedRef} className={classNames('rt-Button', className)} style={combinedStyle} />;
182
160
 
183
161
  // If no tooltip is provided, return the button as-is for better performance
184
- if (!tooltip) {
162
+ if (!hasTooltip) {
185
163
  return button;
186
164
  }
187
165
 
188
166
  // Wrap with Tooltip when tooltip content is provided
189
167
  // This creates a compound component that handles both button and tooltip functionality
190
168
  return (
191
- <Tooltip
192
- content={tooltip}
193
- side={tooltipSide}
194
- align={tooltipAlign}
195
- delayDuration={tooltipDelayDuration}
196
- disableHoverableContent={tooltipDisableHoverableContent}
197
- id={tooltipId}
198
- >
169
+ <Tooltip content={tooltip} side={tooltipSide} align={tooltipAlign} delayDuration={tooltipDelayDuration} disableHoverableContent={tooltipDisableHoverableContent} id={tooltipId}>
199
170
  {button}
200
171
  </Tooltip>
201
172
  );
@@ -1121,13 +1121,16 @@ const RowEnd = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'
1121
1121
  });
1122
1122
  RowEnd.displayName = 'Chatbar.RowEnd';
1123
1123
 
1124
- type SendProps = IconButtonProps & {
1124
+ type SendProps = Omit<IconButtonProps, 'aria-label' | 'aria-labelledby'> & {
1125
+ /** Optional override for accessible name. Defaults to "Send". */
1126
+ 'aria-label'?: string;
1127
+ 'aria-labelledby'?: string;
1125
1128
  asChild?: boolean;
1126
1129
  clearOnSend?: boolean;
1127
1130
  };
1128
1131
 
1129
1132
  const Send = React.forwardRef<HTMLButtonElement, SendProps>((props, forwardedRef) => {
1130
- const { asChild, clearOnSend = true, disabled, children, className, style, size: sizeProp, variant: variantProp, ...buttonProps } = props;
1133
+ const { asChild, clearOnSend = true, disabled, children, className, style, size: sizeProp, variant: variantProp, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...buttonProps } = props;
1131
1134
  const ctx = useChatbarContext();
1132
1135
 
1133
1136
  const trimmed = ctx.value.trim();
@@ -1160,7 +1163,8 @@ const Send = React.forwardRef<HTMLButtonElement, SendProps>((props, forwardedRef
1160
1163
  }}
1161
1164
  asChild={asChild}
1162
1165
  onClick={handleClick}
1163
- aria-label={(buttonProps as any)['aria-label'] ?? 'Send'}
1166
+ aria-label={ariaLabel ?? (ariaLabelledby ? undefined : 'Send')}
1167
+ aria-labelledby={ariaLabelledby}
1164
1168
  >
1165
1169
  {children ?? (
1166
1170
  <svg
@@ -3,6 +3,7 @@ import classNames from 'classnames';
3
3
 
4
4
  import { BaseButton } from './_internal/base-button.js';
5
5
  import { Tooltip } from './tooltip.js';
6
+ import { useTooltipWrapper } from '../hooks/use-tooltip-wrapper.js';
6
7
  import type { BaseButtonProps } from './_internal/base-button.js';
7
8
 
8
9
  type IconButtonElement = React.ElementRef<typeof BaseButton>;
@@ -57,9 +58,7 @@ type IconButtonProps<C extends React.ElementType = 'button'> = IconButtonOwnProp
57
58
  * IconButton component type that supports polymorphic rendering
58
59
  * @template C - The element type to render as
59
60
  */
60
- type IconButtonComponent = <C extends React.ElementType = 'button'>(
61
- props: IconButtonProps<C> & { ref?: React.ForwardedRef<IconButtonElement> },
62
- ) => React.ReactElement | null;
61
+ type IconButtonComponent = <C extends React.ElementType = 'button'>(props: IconButtonProps<C> & { ref?: React.ForwardedRef<IconButtonElement> }) => React.ReactElement | null;
63
62
 
64
63
  /**
65
64
  * IconButton component for compact, accessible icon-only interactions
@@ -103,68 +102,45 @@ type IconButtonComponent = <C extends React.ElementType = 'button'>(
103
102
  */
104
103
  const IconButton = React.forwardRef(
105
104
  (
106
- {
107
- className,
108
- tooltip,
109
- tooltipSide = 'top',
110
- tooltipAlign = 'center',
111
- tooltipDelayDuration,
112
- tooltipDisableHoverableContent,
113
- ...props
114
- }: IconButtonProps,
105
+ { className, tooltip, tooltipSide = 'top', tooltipAlign = 'center', tooltipDelayDuration, tooltipDisableHoverableContent, ...props }: IconButtonProps,
115
106
  forwardedRef: React.ForwardedRef<IconButtonElement>,
116
107
  ) => {
117
- // Generate unique ID for tooltip accessibility
118
- const tooltipId = React.useId();
108
+ // Use shared tooltip wrapper hook for accessibility props
109
+ const { tooltipId, hasTooltip, accessibilityProps: tooltipAccessibilityProps } = useTooltipWrapper(tooltip);
110
+
119
111
  // Runtime accessibility validation to ensure WCAG compliance
120
112
  // This helps catch accessibility issues during development
121
113
  const hasAriaLabel = 'aria-label' in props && props['aria-label'];
122
114
  const hasAriaLabelledBy = 'aria-labelledby' in props && props['aria-labelledby'];
123
115
  const hasChildren = 'children' in props && props.children;
124
116
 
125
- // Throw descriptive error if no accessible name is provided
117
+ // Validate accessible name - throw in development, log error in production
126
118
  if (!hasAriaLabel && !hasAriaLabelledBy && !hasChildren) {
127
- throw new Error(
119
+ const errorMessage =
128
120
  'IconButton: Icon buttons must have an accessible name. Please provide either:' +
129
- '\n- aria-label prop with descriptive text' +
130
- '\n- aria-labelledby prop referencing a label element' +
131
- '\n- or visible text children',
132
- );
133
- }
121
+ '\n- aria-label prop with descriptive text' +
122
+ '\n- aria-labelledby prop referencing a label element' +
123
+ '\n- or visible text children';
134
124
 
135
- // Prepare accessibility props for tooltip integration
136
- const hasTooltip = Boolean(tooltip);
137
- const tooltipAccessibilityProps = React.useMemo(
138
- () => (hasTooltip ? { 'aria-describedby': tooltipId } : {}),
139
- [hasTooltip, tooltipId],
140
- );
125
+ if (process.env.NODE_ENV === 'development') {
126
+ throw new Error(errorMessage);
127
+ } else {
128
+ console.error(errorMessage);
129
+ }
130
+ }
141
131
 
142
132
  // Create the base icon button element with accessibility props
143
- const iconButton = (
144
- <BaseButton
145
- {...props}
146
- {...tooltipAccessibilityProps}
147
- ref={forwardedRef}
148
- className={classNames('rt-IconButton', className)}
149
- />
150
- );
133
+ const iconButton = <BaseButton {...props} {...tooltipAccessibilityProps} ref={forwardedRef} className={classNames('rt-IconButton', className)} />;
151
134
 
152
135
  // If no tooltip is provided, return the icon button as-is for better performance
153
- if (!tooltip) {
136
+ if (!hasTooltip) {
154
137
  return iconButton;
155
138
  }
156
139
 
157
140
  // Wrap with Tooltip when tooltip content is provided
158
141
  // This creates a compound component that handles both button and tooltip functionality
159
142
  return (
160
- <Tooltip
161
- content={tooltip}
162
- side={tooltipSide}
163
- align={tooltipAlign}
164
- delayDuration={tooltipDelayDuration}
165
- disableHoverableContent={tooltipDisableHoverableContent}
166
- id={tooltipId}
167
- >
143
+ <Tooltip content={tooltip} side={tooltipSide} align={tooltipAlign} delayDuration={tooltipDelayDuration} disableHoverableContent={tooltipDisableHoverableContent} id={tooltipId}>
168
144
  {iconButton}
169
145
  </Tooltip>
170
146
  );
@@ -63,7 +63,7 @@
63
63
  .rt-Image {
64
64
  border: 1px solid CanvasText;
65
65
  }
66
-
66
+
67
67
  /* Enhanced focus visibility in forced colors mode */
68
68
  .rt-Image:where(:focus-visible) {
69
69
  outline: 2px solid Highlight;
@@ -78,7 +78,9 @@
78
78
  */
79
79
  .rt-Image:where(:any-link, button, label) {
80
80
  cursor: pointer;
81
- transition: var(--transition-card), filter var(--motion-duration-small) var(--motion-ease-standard); /* Smooth transitions for interactive states */
81
+ transition:
82
+ var(--transition-card),
83
+ filter var(--motion-duration-small) var(--motion-ease-standard); /* Smooth transitions for interactive states */
82
84
 
83
85
  /*
84
86
  * Hover effects with progressive enhancement
@@ -87,7 +89,7 @@
87
89
  @media (hover: hover) {
88
90
  &:where(:hover) {
89
91
  box-shadow: var(--shadow-3); /* Subtle elevation on hover */
90
- filter: brightness(1.05) contrast(1.02); /* Slight brightness/contrast boost */
92
+ filter: brightness(1.08) contrast(1.02); /* Slight brightness/contrast boost */
91
93
  }
92
94
  }
93
95
 
@@ -116,13 +118,15 @@
116
118
  */
117
119
  :where(:any-link, button, label) {
118
120
  cursor: pointer;
119
- transition: var(--transition-card), filter var(--motion-duration-small) var(--motion-ease-standard); /* Smooth transitions for interactive states */
121
+ transition:
122
+ var(--transition-card),
123
+ filter var(--motion-duration-small) var(--motion-ease-standard); /* Smooth transitions for interactive states */
120
124
 
121
125
  /* Hover effects for wrapper elements */
122
126
  @media (hover: hover) {
123
127
  &:where(:hover) {
124
128
  /* box-shadow: var(--shadow-3); */
125
- filter: brightness(1.05) contrast(1.02);
129
+ filter: brightness(1.08) contrast(1.02);
126
130
  }
127
131
  }
128
132
 
@@ -141,8 +145,6 @@
141
145
  outline-offset: -2px;
142
146
  }
143
147
 
144
-
145
-
146
148
  /*
147
149
  * Object-fit variants for responsive image scaling
148
150
  * These classes control how images are resized to fit their containers
@@ -217,7 +219,7 @@
217
219
  color: var(--gray-11); /* Subtle but readable color */
218
220
  margin-top: var(--space-2); /* Consistent spacing from image */
219
221
  text-align: center; /* Centered alignment for visual balance */
220
-
222
+
221
223
  /*
222
224
  * Text wrapping and hyphenation for better layout
223
225
  * Prevents caption text from breaking the layout
@@ -45,6 +45,7 @@ export interface ShellContextValue {
45
45
 
46
46
  const ShellContext = React.createContext<ShellContextValue | null>(null);
47
47
 
48
+ /** @internal Shell root context. Prefer slice hooks (useSidebarMode, useShellActions, etc.). */
48
49
  export function useShell() {
49
50
  const ctx = React.useContext(ShellContext);
50
51
  if (!ctx) throw new Error('Shell components must be used within <Shell.Root>');
@@ -3,7 +3,7 @@ import type { Breakpoint, PresentationValue, ResponsivePresentation } from './sh
3
3
  import { _BREAKPOINTS } from './shell.types.js';
4
4
  import { useShell } from './shell.context.js';
5
5
 
6
- export function useResponsivePresentation(presentation: ResponsivePresentation): PresentationValue {
6
+ function useResponsivePresentation(presentation: ResponsivePresentation): PresentationValue {
7
7
  const { currentBreakpoint } = useShell();
8
8
 
9
9
  return React.useMemo(() => {
@@ -35,7 +35,7 @@ export function useResponsivePresentation(presentation: ResponsivePresentation):
35
35
  * If no value is defined for the current breakpoint, search smaller breakpoints down to 'initial'.
36
36
  * Returns undefined when passed a responsive map with no matching key across the chain.
37
37
  */
38
- export function useResponsiveValue<T>(value: T | Partial<Record<Breakpoint, T>> | undefined): T | undefined {
38
+ function useResponsiveValue<T>(value: T | Partial<Record<Breakpoint, T>> | undefined): T | undefined {
39
39
  const { currentBreakpoint } = useShell();
40
40
 
41
41
  return React.useMemo(() => {
@@ -64,3 +64,68 @@ export function useResponsiveValue<T>(value: T | Partial<Record<Breakpoint, T>>
64
64
  return undefined;
65
65
  }, [value, currentBreakpoint]);
66
66
  }
67
+
68
+ type ResponsiveStateValue<T> = T | Partial<Record<Breakpoint, T>>;
69
+
70
+ interface UseResponsiveInitialStateOptions<T> {
71
+ controlledValue?: ResponsiveStateValue<T>;
72
+ defaultValue?: ResponsiveStateValue<T>;
73
+ currentValue: T;
74
+ setValue: (value: T) => void;
75
+ breakpointReady: boolean;
76
+ onInit?: (value: T) => void;
77
+ onResponsiveChange?: (value: T) => void;
78
+ controlledIsResponsive?: boolean;
79
+ }
80
+
81
+ interface UseResponsiveInitialStateResult<T> {
82
+ resolvedControlled?: T;
83
+ resolvedDefault?: T;
84
+ }
85
+
86
+ function useResponsiveInitialState<T>({
87
+ controlledValue,
88
+ defaultValue,
89
+ currentValue,
90
+ setValue,
91
+ breakpointReady,
92
+ onInit,
93
+ onResponsiveChange,
94
+ controlledIsResponsive = false,
95
+ }: UseResponsiveInitialStateOptions<T>): UseResponsiveInitialStateResult<T> {
96
+ const resolvedControlled = useResponsiveValue(controlledValue);
97
+ const resolvedDefault = useResponsiveValue(defaultValue);
98
+
99
+ const lastControlledRef = React.useRef<T | undefined>(undefined);
100
+ React.useEffect(() => {
101
+ if (resolvedControlled === undefined) return;
102
+ lastControlledRef.current = resolvedControlled;
103
+ if (currentValue === resolvedControlled) {
104
+ if (controlledIsResponsive) {
105
+ onResponsiveChange?.(resolvedControlled);
106
+ }
107
+ return;
108
+ }
109
+ setValue(resolvedControlled);
110
+ if (controlledIsResponsive) {
111
+ onResponsiveChange?.(resolvedControlled);
112
+ }
113
+ }, [resolvedControlled, currentValue, setValue, onResponsiveChange, controlledIsResponsive]);
114
+
115
+ const didInitRef = React.useRef(false);
116
+ React.useEffect(() => {
117
+ if (didInitRef.current) return;
118
+ if (!breakpointReady) return;
119
+ if (typeof controlledValue !== 'undefined') return;
120
+ if (resolvedDefault === undefined) return;
121
+ didInitRef.current = true;
122
+ if (currentValue !== resolvedDefault) {
123
+ setValue(resolvedDefault);
124
+ }
125
+ onInit?.(resolvedDefault);
126
+ }, [breakpointReady, controlledValue, resolvedDefault, currentValue, setValue, onInit]);
127
+
128
+ return { resolvedControlled, resolvedDefault };
129
+ }
130
+
131
+ export { useResponsivePresentation, useResponsiveValue, useResponsiveInitialState };