@toolr/ui-design 0.1.7 → 0.1.8

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 (36) hide show
  1. package/components/hooks/use-modal-behavior.ts +32 -3
  2. package/components/sections/golden-snapshots/file-diff-viewer.tsx +1 -1
  3. package/components/sections/golden-snapshots/status-overview.tsx +1 -1
  4. package/components/ui/action-dialog.tsx +14 -6
  5. package/components/ui/ai-action-button.tsx +2 -4
  6. package/components/ui/badge.tsx +12 -4
  7. package/components/ui/breadcrumb.tsx +5 -5
  8. package/components/ui/checkbox.tsx +17 -11
  9. package/components/ui/collapsible-section.tsx +1 -0
  10. package/components/ui/confirm-badge.tsx +12 -4
  11. package/components/ui/cookie-consent.tsx +1 -1
  12. package/components/ui/extension-list-card.tsx +1 -1
  13. package/components/ui/file-tree.tsx +4 -4
  14. package/components/ui/filter-dropdown.tsx +5 -2
  15. package/components/ui/form-actions.tsx +7 -5
  16. package/components/ui/icon-button.tsx +5 -5
  17. package/components/ui/input.tsx +8 -3
  18. package/components/ui/label.tsx +4 -0
  19. package/components/ui/layout-tab-bar.tsx +5 -5
  20. package/components/ui/modal.tsx +9 -5
  21. package/components/ui/nav-card.tsx +1 -1
  22. package/components/ui/navigation-bar.tsx +4 -4
  23. package/components/ui/number-input.tsx +6 -0
  24. package/components/ui/segmented-toggle.tsx +2 -0
  25. package/components/ui/select.tsx +6 -3
  26. package/components/ui/selection-grid.tsx +4 -0
  27. package/components/ui/setting-row.tsx +4 -2
  28. package/components/ui/settings-card.tsx +2 -2
  29. package/components/ui/settings-info-box.tsx +1 -2
  30. package/components/ui/sort-dropdown.tsx +8 -5
  31. package/components/ui/tab-bar.tsx +14 -4
  32. package/components/ui/toggle.tsx +19 -11
  33. package/components/ui/tooltip.tsx +5 -5
  34. package/dist/index.d.ts +13 -7
  35. package/dist/index.js +258 -156
  36. package/package.json +9 -1
