@kushagradhawan/kookie-ui 0.1.41 → 0.1.43

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 (144) hide show
  1. package/README.md +257 -60
  2. package/components.css +398 -91
  3. package/dist/cjs/components/schemas/base-button.schema.d.ts +319 -0
  4. package/dist/cjs/components/schemas/base-button.schema.d.ts.map +1 -0
  5. package/dist/cjs/components/schemas/base-button.schema.js +2 -0
  6. package/dist/cjs/components/schemas/base-button.schema.js.map +7 -0
  7. package/dist/cjs/components/schemas/button.schema.d.ts +686 -0
  8. package/dist/cjs/components/schemas/button.schema.d.ts.map +1 -0
  9. package/dist/cjs/components/schemas/button.schema.js +2 -0
  10. package/dist/cjs/components/schemas/button.schema.js.map +7 -0
  11. package/dist/cjs/components/schemas/icon-button.schema.d.ts +329 -0
  12. package/dist/cjs/components/schemas/icon-button.schema.d.ts.map +1 -0
  13. package/dist/cjs/components/schemas/icon-button.schema.js +2 -0
  14. package/dist/cjs/components/schemas/icon-button.schema.js.map +7 -0
  15. package/dist/cjs/components/schemas/index.d.ts +52 -0
  16. package/dist/cjs/components/schemas/index.d.ts.map +1 -0
  17. package/dist/cjs/components/schemas/index.js +2 -0
  18. package/dist/cjs/components/schemas/index.js.map +7 -0
  19. package/dist/cjs/components/schemas/toggle-button.schema.d.ts +1172 -0
  20. package/dist/cjs/components/schemas/toggle-button.schema.d.ts.map +1 -0
  21. package/dist/cjs/components/schemas/toggle-button.schema.js +2 -0
  22. package/dist/cjs/components/schemas/toggle-button.schema.js.map +7 -0
  23. package/dist/cjs/components/schemas/toggle-icon-button.schema.d.ts +563 -0
  24. package/dist/cjs/components/schemas/toggle-icon-button.schema.d.ts.map +1 -0
  25. package/dist/cjs/components/schemas/toggle-icon-button.schema.js +2 -0
  26. package/dist/cjs/components/schemas/toggle-icon-button.schema.js.map +7 -0
  27. package/dist/cjs/components/sheet.d.ts +1 -1
  28. package/dist/cjs/components/sheet.d.ts.map +1 -1
  29. package/dist/cjs/components/sheet.js +1 -1
  30. package/dist/cjs/components/sheet.js.map +3 -3
  31. package/dist/cjs/components/shell.d.ts +125 -164
  32. package/dist/cjs/components/shell.d.ts.map +1 -1
  33. package/dist/cjs/components/shell.js +1 -1
  34. package/dist/cjs/components/shell.js.map +3 -3
  35. package/dist/cjs/components/sidebar.d.ts +1 -7
  36. package/dist/cjs/components/sidebar.d.ts.map +1 -1
  37. package/dist/cjs/components/sidebar.js +1 -1
  38. package/dist/cjs/components/sidebar.js.map +3 -3
  39. package/dist/cjs/components/theme.d.ts +3 -0
  40. package/dist/cjs/components/theme.d.ts.map +1 -1
  41. package/dist/cjs/components/theme.js +1 -1
  42. package/dist/cjs/components/theme.js.map +3 -3
  43. package/dist/cjs/components/theme.props.d.ts +10 -0
  44. package/dist/cjs/components/theme.props.d.ts.map +1 -1
  45. package/dist/cjs/components/theme.props.js +1 -1
  46. package/dist/cjs/components/theme.props.js.map +3 -3
  47. package/dist/cjs/helpers/font-config.d.ts +96 -0
  48. package/dist/cjs/helpers/font-config.d.ts.map +1 -0
  49. package/dist/cjs/helpers/font-config.js +3 -0
  50. package/dist/cjs/helpers/font-config.js.map +7 -0
  51. package/dist/cjs/helpers/index.d.ts +1 -0
  52. package/dist/cjs/helpers/index.d.ts.map +1 -1
  53. package/dist/cjs/helpers/index.js +1 -1
  54. package/dist/cjs/helpers/index.js.map +2 -2
  55. package/dist/esm/components/schemas/base-button.schema.d.ts +319 -0
  56. package/dist/esm/components/schemas/base-button.schema.d.ts.map +1 -0
  57. package/dist/esm/components/schemas/base-button.schema.js +2 -0
  58. package/dist/esm/components/schemas/base-button.schema.js.map +7 -0
  59. package/dist/esm/components/schemas/button.schema.d.ts +686 -0
  60. package/dist/esm/components/schemas/button.schema.d.ts.map +1 -0
  61. package/dist/esm/components/schemas/button.schema.js +2 -0
  62. package/dist/esm/components/schemas/button.schema.js.map +7 -0
  63. package/dist/esm/components/schemas/icon-button.schema.d.ts +329 -0
  64. package/dist/esm/components/schemas/icon-button.schema.d.ts.map +1 -0
  65. package/dist/esm/components/schemas/icon-button.schema.js +2 -0
  66. package/dist/esm/components/schemas/icon-button.schema.js.map +7 -0
  67. package/dist/esm/components/schemas/index.d.ts +52 -0
  68. package/dist/esm/components/schemas/index.d.ts.map +1 -0
  69. package/dist/esm/components/schemas/index.js +2 -0
  70. package/dist/esm/components/schemas/index.js.map +7 -0
  71. package/dist/esm/components/schemas/toggle-button.schema.d.ts +1172 -0
  72. package/dist/esm/components/schemas/toggle-button.schema.d.ts.map +1 -0
  73. package/dist/esm/components/schemas/toggle-button.schema.js +2 -0
  74. package/dist/esm/components/schemas/toggle-button.schema.js.map +7 -0
  75. package/dist/esm/components/schemas/toggle-icon-button.schema.d.ts +563 -0
  76. package/dist/esm/components/schemas/toggle-icon-button.schema.d.ts.map +1 -0
  77. package/dist/esm/components/schemas/toggle-icon-button.schema.js +2 -0
  78. package/dist/esm/components/schemas/toggle-icon-button.schema.js.map +7 -0
  79. package/dist/esm/components/sheet.d.ts +1 -1
  80. package/dist/esm/components/sheet.d.ts.map +1 -1
  81. package/dist/esm/components/sheet.js +1 -1
  82. package/dist/esm/components/sheet.js.map +3 -3
  83. package/dist/esm/components/shell.d.ts +125 -164
  84. package/dist/esm/components/shell.d.ts.map +1 -1
  85. package/dist/esm/components/shell.js +1 -1
  86. package/dist/esm/components/shell.js.map +3 -3
  87. package/dist/esm/components/sidebar.d.ts +1 -7
  88. package/dist/esm/components/sidebar.d.ts.map +1 -1
  89. package/dist/esm/components/sidebar.js +1 -1
  90. package/dist/esm/components/sidebar.js.map +3 -3
  91. package/dist/esm/components/theme.d.ts +3 -0
  92. package/dist/esm/components/theme.d.ts.map +1 -1
  93. package/dist/esm/components/theme.js +1 -1
  94. package/dist/esm/components/theme.js.map +3 -3
  95. package/dist/esm/components/theme.props.d.ts +10 -0
  96. package/dist/esm/components/theme.props.d.ts.map +1 -1
  97. package/dist/esm/components/theme.props.js +1 -1
  98. package/dist/esm/components/theme.props.js.map +3 -3
  99. package/dist/esm/helpers/font-config.d.ts +96 -0
  100. package/dist/esm/helpers/font-config.d.ts.map +1 -0
  101. package/dist/esm/helpers/font-config.js +3 -0
  102. package/dist/esm/helpers/font-config.js.map +7 -0
  103. package/dist/esm/helpers/index.d.ts +1 -0
  104. package/dist/esm/helpers/index.d.ts.map +1 -1
  105. package/dist/esm/helpers/index.js +1 -1
  106. package/dist/esm/helpers/index.js.map +2 -2
  107. package/package.json +23 -3
  108. package/schemas/base-button.d.ts +2 -0
  109. package/schemas/base-button.json +284 -0
  110. package/schemas/button.d.ts +2 -0
  111. package/schemas/button.json +535 -0
  112. package/schemas/icon-button.d.ts +2 -0
  113. package/schemas/icon-button.json +318 -0
  114. package/schemas/index.d.ts +2 -0
  115. package/schemas/index.json +2016 -0
  116. package/schemas/schemas.d.ts +29 -0
  117. package/schemas/toggle-button.d.ts +2 -0
  118. package/schemas/toggle-button.json +543 -0
  119. package/schemas/toggle-icon-button.d.ts +2 -0
  120. package/schemas/toggle-icon-button.json +326 -0
  121. package/schemas-json.d.ts +12 -0
  122. package/src/components/_internal/base-sidebar-menu.css +3 -8
  123. package/src/components/_internal/base-sidebar.css +1 -2
  124. package/src/components/schemas/base-button.schema.ts +339 -0
  125. package/src/components/schemas/button.schema.ts +198 -0
  126. package/src/components/schemas/icon-button.schema.ts +142 -0
  127. package/src/components/schemas/index.ts +68 -0
  128. package/src/components/schemas/toggle-button.schema.ts +122 -0
  129. package/src/components/schemas/toggle-icon-button.schema.ts +195 -0
  130. package/src/components/sheet.css +39 -19
  131. package/src/components/sheet.tsx +62 -3
  132. package/src/components/shell.css +510 -89
  133. package/src/components/shell.tsx +2055 -928
  134. package/src/components/sidebar.css +126 -65
  135. package/src/components/sidebar.tsx +5 -24
  136. package/src/components/theme.props.tsx +8 -0
  137. package/src/components/theme.tsx +16 -0
  138. package/src/helpers/font-config.ts +167 -0
  139. package/src/helpers/index.ts +1 -0
  140. package/src/styles/fonts.css +16 -13
  141. package/src/styles/tokens/typography.css +27 -4
  142. package/styles.css +410 -91
  143. package/tokens/base.css +12 -0
  144. package/tokens.css +12 -0
@@ -0,0 +1,339 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * BaseButton Zod schema - Single source of truth for all button component props
5
+ *
6
+ * This schema defines the core button functionality used by Button, IconButton,
7
+ * ToggleButton, and ToggleIconButton components. It ensures consistency across
8
+ * the design system and provides machine-readable validation.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * // Basic validation
13
+ * const props = BaseButtonSchema.parse({ size: '2', variant: 'solid' });
14
+ *
15
+ * // With responsive props
16
+ * const responsiveProps = BaseButtonSchema.parse({
17
+ * size: { initial: '1', sm: '2', md: '3' },
18
+ * variant: 'solid'
19
+ * });
20
+ * ```
21
+ */
22
+ export const BaseButtonSchema = z
23
+ .object({
24
+ /**
25
+ * Polymorphic rendering - render as different HTML elements
26
+ * @default 'button'
27
+ */
28
+ as: z.string().optional().describe('HTML element type to render as'),
29
+
30
+ /**
31
+ * Use asChild to merge props with child element
32
+ * @default false
33
+ */
34
+ asChild: z
35
+ .boolean()
36
+ .optional()
37
+ .describe('Merge props with child element instead of rendering wrapper'),
38
+
39
+ /**
40
+ * Button size for responsive design and interface density
41
+ * Supports responsive objects: { initial: '1', sm: '2', md: '3', lg: '4' }
42
+ * @default '2'
43
+ */
44
+ size: z
45
+ .union([
46
+ z.enum(['1', '2', '3', '4']),
47
+ z.object({
48
+ initial: z.enum(['1', '2', '3', '4']).optional(),
49
+ sm: z.enum(['1', '2', '3', '4']).optional(),
50
+ md: z.enum(['1', '2', '3', '4']).optional(),
51
+ lg: z.enum(['1', '2', '3', '4']).optional(),
52
+ xl: z.enum(['1', '2', '3', '4']).optional(),
53
+ }),
54
+ ])
55
+ .default('2')
56
+ .describe('Button size for responsive design and interface density'),
57
+
58
+ /**
59
+ * Visual variant that determines the button's appearance and context
60
+ * @default 'solid'
61
+ */
62
+ variant: z
63
+ .enum(['classic', 'solid', 'soft', 'surface', 'outline', 'ghost', 'override'])
64
+ .default('solid')
65
+ .describe("Visual variant that determines the button's appearance and context"),
66
+
67
+ /**
68
+ * Accent color for the button
69
+ */
70
+ color: z
71
+ .enum([
72
+ 'tomato',
73
+ 'red',
74
+ 'ruby',
75
+ 'crimson',
76
+ 'pink',
77
+ 'plum',
78
+ 'purple',
79
+ 'violet',
80
+ 'iris',
81
+ 'indigo',
82
+ 'blue',
83
+ 'cyan',
84
+ 'teal',
85
+ 'jade',
86
+ 'green',
87
+ 'grass',
88
+ 'brown',
89
+ 'orange',
90
+ 'sky',
91
+ 'mint',
92
+ 'lime',
93
+ 'yellow',
94
+ 'amber',
95
+ 'gold',
96
+ 'bronze',
97
+ 'gray',
98
+ ])
99
+ .optional()
100
+ .describe('Accent color for the button'),
101
+
102
+ /**
103
+ * High contrast mode for better accessibility
104
+ * @default false
105
+ */
106
+ highContrast: z
107
+ .boolean()
108
+ .optional()
109
+ .default(false)
110
+ .describe('High contrast mode for better accessibility'),
111
+
112
+ /**
113
+ * Border radius for the button
114
+ */
115
+ radius: z
116
+ .enum(['none', 'small', 'medium', 'large', 'full'])
117
+ .optional()
118
+ .describe('Border radius for the button'),
119
+
120
+ /**
121
+ * Loading state that shows a spinner and disables interaction
122
+ * Automatically sets disabled=true and provides accessibility announcements
123
+ * @default false
124
+ */
125
+ loading: z
126
+ .boolean()
127
+ .optional()
128
+ .default(false)
129
+ .describe('Loading state that shows a spinner and disables interaction'),
130
+
131
+ /**
132
+ * Full width mode that expands the button to fill its container
133
+ * Useful for mobile layouts and form submissions
134
+ * @default false
135
+ */
136
+ fullWidth: z
137
+ .boolean()
138
+ .optional()
139
+ .default(false)
140
+ .describe('Full width mode that expands the button to fill its container'),
141
+
142
+ /**
143
+ * Material type for visual rendering and depth effects
144
+ * Controls how the button renders its visual elements
145
+ */
146
+ material: z
147
+ .enum(['solid', 'translucent'])
148
+ .optional()
149
+ .describe('Material type for visual rendering and depth effects'),
150
+
151
+ /**
152
+ * Panel background type (deprecated)
153
+ * @deprecated Use `material` prop instead. This prop will be removed in a future version.
154
+ */
155
+ panelBackground: z
156
+ .enum(['solid', 'translucent'])
157
+ .optional()
158
+ .describe('Panel background type (deprecated - use material instead)'),
159
+
160
+ /**
161
+ * Flush mode that removes visual padding for seamless text integration
162
+ * Only effective with ghost variant
163
+ * @default false
164
+ */
165
+ flush: z
166
+ .boolean()
167
+ .optional()
168
+ .default(false)
169
+ .describe('Flush mode that removes visual padding for seamless text integration'),
170
+
171
+ /**
172
+ * Disabled state
173
+ * @default false
174
+ */
175
+ disabled: z.boolean().optional().default(false).describe('Disabled state'),
176
+
177
+ /**
178
+ * Type attribute for form buttons
179
+ * @default 'button'
180
+ */
181
+ type: z
182
+ .enum(['button', 'submit', 'reset'])
183
+ .optional()
184
+ .describe('Type attribute for form buttons'),
185
+
186
+ /**
187
+ * Additional CSS class name
188
+ */
189
+ className: z.string().optional().describe('Additional CSS class name'),
190
+
191
+ /**
192
+ * Inline styles
193
+ */
194
+ style: z
195
+ .record(z.string(), z.union([z.string(), z.number()]))
196
+ .optional()
197
+ .describe('Inline styles'),
198
+
199
+ /**
200
+ * Click handler
201
+ */
202
+ onClick: z.function().optional().describe('Click handler'),
203
+
204
+ /**
205
+ * Focus handler
206
+ */
207
+ onFocus: z.function().optional().describe('Focus handler'),
208
+
209
+ /**
210
+ * Blur handler
211
+ */
212
+ onBlur: z.function().optional().describe('Blur handler'),
213
+
214
+ /**
215
+ * Mouse enter handler
216
+ */
217
+ onMouseEnter: z.function().optional().describe('Mouse enter handler'),
218
+
219
+ /**
220
+ * Mouse leave handler
221
+ */
222
+ onMouseLeave: z.function().optional().describe('Mouse leave handler'),
223
+
224
+ /**
225
+ * Key down handler
226
+ */
227
+ onKeyDown: z.function().optional().describe('Key down handler'),
228
+
229
+ /**
230
+ * Key up handler
231
+ */
232
+ onKeyUp: z.function().optional().describe('Key up handler'),
233
+
234
+ /**
235
+ * Form submission handler
236
+ */
237
+ onSubmit: z.function().optional().describe('Form submission handler'),
238
+
239
+ /**
240
+ * Tab index for keyboard navigation
241
+ */
242
+ tabIndex: z.number().optional().describe('Tab index for keyboard navigation'),
243
+
244
+ /**
245
+ * ARIA label for accessibility
246
+ */
247
+ 'aria-label': z.string().optional().describe('ARIA label for accessibility'),
248
+
249
+ /**
250
+ * ARIA labelled by reference
251
+ */
252
+ 'aria-labelledby': z.string().optional().describe('ARIA labelled by reference'),
253
+
254
+ /**
255
+ * ARIA described by reference
256
+ */
257
+ 'aria-describedby': z.string().optional().describe('ARIA described by reference'),
258
+
259
+ /**
260
+ * ARIA expanded state
261
+ */
262
+ 'aria-expanded': z.boolean().optional().describe('ARIA expanded state'),
263
+
264
+ /**
265
+ * ARIA pressed state
266
+ */
267
+ 'aria-pressed': z.boolean().optional().describe('ARIA pressed state'),
268
+
269
+ /**
270
+ * ARIA current state
271
+ */
272
+ 'aria-current': z
273
+ .union([z.boolean(), z.enum(['page', 'step', 'location', 'date', 'time'])])
274
+ .optional()
275
+ .describe('ARIA current state'),
276
+
277
+ /**
278
+ * ARIA controls reference
279
+ */
280
+ 'aria-controls': z.string().optional().describe('ARIA controls reference'),
281
+
282
+ /**
283
+ * ARIA owns reference
284
+ */
285
+ 'aria-owns': z.string().optional().describe('ARIA owns reference'),
286
+
287
+ /**
288
+ * Data attributes
289
+ */
290
+ 'data-*': z.record(z.string(), z.string()).optional().describe('Data attributes'),
291
+
292
+ /**
293
+ * ID attribute
294
+ */
295
+ id: z.string().optional().describe('ID attribute'),
296
+
297
+ /**
298
+ * Title attribute for tooltip
299
+ */
300
+ title: z.string().optional().describe('Title attribute for tooltip'),
301
+
302
+ /**
303
+ * Role attribute
304
+ */
305
+ role: z.string().optional().describe('Role attribute'),
306
+
307
+ /**
308
+ * Children elements
309
+ */
310
+ children: z.any().optional().describe('Children elements'),
311
+ })
312
+ .strict();
313
+
314
+ /**
315
+ * Type derived from BaseButton Zod schema
316
+ * This ensures type safety and consistency with the schema
317
+ */
318
+ export type BaseButtonProps = z.infer<typeof BaseButtonSchema>;
319
+
320
+ /**
321
+ * Development-only helper to validate and normalize props
322
+ * This function should only be used in development mode
323
+ *
324
+ * @param props - Props to validate and normalize
325
+ * @returns Validated and normalized props
326
+ *
327
+ * @example
328
+ * ```tsx
329
+ * // In development, this will validate props and show helpful errors
330
+ * const validatedProps = parseBaseButtonProps({ size: 'invalid', variant: 'solid' });
331
+ * // Throws: "Invalid enum value. Expected '1' | '2' | '3' | '4', received 'invalid'"
332
+ * ```
333
+ */
334
+ export function parseBaseButtonProps(props: unknown): BaseButtonProps {
335
+ if (process.env.NODE_ENV === 'development') {
336
+ return BaseButtonSchema.parse(props);
337
+ }
338
+ return props as BaseButtonProps;
339
+ }
@@ -0,0 +1,198 @@
1
+ import { z } from 'zod';
2
+ import { BaseButtonSchema } from './base-button.schema.js';
3
+
4
+ /**
5
+ * Button Zod schema - Single source of truth for Button component props
6
+ *
7
+ * The Button component is the primary interactive element in the Kookie User Interface.
8
+ * It provides six visual variants, four sizes, comprehensive color options, and built-in
9
+ * tooltip support. The component automatically handles icon sizing, supports responsive
10
+ * layouts, and provides accessibility compliance out of the box.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // Basic button validation
15
+ * const props = ButtonSchema.parse({ variant: 'solid', size: '2' });
16
+ *
17
+ * // Button with tooltip
18
+ * const tooltipProps = ButtonSchema.parse({
19
+ * tooltip: 'Save your progress',
20
+ * tooltipSide: 'top'
21
+ * });
22
+ *
23
+ * // Polymorphic button as link
24
+ * const linkProps = ButtonSchema.parse({
25
+ * as: 'a',
26
+ * href: '/dashboard',
27
+ * children: 'Go to Dashboard'
28
+ * });
29
+ * ```
30
+ */
31
+ export const ButtonSchema = BaseButtonSchema.extend({
32
+ /**
33
+ * Content to display in the tooltip on hover/focus
34
+ */
35
+ tooltip: z.string().optional().describe('Content to display in the tooltip on hover/focus'),
36
+
37
+ /**
38
+ * Side of the button where the tooltip should appear
39
+ * @default 'top'
40
+ */
41
+ tooltipSide: z
42
+ .enum(['top', 'right', 'bottom', 'left'])
43
+ .optional()
44
+ .default('top')
45
+ .describe('Side of the button where the tooltip should appear'),
46
+
47
+ /**
48
+ * Alignment of the tooltip relative to the button
49
+ * @default 'center'
50
+ */
51
+ tooltipAlign: z
52
+ .enum(['start', 'center', 'end'])
53
+ .optional()
54
+ .default('center')
55
+ .describe('Alignment of the tooltip relative to the button'),
56
+
57
+ /**
58
+ * Delay before showing the tooltip (in milliseconds)
59
+ */
60
+ tooltipDelayDuration: z
61
+ .number()
62
+ .optional()
63
+ .describe('Delay before showing the tooltip (in milliseconds)'),
64
+
65
+ /**
66
+ * Whether to disable hoverable content behavior
67
+ * @default false
68
+ */
69
+ tooltipDisableHoverableContent: z
70
+ .boolean()
71
+ .optional()
72
+ .default(false)
73
+ .describe('Whether to disable hoverable content behavior'),
74
+
75
+ /**
76
+ * Override styles for different interaction states
77
+ */
78
+ overrideStyles: z
79
+ .object({
80
+ /** Default/idle state styles */
81
+ normal: z
82
+ .object({
83
+ color: z.string().optional(),
84
+ background: z.string().optional(),
85
+ backgroundColor: z.string().optional(),
86
+ boxShadow: z.string().optional(),
87
+ filter: z.string().optional(),
88
+ outline: z.string().optional(),
89
+ outlineOffset: z.string().optional(),
90
+ opacity: z.union([z.string(), z.number()]).optional(),
91
+ })
92
+ .optional(),
93
+ /** Hover state styles */
94
+ hover: z
95
+ .object({
96
+ color: z.string().optional(),
97
+ background: z.string().optional(),
98
+ backgroundColor: z.string().optional(),
99
+ boxShadow: z.string().optional(),
100
+ filter: z.string().optional(),
101
+ outline: z.string().optional(),
102
+ outlineOffset: z.string().optional(),
103
+ opacity: z.union([z.string(), z.number()]).optional(),
104
+ })
105
+ .optional(),
106
+ /** Active (mouse down) state styles */
107
+ active: z
108
+ .object({
109
+ color: z.string().optional(),
110
+ background: z.string().optional(),
111
+ backgroundColor: z.string().optional(),
112
+ boxShadow: z.string().optional(),
113
+ filter: z.string().optional(),
114
+ outline: z.string().optional(),
115
+ outlineOffset: z.string().optional(),
116
+ opacity: z.union([z.string(), z.number()]).optional(),
117
+ })
118
+ .optional(),
119
+ /** Toggle pressed state styles (data-state="on") */
120
+ pressed: z
121
+ .object({
122
+ color: z.string().optional(),
123
+ background: z.string().optional(),
124
+ backgroundColor: z.string().optional(),
125
+ boxShadow: z.string().optional(),
126
+ filter: z.string().optional(),
127
+ outline: z.string().optional(),
128
+ outlineOffset: z.string().optional(),
129
+ opacity: z.union([z.string(), z.number()]).optional(),
130
+ })
131
+ .optional(),
132
+ /** Open state styles (e.g., when used as a trigger) */
133
+ open: z
134
+ .object({
135
+ color: z.string().optional(),
136
+ background: z.string().optional(),
137
+ backgroundColor: z.string().optional(),
138
+ boxShadow: z.string().optional(),
139
+ filter: z.string().optional(),
140
+ outline: z.string().optional(),
141
+ outlineOffset: z.string().optional(),
142
+ opacity: z.union([z.string(), z.number()]).optional(),
143
+ })
144
+ .optional(),
145
+ /** Disabled state styles */
146
+ disabled: z
147
+ .object({
148
+ color: z.string().optional(),
149
+ background: z.string().optional(),
150
+ backgroundColor: z.string().optional(),
151
+ boxShadow: z.string().optional(),
152
+ filter: z.string().optional(),
153
+ outline: z.string().optional(),
154
+ outlineOffset: z.string().optional(),
155
+ opacity: z.union([z.string(), z.number()]).optional(),
156
+ })
157
+ .optional(),
158
+ /** Focus-visible outline styles */
159
+ focus: z
160
+ .object({
161
+ outline: z.string().optional(),
162
+ outlineOffset: z.string().optional(),
163
+ })
164
+ .optional(),
165
+ })
166
+ .optional()
167
+ .describe('Override styles for different interaction states'),
168
+ }).strict();
169
+
170
+ /**
171
+ * Type derived from Button Zod schema
172
+ * This ensures type safety and consistency with the schema
173
+ */
174
+ export type ButtonProps = z.infer<typeof ButtonSchema>;
175
+
176
+ /**
177
+ * Development-only helper to validate and normalize Button props
178
+ * This function should only be used in development mode
179
+ *
180
+ * @param props - Props to validate and normalize
181
+ * @returns Validated and normalized props
182
+ *
183
+ * @example
184
+ * ```tsx
185
+ * // In development, this will validate props and show helpful errors
186
+ * const validatedProps = parseButtonProps({
187
+ * variant: 'invalid',
188
+ * tooltipSide: 'invalid'
189
+ * });
190
+ * // Throws validation errors for invalid enum values
191
+ * ```
192
+ */
193
+ export function parseButtonProps(props: unknown): ButtonProps {
194
+ if (process.env.NODE_ENV === 'development') {
195
+ return ButtonSchema.parse(props);
196
+ }
197
+ return props as ButtonProps;
198
+ }
@@ -0,0 +1,142 @@
1
+ import { z } from 'zod';
2
+ import { BaseButtonSchema } from './base-button.schema.js';
3
+
4
+ /**
5
+ * IconButton Zod schema - Single source of truth for IconButton component props
6
+ *
7
+ * IconButton is a specialized button component designed for icon-only interactions.
8
+ * It requires proper accessibility attributes to meet WCAG guidelines and provides
9
+ * comprehensive tooltip support for better user experience.
10
+ *
11
+ * Key features:
12
+ * - Required accessibility attributes (aria-label, aria-labelledby, or children)
13
+ * - Tooltip support for better UX
14
+ * - Inherits all BaseButton functionality
15
+ * - Optimized for icon-only interactions
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * // Basic icon button with aria-label
20
+ * const props = IconButtonSchema.parse({
21
+ * 'aria-label': 'Close dialog',
22
+ * variant: 'ghost'
23
+ * });
24
+ *
25
+ * // Icon button with tooltip
26
+ * const tooltipProps = IconButtonSchema.parse({
27
+ * 'aria-label': 'Settings',
28
+ * tooltip: 'Open settings menu',
29
+ * tooltipSide: 'right'
30
+ * });
31
+ *
32
+ * // Icon button with children (fallback accessibility)
33
+ * const childrenProps = IconButtonSchema.parse({
34
+ * children: <SettingsIcon />,
35
+ * variant: 'soft'
36
+ * });
37
+ * ```
38
+ */
39
+ export const IconButtonSchema = BaseButtonSchema.extend({
40
+ /**
41
+ * Content to display in the tooltip on hover/focus
42
+ */
43
+ tooltip: z.string().optional().describe('Content to display in the tooltip on hover/focus'),
44
+
45
+ /**
46
+ * Side of the button where the tooltip should appear
47
+ * @default 'top'
48
+ */
49
+ tooltipSide: z
50
+ .enum(['top', 'right', 'bottom', 'left'])
51
+ .optional()
52
+ .default('top')
53
+ .describe('Side of the button where the tooltip should appear'),
54
+
55
+ /**
56
+ * Alignment of the tooltip relative to the button
57
+ * @default 'center'
58
+ */
59
+ tooltipAlign: z
60
+ .enum(['start', 'center', 'end'])
61
+ .optional()
62
+ .default('center')
63
+ .describe('Alignment of the tooltip relative to the button'),
64
+
65
+ /**
66
+ * Delay before showing the tooltip (in milliseconds)
67
+ */
68
+ tooltipDelayDuration: z
69
+ .number()
70
+ .optional()
71
+ .describe('Delay before showing the tooltip (in milliseconds)'),
72
+
73
+ /**
74
+ * Whether to disable hoverable content behavior
75
+ * @default false
76
+ */
77
+ tooltipDisableHoverableContent: z
78
+ .boolean()
79
+ .optional()
80
+ .default(false)
81
+ .describe('Whether to disable hoverable content behavior'),
82
+
83
+ /**
84
+ * ARIA label for accessibility (required if no aria-labelledby or children)
85
+ * Icon buttons must have an accessible name to meet WCAG guidelines
86
+ */
87
+ 'aria-label': z.string().optional().describe('ARIA label for accessibility'),
88
+
89
+ /**
90
+ * ARIA labelled by reference (required if no aria-label or children)
91
+ * Icon buttons must have an accessible name to meet WCAG guidelines
92
+ */
93
+ 'aria-labelledby': z.string().optional().describe('ARIA labelled by reference'),
94
+
95
+ /**
96
+ * Children elements (required if no aria-label or aria-labelledby)
97
+ * Icon buttons must have an accessible name to meet WCAG guidelines
98
+ */
99
+ children: z
100
+ .any()
101
+ .optional()
102
+ .describe('Children elements (required for accessibility if no aria-label)'),
103
+ }).refine((data) => data['aria-label'] || data['aria-labelledby'] || data.children, {
104
+ message:
105
+ "IconButton must have either 'aria-label', 'aria-labelledby', or 'children' for accessibility",
106
+ path: ['aria-label', 'aria-labelledby', 'children'],
107
+ });
108
+
109
+ /**
110
+ * Type derived from IconButton Zod schema
111
+ * This ensures type safety and consistency with the schema
112
+ */
113
+ export type IconButtonProps = z.infer<typeof IconButtonSchema>;
114
+
115
+ /**
116
+ * Development-only helper to validate and normalize IconButton props
117
+ * This function should only be used in development mode
118
+ *
119
+ * @param props - Props to validate and normalize
120
+ * @returns Validated and normalized props
121
+ *
122
+ * @example
123
+ * ```tsx
124
+ * // In development, this will validate props and show helpful errors
125
+ * const validatedProps = parseIconButtonProps({
126
+ * variant: 'ghost'
127
+ * // Missing accessibility - will throw error
128
+ * });
129
+ * // Throws: "IconButton must have either 'aria-label', 'aria-labelledby', or 'children' for accessibility"
130
+ *
131
+ * const validProps = parseIconButtonProps({
132
+ * 'aria-label': 'Close',
133
+ * variant: 'ghost'
134
+ * });
135
+ * ```
136
+ */
137
+ export function parseIconButtonProps(props: unknown): IconButtonProps {
138
+ if (process.env.NODE_ENV === 'development') {
139
+ return IconButtonSchema.parse(props);
140
+ }
141
+ return props as IconButtonProps;
142
+ }