@@ -139,7 +139,7 @@ export function NavigationBar({
139
139
  active={historyOpen}
140
140
  />
141
141
  {historyOpen && hasHistoryEntries && (
142
- <div className="absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-800 border border-neutral-700 rounded-lg shadow-xl z-50">
142
+ <div className="absolute left-0 top-full mt-1 w-max min-w-[200px] max-w-[420px] bg-neutral-800 border border-neutral-700 rounded-lg shadow-lg z-50">
143
143
  <div className="px-3 py-1.5 border-b border-neutral-700/50">
144
144
  <p className="text-sm font-medium text-neutral-500">History</p>
145
145
  </div>
@@ -185,14 +185,14 @@ export function NavigationBar({
185
185
  const colors = segment.color && ACCENT_NAV[segment.color as AccentColor] ? ACCENT_NAV[segment.color as AccentColor] : null
186
186
 
187
187
  return (
188
- <div key={segment.id} className="flex items-center gap-1">
188
+ <div key={segment.id} className="flex items-center gap-1 min-w-0">
189
189
  {index > 0 && <SegmentSeparator type={separator} size={size} />}
190
190
  {isClickable ? (
191
191
  <button
192
192
  type="button"
193
193
  onClick={segment.onClick}
194
194
  className={cn(
195
- 'flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer',
195
+ 'flex items-center gap-1.5 px-2 py-0.5 rounded-md transition-colors cursor-pointer min-w-0',
196
196
  s.text,
197
197
  'font-medium hover:text-white',
198
198
  colors ? [colors.text, `hover:${colors.bg}`] : ['text-neutral-300', 'hover:bg-neutral-700/50'],
@@ -204,7 +204,7 @@ export function NavigationBar({
204
204
  ) : (
205
205
  <div
206
206
  className={cn(
207
- 'flex items-center gap-1.5 px-2 py-0.5 rounded-md',
207
+ 'flex items-center gap-1.5 px-2 py-0.5 rounded-md min-w-0',
208
208
  s.text,
209
209
  isLast
210
210
  ? ['font-medium bg-neutral-700/50', colors ? colors.text : 'text-white']
@@ -13,6 +13,8 @@ export interface NumberInputProps {
13
13
  size?: 'xss' | 'xs' | 'sm' | 'md' | 'lg'
14
14
  disabled?: boolean
15
15
  className?: string
16
+ /** Accessible label — required for screen readers */
17
+ 'aria-label'?: string
16
18
  }
17
19
 
18
20
  const SIZE_CONFIG = {
@@ -39,6 +41,7 @@ export function NumberInput({
39
41
  size = 'sm',
40
42
  disabled = false,
41
43
  className = '',
44
+ 'aria-label': ariaLabel,
42
45
  }: NumberInputProps) {
43
46
  const [focused, setFocused] = useState(false)
44
47
  const [editText, setEditText] = useState<string | null>(null)
@@ -92,6 +95,7 @@ export function NumberInput({
92
95
  ref={inputRef}
93
96
  type="text"
94
97
  inputMode="numeric"
98
+ aria-label={ariaLabel}
95
99
  value={editText ?? value}
96
100
  onChange={(e) => setEditText(e.target.value)}
97
101
  onFocus={() => {
@@ -128,6 +132,7 @@ export function NumberInput({
128
132
  >
129
133
  <button
130
134
  type="button"
135
+ aria-label="Increase value"
131
136
  tabIndex={-1}
132
137
  onMouseDown={(e) => e.preventDefault()}
133
138
  onClick={() => nudge(1)}
@@ -145,6 +150,7 @@ export function NumberInput({
145
150
  <div className={`border-t ${fc.border}`} />
146
151
  <button
147
152
  type="button"
153
+ aria-label="Decrease value"
148
154
  tabIndex={-1}
149
155
  onMouseDown={(e) => e.preventDefault()}
150
156
  onClick={() => nudge(-1)}
@@ -136,6 +136,8 @@ export function SegmentedToggle<T extends string>({
136
136
  return (
137
137
  <Tooltip key={option.value} content={option.tooltip} position={tooltipPosition}>
138
138
  <button
139
+ aria-pressed={isActive}
140
+ aria-label={option.label || (typeof option.tooltip.description === 'string' ? option.tooltip.description : undefined)}
139
141
  onClick={() => onChange(option.value)}
140
142
  disabled={disabled}
141
143
  className={`flex items-center justify-center ${sizeClass} ${rounding} font-medium transition-all cursor-pointer ${
@@ -110,6 +110,8 @@ export function Select<T extends string | number = string>({
110
110
  <button
111
111
  ref={buttonRef}
112
112
  type="button"
113
+ aria-expanded={isOpen}
114
+ aria-haspopup="listbox"
113
115
  onClick={() => !disabled && (isOpen ? close() : open())}
114
116
  disabled={disabled}
115
117
  className={`flex items-center gap-1.5 min-w-0 rounded-lg border ${v.bg} ${FORM_COLORS[color].border} text-neutral-200 focus:outline-none ${FORM_COLORS[color].focus} transition-colors ${
@@ -117,7 +119,7 @@ export function Select<T extends string | number = string>({
117
119
  } ${s}`}
118
120
  >
119
121
  {selectedOption?.icon}
120
- <span className={`whitespace-nowrap ${selectedOption ? '' : 'text-neutral-500'}`}>
122
+ <span className={`truncate ${selectedOption ? '' : 'text-neutral-500'}`}>
121
123
  {selectedOption?.label ?? placeholder}
122
124
  </span>
123
125
  <ChevronDown className={`w-3 h-3 ml-auto text-neutral-500 transition-transform shrink-0 ${isOpen ? 'rotate-180' : ''}`} />
@@ -125,7 +127,8 @@ export function Select<T extends string | number = string>({
125
127
  {isOpen && menuPos && createPortal(
126
128
  <div
127
129
  ref={menuRef}
128
- className={`fixed z-[9999] whitespace-nowrap ${v.menuBg} border ${FORM_COLORS[color].border} rounded-lg shadow-xl overflow-hidden`}
130
+ role="listbox"
131
+ className={`fixed z-[9999] whitespace-nowrap ${v.menuBg} border ${FORM_COLORS[color].border} rounded-lg shadow-lg overflow-hidden`}
129
132
  style={{
130
133
  top: menuPos.top,
131
134
  left: align === 'right' ? undefined : menuPos.left,
@@ -151,7 +154,7 @@ export function Select<T extends string | number = string>({
151
154
  >
152
155
  <Check className={`w-3 h-3 shrink-0 ${isSelected ? FORM_COLORS[color].accent : 'invisible'}`} />
153
156
  {opt.icon}
154
- <span>{opt.label}</span>
157
+ <span className="truncate">{opt.label}</span>
155
158
  </button>
156
159
  )
157
160
  })}
@@ -180,6 +180,8 @@ function GridCard({ item, selected, onClick }: CardProps) {
180
180
  return (
181
181
  <button
182
182
  type="button"
183
+ aria-pressed={selected}
184
+ aria-label={item.name}
183
185
  onClick={onClick}
184
186
  disabled={item.disabled}
185
187
  className={cn(
@@ -219,6 +221,8 @@ function ListCard({ item, selected, onClick }: CardProps) {
219
221
  return (
220
222
  <button
221
223
  type="button"
224
+ aria-pressed={selected}
225
+ aria-label={item.name}
222
226
  onClick={onClick}
223
227
  disabled={item.disabled}
224
228
  className={cn(
@@ -14,6 +14,7 @@
14
14
  import { Toggle, type ToggleColor, type ToggleSize } from './toggle.tsx'
15
15
  import { Select, type SelectOption } from './select.tsx'
16
16
  import { Input } from './input.tsx'
17
+ import { cn } from '../lib/cn.ts'
17
18
 
18
19
  interface SettingRowBase {
19
20
  label: string
@@ -51,10 +52,10 @@ interface SettingRowInput extends SettingRowBase {
51
52
  export type SettingRowProps = SettingRowToggle | SettingRowSelect | SettingRowInput
52
53
 
53
54
  export function SettingRow(props: SettingRowProps) {
54
- const { label, description, disabled, className = '' } = props
55
+ const { label, description, disabled, className } = props
55
56
 
56
57
  return (
57
- <div className={`flex items-start justify-between gap-4 ${className}`}>
58
+ <div className={cn('flex items-start justify-between gap-4', className)}>
58
59
  <div>
59
60
  <label className="text-neutral-200 leading-7">{label}</label>
60
61
  {description && <p className="text-md text-neutral-500">{description}</p>}
@@ -66,6 +67,7 @@ export function SettingRow(props: SettingRowProps) {
66
67
  disabled={disabled}
67
68
  color={props.color}
68
69
  size={props.size}
70
+ aria-label={label}
69
71
  />
70
72
  )}
71
73
  {props.type === 'select' && (
@@ -16,8 +16,8 @@ export function SettingsCard({ children, className, title, description, testId }
16
16
  >
17
17
  {title && (
18
18
  <div>
19
- <h3 className="text-md font-medium text-neutral-200">{title}</h3>
20
- {description && <p className="text-md text-neutral-500 mt-1">{description}</p>}
19
+ <h3 className="text-md font-medium text-neutral-200 truncate">{title}</h3>
20
+ {description && <p className="text-md text-neutral-500 mt-1 line-clamp-2">{description}</p>}
21
21
  </div>
22
22
  )}
23
23
  {!title && description && <p className="text-md text-neutral-500">{description}</p>}
@@ -52,8 +52,7 @@ export function SettingsInfoBox({ children, color = 'neutral', className, testId
52
52
 
53
53
  return (
54
54
  <div
55
- className={cn('flex items-start gap-3 border-l-2', borderColorMap[color], className)}
56
- style={{ paddingLeft: 10 }}
55
+ className={cn('flex items-start gap-3 border-l-2 pl-2.5', borderColorMap[color], className)}
57
56
  data-testid={testId}
58
57
  >
59
58
  <Icon className={cn('w-4 h-4 mt-0.5 shrink-0', ACCENT_TEXT[color])} />
@@ -73,22 +73,25 @@ export function SortDropdown({
73
73
  return (
74
74
  <div className="relative flex items-center" ref={ref} onKeyDown={handleKeyDown}>
75
75
  <button
76
+ aria-expanded={isOpen}
77
+ aria-haspopup="listbox"
76
78
  onClick={() => setIsOpen(!isOpen)}
77
79
  className={`flex items-center gap-1.5 h-7 px-2 rounded-md border ${v.bg} text-sm transition-colors cursor-pointer ${FORM_COLORS[color].border} text-neutral-200 ${FORM_COLORS[color].hover}`}
78
80
  >
79
- <span
80
- className={`${FORM_COLORS[color].accent} hover:brightness-125 transition-colors`}
81
+ <button
82
+ type="button"
83
+ aria-label={ascending ? 'Sort descending' : 'Sort ascending'}
84
+ className={`${FORM_COLORS[color].accent} hover:brightness-125 transition-colors cursor-pointer`}
81
85
  onClick={(e) => { e.stopPropagation(); onToggleDirection() }}
82
- role="button"
83
86
  >
84
87
  <DirIcon className="w-3 h-3" />
85
- </span>
88
+ </button>
86
89
  <span className="whitespace-nowrap">{current.label}</span>
87
90
  <ChevronDown className={`w-3 h-3 transition-transform ${isOpen ? 'rotate-180' : ''}`} />
88
91
  </button>
89
92
 
90
93
  {isOpen && (
91
- <div ref={menuRef} className={`absolute right-0 top-full z-50 mt-1 min-w-[140px] bg-[var(--popover)] border ${FORM_COLORS[color].border} rounded-lg shadow-xl overflow-hidden`}>
94
+ <div ref={menuRef} role="listbox" className={`absolute right-0 top-full z-50 mt-1 min-w-[140px] bg-[var(--popover)] border ${FORM_COLORS[color].border} rounded-lg shadow-lg overflow-hidden`}>
92
95
  {fields.map((f, idx) => (
93
96
  <button
94
97
  key={f.value}
@@ -88,11 +88,12 @@ function TabBadge({ badge, size, badgeColor }: { badge: number | string; size: '
88
88
  return <Badge value={badge} color={badgeColor} size={s.badgeSize} className="flex-shrink-0" />
89
89
  }
90
90
 
91
- function CloseButton({ size, onClick }: { size: 'xss' | 'xs' | 'sm' | 'md' | 'lg'; onClick: () => void }) {
91
+ function CloseButton({ size, onClick, tabLabel }: { size: 'xss' | 'xs' | 'sm' | 'md' | 'lg'; onClick: () => void; tabLabel: string }) {
92
92
  const s = sizeConfig[size]
93
93
  return (
94
94
  <button
95
95
  type="button"
96
+ aria-label={`Close ${tabLabel}`}
96
97
  onClick={(e) => {
97
98
  e.stopPropagation()
98
99
  onClick()
@@ -123,6 +124,8 @@ function CompactTab({
123
124
  return (
124
125
  <button
125
126
  type="button"
127
+ role="tab"
128
+ aria-selected={isActive}
126
129
  onClick={onSelect}
127
130
  className={cn(
128
131
  'relative flex items-center justify-center transition-colors cursor-pointer',
@@ -160,6 +163,8 @@ function UnderlineTab({
160
163
  return (
161
164
  <button
162
165
  type="button"
166
+ role="tab"
167
+ aria-selected={isActive}
163
168
  onClick={onSelect}
164
169
  className={cn(
165
170
  'group relative flex items-center whitespace-nowrap transition-colors cursor-pointer',
@@ -171,7 +176,7 @@ function UnderlineTab({
171
176
  {tab.icon && <TabIcon icon={tab.icon} size={size} color={isActive ? tab.color : undefined} />}
172
177
  <span>{tab.label}</span>
173
178
  {tab.badge !== undefined && <TabBadge badge={tab.badge} size={size} badgeColor={tab.badgeColor} />}
174
- {showClose && <CloseButton size={size} onClick={onClose!} />}
179
+ {showClose && <CloseButton size={size} onClick={onClose!} tabLabel={tab.label} />}
175
180
  {isActive && (
176
181
  <span className={cn('absolute bottom-0 left-0 right-0 h-0.5 rounded-full', c.indicator)} />
177
182
  )}
@@ -189,6 +194,8 @@ function PillTab({
189
194
  return (
190
195
  <button
191
196
  type="button"
197
+ role="tab"
198
+ aria-selected={isActive}
192
199
  onClick={onSelect}
193
200
  className={cn(
194
201
  'group flex items-center whitespace-nowrap rounded-md transition-colors cursor-pointer',
@@ -202,7 +209,7 @@ function PillTab({
202
209
  {tab.icon && <TabIcon icon={tab.icon} size={size} color={isActive ? tab.color : undefined} />}
203
210
  <span>{tab.label}</span>
204
211
  {tab.badge !== undefined && <TabBadge badge={tab.badge} size={size} badgeColor={tab.badgeColor} />}
205
- {showClose && <CloseButton size={size} onClick={onClose!} />}
212
+ {showClose && <CloseButton size={size} onClick={onClose!} tabLabel={tab.label} />}
206
213
  </button>
207
214
  )
208
215
  }
@@ -217,6 +224,8 @@ function CardTab({
217
224
  return (
218
225
  <button
219
226
  type="button"
227
+ role="tab"
228
+ aria-selected={isActive}
220
229
  onClick={onSelect}
221
230
  className={cn(
222
231
  'group relative flex items-center whitespace-nowrap transition-colors cursor-pointer rounded-t-lg border border-b-0',
@@ -230,7 +239,7 @@ function CardTab({
230
239
  {tab.icon && <TabIcon icon={tab.icon} size={size} color={isActive ? tab.color : undefined} />}
231
240
  <span>{tab.label}</span>
232
241
  {tab.badge !== undefined && <TabBadge badge={tab.badge} size={size} badgeColor={tab.badgeColor} />}
233
- {showClose && <CloseButton size={size} onClick={onClose!} />}
242
+ {showClose && <CloseButton size={size} onClick={onClose!} tabLabel={tab.label} />}
234
243
  {isActive && (
235
244
  <span className="absolute -bottom-px left-0 right-0 h-px bg-neutral-800" />
236
245
  )}
@@ -277,6 +286,7 @@ export function TabBar({
277
286
  return (
278
287
  <div
279
288
  ref={containerRef}
289
+ role="tablist"
280
290
  className={cn(
281
291
  'flex items-end',
282
292
  variant === 'underline' && 'border-b border-neutral-700',
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { type AccentColor } from '../lib/form-colors.ts'
11
+ import { cn } from '../lib/cn.ts'
11
12
 
12
13
  export type ToggleColor = AccentColor
13
14
 
@@ -84,6 +85,8 @@ export interface ToggleProps {
84
85
  size?: ToggleSize
85
86
  className?: string
86
87
  color?: ToggleColor
88
+ /** Accessible label — required for screen readers */
89
+ 'aria-label'?: string
87
90
  /** Test ID for E2E testing */
88
91
  testId?: string
89
92
  }
@@ -93,8 +96,9 @@ export function Toggle({
93
96
  onChange,
94
97
  disabled = false,
95
98
  size = 'sm',
96
- className = '',
99
+ className,
97
100
  color = 'blue',
101
+ 'aria-label': ariaLabel,
98
102
  testId,
99
103
  }: ToggleProps) {
100
104
  const s = TOGGLE_SIZES[size]
@@ -103,23 +107,27 @@ export function Toggle({
103
107
  return (
104
108
  <button
105
109
  type="button"
110
+ role="switch"
111
+ aria-checked={checked}
112
+ aria-label={ariaLabel}
106
113
  onClick={() => !disabled && onChange(!checked)}
107
114
  disabled={disabled}
108
115
  data-testid={testId}
109
116
  style={{ boxShadow: `inset 0 0 0 1px ${checked ? bc.active : bc.idle}` }}
110
- className={`
111
- relative ${s.track} rounded-full transition-all flex-shrink-0
112
- cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed
113
- ${checked ? TOGGLE_CHECKED_TRACK[color] : TOGGLE_UNCHECKED_TRACK[color]}
114
- ${className}
115
- `}
117
+ className={cn(
118
+ 'relative rounded-full transition-all flex-shrink-0 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed',
119
+ s.track,
120
+ checked ? TOGGLE_CHECKED_TRACK[color] : TOGGLE_UNCHECKED_TRACK[color],
121
+ className,
122
+ )}
116
123
  >
117
124
  <span
118
125
  style={{ backgroundColor: checked ? kc.on : kc.off }}
119
- className={`
120
- block absolute top-0.5 left-0.5 ${s.knob} rounded-full transition-transform
121
- ${checked ? s.translate : 'translate-x-0'}
122
- `}
126
+ className={cn(
127
+ 'block absolute top-0.5 left-0.5 rounded-full transition-transform',
128
+ s.knob,
129
+ checked ? s.translate : 'translate-x-0',
130
+ )}
123
131
  />
124
132
  </button>
125
133
  )
@@ -209,19 +209,18 @@ export function Tooltip({
209
209
  if (!triggerRef.current) return
210
210
  const triggerRect = triggerRef.current.getBoundingClientRect()
211
211
  const tooltipEl = tooltipRef.current
212
+ const tooltipRect = tooltipEl?.getBoundingClientRect()
212
213
 
213
214
  let resolvedPosition: ResolvedPosition = position === 'auto' ? 'top' : position
214
215
 
215
- if (position === 'auto' && tooltipEl) {
216
- const tooltipRect = tooltipEl.getBoundingClientRect()
216
+ if (position === 'auto' && tooltipRect) {
217
217
  resolvedPosition = resolveAutoPosition(triggerRect, tooltipRect)
218
218
  setActualPosition(resolvedPosition)
219
219
  }
220
220
 
221
221
  let newCoords = calculateBasePosition(triggerRect, resolvedPosition, align)
222
222
 
223
- if (tooltipEl) {
224
- const tooltipRect = tooltipEl.getBoundingClientRect()
223
+ if (tooltipRect) {
225
224
  newCoords = adjustForTooltipSize(newCoords, tooltipRect, resolvedPosition, align)
226
225
  newCoords = clampToViewport(newCoords, tooltipRect)
227
226
  }
@@ -248,7 +247,8 @@ export function Tooltip({
248
247
  const tooltipContent = (
249
248
  <div
250
249
  ref={tooltipRef}
251
- className={`fixed px-3 py-1.5 bg-[var(--popover)] border border-neutral-600 rounded-lg shadow-xl z-[9999] ${interactive || trigger === 'click' ? '' : 'pointer-events-none'} ${multiline ? 'whitespace-pre-line' : 'whitespace-nowrap'}`}
250
+ role="tooltip"
251
+ className={`fixed px-3 py-1.5 bg-[var(--popover)] border border-neutral-600 rounded-lg shadow-lg z-[9999] ${interactive || trigger === 'click' ? '' : 'pointer-events-none'} ${multiline ? 'whitespace-pre-line' : 'whitespace-nowrap'}`}
252
252
  style={{
253
253
  top: coords.top,
254
254
  left: coords.left,
package/dist/index.d.ts CHANGED
@@ -306,7 +306,7 @@ interface BadgeProps {
306
306
  className?: string;
307
307
  testId?: string;
308
308
  }
309
- declare function Badge({ value, color, size, className, testId, }: BadgeProps): react_jsx_runtime.JSX.Element;
309
+ declare const Badge: react.NamedExoticComponent<BadgeProps>;
310
310
 
311
311
  type ConfirmBadgeColor = AccentColor;
312
312
  interface ConfirmBadgeProps {
@@ -315,7 +315,7 @@ interface ConfirmBadgeProps {
315
315
  className?: string;
316
316
  testId?: string;
317
317
  }
318
- declare function ConfirmBadge({ color, size, className, testId, }: ConfirmBadgeProps): react_jsx_runtime.JSX.Element;
318
+ declare const ConfirmBadge: react.NamedExoticComponent<ConfirmBadgeProps>;
319
319
 
320
320
  type CheckboxSize = 'xss' | 'xs' | 'sm' | 'md' | 'lg';
321
321
  type CheckboxColor = AccentColor;
@@ -328,10 +328,12 @@ interface CheckboxProps {
328
328
  color?: CheckboxColor;
329
329
  variant?: CheckboxVariant;
330
330
  className?: string;
331
+ /** Accessible label — required for screen readers */
332
+ 'aria-label'?: string;
331
333
  /** Test ID for E2E testing */
332
334
  testId?: string;
333
335
  }
334
- declare function Checkbox({ checked, onChange, disabled, size, color, variant, className, testId, }: CheckboxProps): react_jsx_runtime.JSX.Element;
336
+ declare function Checkbox({ checked, onChange, disabled, size, color, variant, className, 'aria-label': ariaLabel, testId, }: CheckboxProps): react_jsx_runtime.JSX.Element;
335
337
 
336
338
  type ToggleColor = AccentColor;
337
339
  type ToggleSize = 'xss' | 'xs' | 'sm' | 'md' | 'lg';
@@ -342,10 +344,12 @@ interface ToggleProps {
342
344
  size?: ToggleSize;
343
345
  className?: string;
344
346
  color?: ToggleColor;
347
+ /** Accessible label — required for screen readers */
348
+ 'aria-label'?: string;
345
349
  /** Test ID for E2E testing */
346
350
  testId?: string;
347
351
  }
348
- declare function Toggle({ checked, onChange, disabled, size, className, color, testId, }: ToggleProps): react_jsx_runtime.JSX.Element;
352
+ declare function Toggle({ checked, onChange, disabled, size, className, color, 'aria-label': ariaLabel, testId, }: ToggleProps): react_jsx_runtime.JSX.Element;
349
353
 
350
354
  interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size' | 'type'> {
351
355
  value: string;
@@ -375,8 +379,10 @@ interface NumberInputProps {
375
379
  size?: 'xss' | 'xs' | 'sm' | 'md' | 'lg';
376
380
  disabled?: boolean;
377
381
  className?: string;
382
+ /** Accessible label — required for screen readers */
383
+ 'aria-label'?: string;
378
384
  }
379
- declare function NumberInput({ value, onChange, min, max, step, variant, color, size, disabled, className, }: NumberInputProps): react_jsx_runtime.JSX.Element;
385
+ declare function NumberInput({ value, onChange, min, max, step, variant, color, size, disabled, className, 'aria-label': ariaLabel, }: NumberInputProps): react_jsx_runtime.JSX.Element;
380
386
 
381
387
  type ScopeType = 'user' | 'project' | 'local' | 'read-only';
382
388
  interface ScopeBadgeProps {
@@ -2626,9 +2632,9 @@ declare function useClickOutside(ref: RefObject<HTMLElement | null> | RefObject<
2626
2632
  declare function useDropdownMaxHeight<T extends HTMLElement>(isOpen: boolean, margin?: number): react.RefObject<T | null>;
2627
2633
 
2628
2634
  /**
2629
- * Shared modal behavior: Escape key to close + body overflow lock.
2635
+ * Shared modal behavior: Escape key to close + body overflow lock + focus trap.
2630
2636
  */
2631
- declare function useModalBehavior(isOpen: boolean, onClose: () => void): void;
2637
+ declare function useModalBehavior(isOpen: boolean, onClose: () => void, containerRef?: RefObject<HTMLElement | null>): void;
2632
2638
 
2633
2639
  /** Hook for managing back/forward navigation history with a breadcrumb segment stack. */
2634
2640