@aspect-ops/exon-ui 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +793 -43
  2. package/dist/components/Accordion/Accordion.svelte +79 -0
  3. package/dist/components/Accordion/Accordion.svelte.d.ts +10 -0
  4. package/dist/components/Accordion/AccordionItem.svelte +198 -0
  5. package/dist/components/Accordion/AccordionItem.svelte.d.ts +10 -0
  6. package/dist/components/Accordion/index.d.ts +2 -0
  7. package/dist/components/Accordion/index.js +2 -0
  8. package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
  9. package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
  10. package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
  11. package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
  12. package/dist/components/ActionSheet/index.d.ts +3 -0
  13. package/dist/components/ActionSheet/index.js +2 -0
  14. package/dist/components/Alert/Alert.svelte +165 -0
  15. package/dist/components/Alert/Alert.svelte.d.ts +11 -0
  16. package/dist/components/Alert/index.d.ts +1 -0
  17. package/dist/components/Alert/index.js +1 -0
  18. package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
  19. package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
  20. package/dist/components/AspectRatio/index.d.ts +1 -0
  21. package/dist/components/AspectRatio/index.js +1 -0
  22. package/dist/components/Avatar/Avatar.svelte +147 -0
  23. package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
  24. package/dist/components/Avatar/AvatarGroup.svelte +153 -0
  25. package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
  26. package/dist/components/Avatar/index.d.ts +2 -0
  27. package/dist/components/Avatar/index.js +2 -0
  28. package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
  29. package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
  30. package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
  31. package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
  32. package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
  33. package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
  34. package/dist/components/BottomSheet/index.d.ts +3 -0
  35. package/dist/components/BottomSheet/index.js +3 -0
  36. package/dist/components/Box/Box.svelte +41 -0
  37. package/dist/components/Box/Box.svelte.d.ts +7 -0
  38. package/dist/components/Box/index.d.ts +1 -0
  39. package/dist/components/Box/index.js +1 -0
  40. package/dist/components/Card/Card.svelte +95 -0
  41. package/dist/components/Card/Card.svelte.d.ts +10 -0
  42. package/dist/components/Card/CardBody.svelte +32 -0
  43. package/dist/components/Card/CardBody.svelte.d.ts +7 -0
  44. package/dist/components/Card/CardFooter.svelte +34 -0
  45. package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
  46. package/dist/components/Card/CardHeader.svelte +67 -0
  47. package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
  48. package/dist/components/Card/index.d.ts +4 -0
  49. package/dist/components/Card/index.js +4 -0
  50. package/dist/components/Carousel/Carousel.svelte +454 -0
  51. package/dist/components/Carousel/Carousel.svelte.d.ts +14 -0
  52. package/dist/components/Carousel/CarouselSlide.svelte +22 -0
  53. package/dist/components/Carousel/CarouselSlide.svelte.d.ts +7 -0
  54. package/dist/components/Carousel/index.d.ts +2 -0
  55. package/dist/components/Carousel/index.js +2 -0
  56. package/dist/components/Center/Center.svelte +28 -0
  57. package/dist/components/Center/Center.svelte.d.ts +8 -0
  58. package/dist/components/Center/index.d.ts +1 -0
  59. package/dist/components/Center/index.js +1 -0
  60. package/dist/components/Chip/Chip.svelte +461 -0
  61. package/dist/components/Chip/Chip.svelte.d.ts +17 -0
  62. package/dist/components/Chip/ChipGroup.svelte +76 -0
  63. package/dist/components/Chip/ChipGroup.svelte.d.ts +9 -0
  64. package/dist/components/Chip/index.d.ts +2 -0
  65. package/dist/components/Chip/index.js +2 -0
  66. package/dist/components/Container/Container.svelte +58 -0
  67. package/dist/components/Container/Container.svelte.d.ts +10 -0
  68. package/dist/components/Container/index.d.ts +1 -0
  69. package/dist/components/Container/index.js +1 -0
  70. package/dist/components/DatePicker/DatePicker.svelte +746 -0
  71. package/dist/components/DatePicker/DatePicker.svelte.d.ts +19 -0
  72. package/dist/components/DatePicker/index.d.ts +1 -0
  73. package/dist/components/DatePicker/index.js +1 -0
  74. package/dist/components/Divider/Divider.svelte +38 -0
  75. package/dist/components/Divider/Divider.svelte.d.ts +9 -0
  76. package/dist/components/Divider/index.d.ts +1 -0
  77. package/dist/components/Divider/index.js +1 -0
  78. package/dist/components/EmptyState/EmptyState.svelte +164 -0
  79. package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
  80. package/dist/components/EmptyState/index.d.ts +1 -0
  81. package/dist/components/EmptyState/index.js +1 -0
  82. package/dist/components/FAB/FAB.svelte +242 -0
  83. package/dist/components/FAB/FAB.svelte.d.ts +9 -0
  84. package/dist/components/FAB/FABGroup.svelte +449 -0
  85. package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
  86. package/dist/components/FAB/index.d.ts +3 -0
  87. package/dist/components/FAB/index.js +2 -0
  88. package/dist/components/FileUpload/FileUpload.svelte +484 -0
  89. package/dist/components/FileUpload/FileUpload.svelte.d.ts +16 -0
  90. package/dist/components/FileUpload/index.d.ts +1 -0
  91. package/dist/components/FileUpload/index.js +1 -0
  92. package/dist/components/Grid/Grid.svelte +136 -0
  93. package/dist/components/Grid/Grid.svelte.d.ts +12 -0
  94. package/dist/components/Grid/GridItem.svelte +21 -0
  95. package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
  96. package/dist/components/Grid/index.d.ts +2 -0
  97. package/dist/components/Grid/index.js +2 -0
  98. package/dist/components/Image/Image.svelte +223 -0
  99. package/dist/components/Image/Image.svelte.d.ts +19 -0
  100. package/dist/components/Image/index.d.ts +1 -0
  101. package/dist/components/Image/index.js +1 -0
  102. package/dist/components/List/List.svelte +42 -0
  103. package/dist/components/List/List.svelte.d.ts +18 -0
  104. package/dist/components/List/ListItem.svelte +139 -0
  105. package/dist/components/List/ListItem.svelte.d.ts +36 -0
  106. package/dist/components/List/index.d.ts +2 -0
  107. package/dist/components/List/index.js +2 -0
  108. package/dist/components/Modal/Modal.svelte +204 -0
  109. package/dist/components/Modal/Modal.svelte.d.ts +7 -0
  110. package/dist/components/Modal/ModalBody.svelte +50 -0
  111. package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
  112. package/dist/components/Modal/ModalFooter.svelte +37 -0
  113. package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
  114. package/dist/components/Modal/ModalHeader.svelte +73 -0
  115. package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
  116. package/dist/components/Modal/index.d.ts +4 -0
  117. package/dist/components/Modal/index.js +4 -0
  118. package/dist/components/OTPInput/OTPInput.svelte +312 -0
  119. package/dist/components/OTPInput/OTPInput.svelte.d.ts +57 -0
  120. package/dist/components/OTPInput/index.d.ts +1 -0
  121. package/dist/components/OTPInput/index.js +1 -0
  122. package/dist/components/Popover/Popover.svelte +14 -0
  123. package/dist/components/Popover/Popover.svelte.d.ts +7 -0
  124. package/dist/components/Popover/PopoverContent.svelte +63 -0
  125. package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
  126. package/dist/components/Popover/PopoverTrigger.svelte +14 -0
  127. package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
  128. package/dist/components/Popover/index.d.ts +3 -0
  129. package/dist/components/Popover/index.js +3 -0
  130. package/dist/components/Progress/ProgressBar.svelte +86 -0
  131. package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
  132. package/dist/components/Progress/ProgressCircle.svelte +134 -0
  133. package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
  134. package/dist/components/Progress/Spinner.svelte +68 -0
  135. package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
  136. package/dist/components/Progress/index.d.ts +3 -0
  137. package/dist/components/Progress/index.js +3 -0
  138. package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
  139. package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
  140. package/dist/components/PullToRefresh/index.d.ts +1 -0
  141. package/dist/components/PullToRefresh/index.js +1 -0
  142. package/dist/components/Rating/Rating.svelte +316 -0
  143. package/dist/components/Rating/Rating.svelte.d.ts +16 -0
  144. package/dist/components/Rating/index.d.ts +1 -0
  145. package/dist/components/Rating/index.js +1 -0
  146. package/dist/components/SafeArea/SafeArea.svelte +33 -0
  147. package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
  148. package/dist/components/SearchInput/SearchInput.svelte +480 -0
  149. package/dist/components/SearchInput/SearchInput.svelte.d.ts +22 -0
  150. package/dist/components/SearchInput/index.d.ts +1 -0
  151. package/dist/components/SearchInput/index.js +1 -0
  152. package/dist/components/Select/Select.svelte +55 -12
  153. package/dist/components/Skeleton/Skeleton.svelte +59 -0
  154. package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
  155. package/dist/components/Skeleton/index.d.ts +1 -0
  156. package/dist/components/Skeleton/index.js +1 -0
  157. package/dist/components/Slider/Slider.svelte +324 -0
  158. package/dist/components/Slider/Slider.svelte.d.ts +14 -0
  159. package/dist/components/Slider/index.d.ts +1 -0
  160. package/dist/components/Slider/index.js +1 -0
  161. package/dist/components/Spacer/Spacer.svelte +56 -0
  162. package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
  163. package/dist/components/Spacer/index.d.ts +1 -0
  164. package/dist/components/Spacer/index.js +1 -0
  165. package/dist/components/Stack/Stack.svelte +117 -0
  166. package/dist/components/Stack/Stack.svelte.d.ts +13 -0
  167. package/dist/components/Stack/index.d.ts +1 -0
  168. package/dist/components/Stack/index.js +1 -0
  169. package/dist/components/Stepper/Stepper.svelte +100 -0
  170. package/dist/components/Stepper/Stepper.svelte.d.ts +11 -0
  171. package/dist/components/Stepper/StepperStep.svelte +391 -0
  172. package/dist/components/Stepper/StepperStep.svelte.d.ts +13 -0
  173. package/dist/components/Stepper/index.d.ts +2 -0
  174. package/dist/components/Stepper/index.js +2 -0
  175. package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
  176. package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
  177. package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
  178. package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
  179. package/dist/components/SwipeActions/index.d.ts +2 -0
  180. package/dist/components/SwipeActions/index.js +2 -0
  181. package/dist/components/Switch/Switch.svelte +29 -9
  182. package/dist/components/Table/Table.svelte +175 -0
  183. package/dist/components/Table/Table.svelte.d.ts +38 -0
  184. package/dist/components/Table/TableBody.svelte +26 -0
  185. package/dist/components/Table/TableBody.svelte.d.ts +13 -0
  186. package/dist/components/Table/TableCell.svelte +85 -0
  187. package/dist/components/Table/TableCell.svelte.d.ts +28 -0
  188. package/dist/components/Table/TableHead.svelte +36 -0
  189. package/dist/components/Table/TableHead.svelte.d.ts +13 -0
  190. package/dist/components/Table/TableHeader.svelte +217 -0
  191. package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
  192. package/dist/components/Table/TableRow.svelte +92 -0
  193. package/dist/components/Table/TableRow.svelte.d.ts +28 -0
  194. package/dist/components/Table/index.d.ts +6 -0
  195. package/dist/components/Table/index.js +6 -0
  196. package/dist/components/Tag/Tag.svelte +189 -0
  197. package/dist/components/Tag/Tag.svelte.d.ts +13 -0
  198. package/dist/components/Tag/index.d.ts +1 -0
  199. package/dist/components/Tag/index.js +1 -0
  200. package/dist/components/TimePicker/TimePicker.svelte +803 -0
  201. package/dist/components/TimePicker/TimePicker.svelte.d.ts +17 -0
  202. package/dist/components/TimePicker/index.d.ts +1 -0
  203. package/dist/components/TimePicker/index.js +1 -0
  204. package/dist/components/Toast/Toast.svelte +241 -0
  205. package/dist/components/Toast/Toast.svelte.d.ts +18 -0
  206. package/dist/components/Toast/ToastContainer.svelte +110 -0
  207. package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
  208. package/dist/components/Toast/index.d.ts +3 -0
  209. package/dist/components/Toast/index.js +3 -0
  210. package/dist/components/Toast/toast.d.ts +13 -0
  211. package/dist/components/Toast/toast.js +55 -0
  212. package/dist/components/Tooltip/Tooltip.svelte +71 -0
  213. package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
  214. package/dist/components/Tooltip/index.d.ts +2 -0
  215. package/dist/components/Tooltip/index.js +1 -0
  216. package/dist/index.d.ts +38 -1
  217. package/dist/index.js +41 -0
  218. package/dist/styles/tokens.css +5 -0
  219. package/dist/types/data-display.d.ts +161 -0
  220. package/dist/types/data-display.js +1 -0
  221. package/dist/types/feedback.d.ts +92 -0
  222. package/dist/types/feedback.js +1 -0
  223. package/dist/types/index.d.ts +6 -1
  224. package/dist/types/input.d.ts +67 -0
  225. package/dist/types/input.js +2 -0
  226. package/dist/types/layout.d.ts +57 -0
  227. package/dist/types/layout.js +1 -0
  228. package/dist/types/mobile.d.ts +91 -0
  229. package/dist/types/mobile.js +1 -0
  230. package/dist/types/navigation.d.ts +15 -0
  231. package/dist/utils/gestures.d.ts +219 -0
  232. package/dist/utils/gestures.js +492 -0
  233. package/dist/utils/haptics.d.ts +89 -0
  234. package/dist/utils/haptics.js +198 -0
  235. package/dist/utils/platform.d.ts +47 -0
  236. package/dist/utils/platform.js +156 -0
  237. package/package.json +1 -1
package/README.md CHANGED
@@ -57,6 +57,11 @@ npm install @aspect-ops/exon-ui
57
57
  | `RadioGroup` | Radio button group with orientation |
58
58
  | `Switch` | Toggle switch component |
59
59
  | `FormField` | Label wrapper with helper/error text |
60
+ | `SearchInput` | Search with autocomplete suggestions |
61
+ | `DatePicker` | Date selection with calendar popup |
62
+ | `TimePicker` | Time selection with hour/minute picker |
63
+ | `FileUpload` | Drag-drop file upload with previews |
64
+ | `OTPInput` | One-time password input |
60
65
 
61
66
  ### Navigation Components
62
67
 
@@ -68,6 +73,30 @@ npm install @aspect-ops/exon-ui
68
73
  | `BottomNav`, `BottomNavItem` | Mobile bottom navigation |
69
74
  | `Navbar`, `NavItem` | Responsive header with mobile menu |
70
75
  | `Sidebar`, `SidebarItem`, `SidebarGroup` | Collapsible sidebar navigation |
76
+ | `Stepper`, `StepperStep` | Multi-step progress indicator |
77
+
78
+ ### Data Display Components
79
+
80
+ | Component | Description |
81
+ | ---------------------------- | -------------------------------------------- |
82
+ | `Accordion`, `AccordionItem` | Collapsible content panels |
83
+ | `Slider` | Range slider with drag/keyboard support |
84
+ | `Carousel`, `CarouselSlide` | Image/content carousel with swipe |
85
+ | `Image` | Lazy loading image with placeholder/fallback |
86
+ | `Rating` | Star rating display/input |
87
+ | `Chip`, `ChipGroup` | Tag/filter chips with selection |
88
+
89
+ ### Mobile Components
90
+
91
+ | Component | Description |
92
+ | ----------------------------------------------------- | ------------------------------------------- |
93
+ | `ActionSheet`, `ActionSheetItem` | iOS/Android-style bottom action menu |
94
+ | `BottomSheet`, `BottomSheetHeader`, `BottomSheetBody` | Draggable bottom sheet with snap points |
95
+ | `FAB` | Floating action button with positions |
96
+ | `FABGroup` | Speed dial FAB with expandable actions |
97
+ | `PullToRefresh` | Pull-to-refresh gesture for lists |
98
+ | `SwipeActions` | Swipe-to-reveal actions on list items |
99
+ | `SafeArea` | Safe area inset wrapper for notched devices |
71
100
 
72
101
  ## Usage Examples
73
102
 
@@ -320,73 +349,779 @@ npm install @aspect-ops/exon-ui
320
349
  </Sidebar>
321
350
  ```
322
351
 
323
- ## Theming
352
+ ## Advanced Form Components
324
353
 
325
- ### CSS Custom Properties
354
+ ### SearchInput
326
355
 
327
- Import the default styles and customize with CSS variables:
356
+ Search input with autocomplete suggestions and keyboard navigation.
357
+
358
+ **Props:**
359
+
360
+ | Prop | Type | Default | Description |
361
+ | ---------------- | ----------- | ---------- | --------------------------------- |
362
+ | `value` | `string` | `''` | Bindable search value |
363
+ | `placeholder` | `string` | `'Search'` | Input placeholder text |
364
+ | `suggestions` | `string[]` | `[]` | Array of autocomplete suggestions |
365
+ | `size` | `InputSize` | `'md'` | Input size: sm, md, lg |
366
+ | `disabled` | `boolean` | `false` | Disabled state |
367
+ | `loading` | `boolean` | `false` | Show loading spinner |
368
+ | `maxSuggestions` | `number` | `5` | Maximum suggestions to display |
369
+ | `onselect` | `function` | - | Callback when suggestion selected |
370
+
371
+ **Usage:**
328
372
 
329
373
  ```svelte
330
374
  <script>
331
- import '@aspect-ops/exon-ui/styles';
375
+ import { SearchInput } from '@aspect-ops/exon-ui';
376
+
377
+ let query = $state('');
378
+ const suggestions = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
332
379
  </script>
333
380
 
334
- <style>
335
- :root {
336
- /* Colors */
337
- --color-primary: #3b82f6;
338
- --color-primary-hover: #2563eb;
339
- --color-bg: #ffffff;
340
- --color-bg-muted: #f9fafb;
341
- --color-text: #1f2937;
342
- --color-border: #e5e7eb;
343
-
344
- /* Spacing */
345
- --space-xs: 0.25rem;
346
- --space-sm: 0.5rem;
347
- --space-md: 1rem;
348
- --space-lg: 1.5rem;
349
- --space-xl: 2rem;
350
-
351
- /* Border Radius */
352
- --radius-sm: 0.25rem;
353
- --radius-md: 0.5rem;
354
- --radius-lg: 0.75rem;
355
-
356
- /* Transitions */
357
- --transition-fast: 150ms ease;
358
- --transition-base: 200ms ease;
381
+ <SearchInput bind:value={query} {suggestions} onselect={(val) => console.log('Selected:', val)} />
382
+ ```
383
+
384
+ ### DatePicker
385
+
386
+ Date selection with calendar popup and keyboard navigation.
387
+
388
+ **Props:**
389
+
390
+ | Prop | Type | Default | Description |
391
+ | ------------- | ----------- | --------------- | ----------------------- |
392
+ | `value` | `Date` | `null` | Bindable selected date |
393
+ | `placeholder` | `string` | `'Select date'` | Input placeholder |
394
+ | `min` | `Date` | `null` | Minimum selectable date |
395
+ | `max` | `Date` | `null` | Maximum selectable date |
396
+ | `disabled` | `boolean` | `false` | Disabled state |
397
+ | `size` | `InputSize` | `'md'` | Input size: sm, md, lg |
398
+ | `format` | `string` | `'yyyy-MM-dd'` | Date format string |
399
+
400
+ **Usage:**
401
+
402
+ ```svelte
403
+ <script>
404
+ import { DatePicker } from '@aspect-ops/exon-ui';
405
+
406
+ let selectedDate = $state(null);
407
+ </script>
408
+
409
+ <DatePicker bind:value={selectedDate} placeholder="Choose a date" />
410
+ ```
411
+
412
+ ### TimePicker
413
+
414
+ Time selection with scrollable hour/minute/period picker.
415
+
416
+ **Props:**
417
+
418
+ | Prop | Type | Default | Description |
419
+ | ------------ | ---------------- | ------- | --------------------------- |
420
+ | `value` | `string` | `''` | Bindable time value (HH:mm) |
421
+ | `format` | `'12h' \| '24h'` | `'24h'` | Time format |
422
+ | `minuteStep` | `number` | `1` | Minute increment step |
423
+ | `min` | `string` | - | Minimum selectable time |
424
+ | `max` | `string` | - | Maximum selectable time |
425
+ | `disabled` | `boolean` | `false` | Disabled state |
426
+ | `size` | `InputSize` | `'md'` | Input size: sm, md, lg |
427
+
428
+ **Usage:**
429
+
430
+ ```svelte
431
+ <script>
432
+ import { TimePicker } from '@aspect-ops/exon-ui';
433
+
434
+ let time = $state('');
435
+ </script>
436
+
437
+ <TimePicker bind:value={time} format="12h" minuteStep={15} />
438
+ ```
439
+
440
+ ### FileUpload
441
+
442
+ Drag-and-drop file upload with image previews and validation.
443
+
444
+ **Props:**
445
+
446
+ | Prop | Type | Default | Description |
447
+ | ------------- | --------- | ------- | -------------------------------- |
448
+ | `files` | `File[]` | `[]` | Bindable array of uploaded files |
449
+ | `accept` | `string` | `''` | Accepted file types |
450
+ | `multiple` | `boolean` | `false` | Allow multiple file selection |
451
+ | `maxSize` | `number` | `10MB` | Maximum file size in bytes |
452
+ | `maxFiles` | `number` | `10` | Maximum number of files |
453
+ | `showPreview` | `boolean` | `true` | Show image previews |
454
+
455
+ **Usage:**
456
+
457
+ ```svelte
458
+ <script>
459
+ import { FileUpload } from '@aspect-ops/exon-ui';
460
+
461
+ let files = $state([]);
462
+ </script>
463
+
464
+ <FileUpload bind:files multiple accept="image/*" maxSize={5242880} />
465
+ ```
466
+
467
+ ### OTPInput
468
+
469
+ One-time password input with auto-focus and paste support.
470
+
471
+ **Props:**
472
+
473
+ | Prop | Type | Default | Description |
474
+ | ------------ | ----------------------------- | ----------- | -------------------------------- |
475
+ | `value` | `string` | `''` | Bindable OTP value |
476
+ | `length` | `number` | `6` | Number of OTP digits |
477
+ | `type` | `'numeric' \| 'alphanumeric'` | `'numeric'` | Input validation type |
478
+ | `masked` | `boolean` | `false` | Show dots instead of characters |
479
+ | `disabled` | `boolean` | `false` | Disabled state |
480
+ | `size` | `Size` | `'md'` | Input size: sm, md, lg |
481
+ | `oncomplete` | `function` | - | Callback when all digits entered |
482
+
483
+ **Usage:**
484
+
485
+ ```svelte
486
+ <script>
487
+ import { OTPInput } from '@aspect-ops/exon-ui';
488
+
489
+ let otp = $state('');
490
+ </script>
491
+
492
+ <OTPInput bind:value={otp} length={6} oncomplete={(code) => console.log('OTP:', code)} />
493
+ ```
494
+
495
+ ## Data Display Components
496
+
497
+ ### Accordion
498
+
499
+ Collapsible content panels with single or multiple expansion.
500
+
501
+ **Props:**
502
+
503
+ | Prop | Type | Default | Description |
504
+ | ---------- | -------------------- | ------- | ------------------------------- |
505
+ | `value` | `string \| string[]` | `[]` | Bindable expanded item value(s) |
506
+ | `multiple` | `boolean` | `false` | Allow multiple panels open |
507
+ | `disabled` | `boolean` | `false` | Disabled state |
508
+
509
+ **Usage:**
510
+
511
+ ```svelte
512
+ <script>
513
+ import { Accordion, AccordionItem } from '@aspect-ops/exon-ui';
514
+
515
+ let expanded = $state([]);
516
+ </script>
517
+
518
+ <Accordion bind:value={expanded} multiple>
519
+ {#snippet children()}
520
+ <AccordionItem value="item1" title="Section 1">Content for section 1</AccordionItem>
521
+ <AccordionItem value="item2" title="Section 2">Content for section 2</AccordionItem>
522
+ {/snippet}
523
+ </Accordion>
524
+ ```
525
+
526
+ ### Slider
527
+
528
+ Range slider with drag, keyboard support, and optional value display.
529
+
530
+ **Props:**
531
+
532
+ | Prop | Type | Default | Description |
533
+ | ----------- | --------- | ------- | --------------------- |
534
+ | `value` | `number` | `0` | Bindable slider value |
535
+ | `min` | `number` | `0` | Minimum value |
536
+ | `max` | `number` | `100` | Maximum value |
537
+ | `step` | `number` | `1` | Value increment step |
538
+ | `disabled` | `boolean` | `false` | Disabled state |
539
+ | `showValue` | `boolean` | `false` | Show value tooltip |
540
+ | `showTicks` | `boolean` | `false` | Show tick marks |
541
+
542
+ **Usage:**
543
+
544
+ ```svelte
545
+ <script>
546
+ import { Slider } from '@aspect-ops/exon-ui';
547
+
548
+ let volume = $state(50);
549
+ </script>
550
+
551
+ <Slider bind:value={volume} min={0} max={100} showValue />
552
+ ```
553
+
554
+ ### Carousel
555
+
556
+ Image/content carousel with swipe gestures and autoplay.
557
+
558
+ **Props:**
559
+
560
+ | Prop | Type | Default | Description |
561
+ | ------------------ | --------- | ------- | --------------------------- |
562
+ | `activeIndex` | `number` | `0` | Bindable active slide index |
563
+ | `autoplay` | `boolean` | `false` | Enable autoplay |
564
+ | `autoplayInterval` | `number` | `5000` | Autoplay interval (ms) |
565
+ | `loop` | `boolean` | `true` | Loop slides |
566
+ | `showIndicators` | `boolean` | `true` | Show dot indicators |
567
+ | `showArrows` | `boolean` | `true` | Show navigation arrows |
568
+
569
+ **Usage:**
570
+
571
+ ```svelte
572
+ <script>
573
+ import { Carousel, CarouselSlide } from '@aspect-ops/exon-ui';
574
+
575
+ let activeSlide = $state(0);
576
+ </script>
577
+
578
+ <Carousel bind:activeIndex={activeSlide} autoplay loop>
579
+ {#snippet children()}
580
+ <CarouselSlide><img src="slide1.jpg" alt="Slide 1" /></CarouselSlide>
581
+ <CarouselSlide><img src="slide2.jpg" alt="Slide 2" /></CarouselSlide>
582
+ <CarouselSlide><img src="slide3.jpg" alt="Slide 3" /></CarouselSlide>
583
+ {/snippet}
584
+ </Carousel>
585
+ ```
586
+
587
+ ### Image
588
+
589
+ Lazy-loading image with placeholder and fallback support.
590
+
591
+ **Props:**
592
+
593
+ | Prop | Type | Default | Description |
594
+ | ------------- | ------------------- | --------- | ------------------------------- |
595
+ | `src` | `string` | - | Image source URL (required) |
596
+ | `alt` | `string` | - | Alt text (required) |
597
+ | `loading` | `'lazy' \| 'eager'` | `'lazy'` | Loading strategy |
598
+ | `objectFit` | `string` | `'cover'` | CSS object-fit value |
599
+ | `placeholder` | `string` | - | Placeholder image or color |
600
+ | `fallback` | `string` | - | Fallback image on error |
601
+ | `rounded` | `boolean \| string` | `false` | Border radius: sm, md, lg, full |
602
+ | `aspectRatio` | `string` | - | CSS aspect ratio |
603
+
604
+ **Usage:**
605
+
606
+ ```svelte
607
+ <script>
608
+ import { Image } from '@aspect-ops/exon-ui';
609
+ </script>
610
+
611
+ <Image
612
+ src="profile.jpg"
613
+ alt="User profile"
614
+ rounded="full"
615
+ aspectRatio="1/1"
616
+ placeholder="#e5e7eb"
617
+ fallback="default-avatar.png"
618
+ />
619
+ ```
620
+
621
+ ### Rating
622
+
623
+ Star rating display and input with half-star support.
624
+
625
+ **Props:**
626
+
627
+ | Prop | Type | Default | Description |
628
+ | ----------- | --------- | ------- | ----------------------- |
629
+ | `value` | `number` | `0` | Bindable rating value |
630
+ | `max` | `number` | `5` | Maximum rating |
631
+ | `allowHalf` | `boolean` | `false` | Allow half-star ratings |
632
+ | `readonly` | `boolean` | `false` | Read-only display mode |
633
+ | `disabled` | `boolean` | `false` | Disabled state |
634
+ | `size` | `Size` | `'md'` | Star size: sm, md, lg |
635
+ | `showValue` | `boolean` | `false` | Show numeric value |
636
+
637
+ **Usage:**
638
+
639
+ ```svelte
640
+ <script>
641
+ import { Rating } from '@aspect-ops/exon-ui';
642
+
643
+ let rating = $state(4.5);
644
+ </script>
645
+
646
+ <Rating bind:value={rating} allowHalf showValue />
647
+ ```
648
+
649
+ ## Navigation & Feedback Components
650
+
651
+ ### Stepper
652
+
653
+ Multi-step progress indicator with linear or non-linear navigation.
654
+
655
+ **Props:**
656
+
657
+ | Prop | Type | Default | Description |
658
+ | ------------- | ---------------------------- | -------------- | ----------------------------- |
659
+ | `activeStep` | `number` | `0` | Bindable active step index |
660
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout orientation |
661
+ | `linear` | `boolean` | `true` | Enforce sequential navigation |
662
+
663
+ **Usage:**
664
+
665
+ ```svelte
666
+ <script>
667
+ import { Stepper, StepperStep } from '@aspect-ops/exon-ui';
668
+
669
+ let currentStep = $state(0);
670
+ </script>
671
+
672
+ <Stepper bind:activeStep={currentStep}>
673
+ {#snippet children()}
674
+ <StepperStep label="Personal Info" description="Enter your details" />
675
+ <StepperStep label="Address" description="Shipping information" />
676
+ <StepperStep label="Review" description="Confirm your order" />
677
+ {/snippet}
678
+ </Stepper>
679
+ ```
680
+
681
+ ### Chip
682
+
683
+ Compact tag/filter chips with selection and removal.
684
+
685
+ **Props:**
686
+
687
+ | Prop | Type | Default | Description |
688
+ | ----------- | ------------- | ----------- | ------------------------------------------------ |
689
+ | `variant` | `ChipVariant` | `'filled'` | Chip style: filled, outlined, soft |
690
+ | `color` | `ChipColor` | `'default'` | Color: default, primary, success, warning, error |
691
+ | `size` | `ChipSize` | `'md'` | Size: sm, md, lg |
692
+ | `removable` | `boolean` | `false` | Show remove button |
693
+ | `selected` | `boolean` | `false` | Selected state |
694
+ | `disabled` | `boolean` | `false` | Disabled state |
695
+
696
+ **Usage:**
697
+
698
+ ```svelte
699
+ <script>
700
+ import { Chip, ChipGroup } from '@aspect-ops/exon-ui';
701
+
702
+ let selected = $state(['tag1']);
703
+ </script>
704
+
705
+ <ChipGroup>
706
+ <Chip onclick={() => console.log('clicked')}>JavaScript</Chip>
707
+ <Chip color="primary" removable onremove={() => console.log('removed')}>Svelte</Chip>
708
+ <Chip variant="outlined" color="success">TypeScript</Chip>
709
+ </ChipGroup>
710
+ ```
711
+
712
+ ## Mobile Components
713
+
714
+ Components optimized for Capacitor mobile apps with gesture support, haptic feedback, and safe area handling.
715
+
716
+ | Component | Description |
717
+ | --------------- | ------------------------------------------- |
718
+ | `ActionSheet` | iOS/Android-style bottom action menu |
719
+ | `BottomSheet` | Draggable bottom sheet with snap points |
720
+ | `FAB` | Floating action button with positions |
721
+ | `FABGroup` | Speed dial FAB with expandable actions |
722
+ | `PullToRefresh` | Pull-to-refresh gesture for lists |
723
+ | `SwipeActions` | Swipe-to-reveal actions on list items |
724
+ | `SafeArea` | Safe area inset wrapper for notched devices |
725
+
726
+ ### ActionSheet
727
+
728
+ ```svelte
729
+ <script>
730
+ import { ActionSheet, ActionSheetItem, Button } from '@aspect-ops/exon-ui';
731
+
732
+ let open = $state(false);
733
+ </script>
734
+
735
+ <Button onclick={() => (open = true)}>Show Actions</Button>
736
+
737
+ <ActionSheet bind:open title="Choose an action" description="Select what you want to do">
738
+ {#snippet actions()}
739
+ <ActionSheetItem onclick={() => console.log('Edit')}>Edit</ActionSheetItem>
740
+ <ActionSheetItem onclick={() => console.log('Share')}>Share</ActionSheetItem>
741
+ <ActionSheetItem destructive onclick={() => console.log('Delete')}>Delete</ActionSheetItem>
742
+ {/snippet}
743
+ </ActionSheet>
744
+ ```
745
+
746
+ **Props:**
747
+
748
+ - `open` - Bindable open state
749
+ - `title` - Optional header title
750
+ - `description` - Optional header description
751
+ - `cancelLabel` - Cancel button text (default: "Cancel")
752
+ - `showCancel` - Show cancel button (default: true)
753
+ - `closeOnSelect` - Close when action selected (default: true)
754
+
755
+ ### BottomSheet
756
+
757
+ ```svelte
758
+ <script>
759
+ import { BottomSheet, BottomSheetHeader, BottomSheetBody, Button } from '@aspect-ops/exon-ui';
760
+
761
+ let open = $state(false);
762
+ </script>
763
+
764
+ <Button onclick={() => (open = true)}>Open Sheet</Button>
765
+
766
+ <BottomSheet bind:open snapPoints={['half', 'full']} defaultSnapPoint="half">
767
+ <BottomSheetHeader>
768
+ <h3>Sheet Title</h3>
769
+ </BottomSheetHeader>
770
+ <BottomSheetBody>
771
+ <p>Drag the handle to resize or swipe down to close.</p>
772
+ </BottomSheetBody>
773
+ </BottomSheet>
774
+ ```
775
+
776
+ **Props:**
777
+
778
+ - `open` - Bindable open state
779
+ - `snapPoints` - Array of snap points: `'min'` | `'half'` | `'full'` | number (px)
780
+ - `defaultSnapPoint` - Initial snap point (default: 'half')
781
+ - `showHandle` - Show drag handle (default: true)
782
+ - `closeOnBackdrop` - Close on backdrop click (default: true)
783
+ - `closeOnEscape` - Close on Escape key (default: true)
784
+
785
+ ### FAB (Floating Action Button)
786
+
787
+ ```svelte
788
+ <script>
789
+ import { FAB } from '@aspect-ops/exon-ui';
790
+ </script>
791
+
792
+ <!-- Basic FAB -->
793
+ <FAB onclick={() => console.log('clicked')}>+</FAB>
794
+
795
+ <!-- Positioned FAB -->
796
+ <FAB position="bottom-left" size="lg">📝</FAB>
797
+
798
+ <!-- Extended FAB with label -->
799
+ <FAB extended position="bottom-right">
800
+ {#snippet children()}➕{/snippet}
801
+ {#snippet label()}Add Item{/snippet}
802
+ </FAB>
803
+ ```
804
+
805
+ **Props:**
806
+
807
+ - `size` - `'sm'` | `'md'` | `'lg'` (44px, 56px, 72px)
808
+ - `position` - `'bottom-right'` | `'bottom-left'` | `'bottom-center'` | `'top-right'` | `'top-left'`
809
+ - `extended` - Extended FAB with label
810
+ - `disabled` - Disable the button
811
+
812
+ ### FABGroup (Speed Dial)
813
+
814
+ ```svelte
815
+ <script>
816
+ import { FABGroup } from '@aspect-ops/exon-ui';
817
+
818
+ const actions = [
819
+ { icon: '📷', label: 'Camera', onAction: () => console.log('Camera') },
820
+ { icon: '🖼️', label: 'Gallery', onAction: () => console.log('Gallery') },
821
+ { icon: '📎', label: 'Attach', onAction: () => console.log('Attach') }
822
+ ];
823
+ </script>
824
+
825
+ <FABGroup {actions} icon="+" closeIcon="×" position="bottom-right" direction="up" />
826
+ ```
827
+
828
+ **Props:**
829
+
830
+ - `actions` - Array of `{ icon, label, onAction }` objects
831
+ - `icon` - Main FAB icon (default: '+')
832
+ - `closeIcon` - Icon when open (default: '×')
833
+ - `position` - Same as FAB positions
834
+ - `direction` - `'up'` | `'down'` | `'left'` | `'right'`
835
+
836
+ ### PullToRefresh
837
+
838
+ ```svelte
839
+ <script>
840
+ import { PullToRefresh } from '@aspect-ops/exon-ui';
841
+
842
+ let refreshing = $state(false);
843
+
844
+ async function handleRefresh() {
845
+ // Fetch new data
846
+ await new Promise((r) => setTimeout(r, 2000));
847
+ refreshing = false;
359
848
  }
360
- </style>
849
+ </script>
850
+
851
+ <PullToRefresh bind:refreshing onrefresh={handleRefresh}>
852
+ <div class="content">
853
+ <p>Pull down to refresh...</p>
854
+ <!-- Your scrollable content -->
855
+ </div>
856
+ </PullToRefresh>
857
+ ```
858
+
859
+ **Props:**
860
+
861
+ - `refreshing` - Bindable loading state
862
+ - `threshold` - Pull distance to trigger (default: 80px)
863
+ - `maxPull` - Maximum pull distance (default: 150px)
864
+ - `disabled` - Disable pull-to-refresh
865
+ - `onrefresh` - Callback when threshold reached
866
+
867
+ ### SwipeActions
868
+
869
+ ```svelte
870
+ <script>
871
+ import { SwipeActions } from '@aspect-ops/exon-ui';
872
+
873
+ const leftActions = [
874
+ { icon: '📌', label: 'Pin', color: '#3b82f6', onAction: () => console.log('Pin') }
875
+ ];
876
+
877
+ const rightActions = [
878
+ { icon: '🗑️', label: 'Delete', color: '#ef4444', onAction: () => console.log('Delete') }
879
+ ];
880
+ </script>
881
+
882
+ <SwipeActions {leftActions} {rightActions}>
883
+ <div class="list-item">
884
+ <p>Swipe me left or right</p>
885
+ </div>
886
+ </SwipeActions>
887
+ ```
888
+
889
+ **Props:**
890
+
891
+ - `leftActions` - Actions revealed on swipe right
892
+ - `rightActions` - Actions revealed on swipe left
893
+ - `threshold` - Swipe distance to reveal (default: 60px)
894
+ - `disabled` - Disable swipe gestures
895
+
896
+ **Action object:**
897
+
898
+ - `icon` - Icon string or emoji
899
+ - `label` - Action label text
900
+ - `color` - Background color
901
+ - `onAction` - Callback when tapped
902
+
903
+ ### SafeArea
904
+
905
+ ```svelte
906
+ <script>
907
+ import { SafeArea } from '@aspect-ops/exon-ui';
908
+ </script>
909
+
910
+ <!-- All edges (default) -->
911
+ <SafeArea>
912
+ <main>Content with safe area padding on all sides</main>
913
+ </SafeArea>
914
+
915
+ <!-- Specific edges -->
916
+ <SafeArea edges={['top', 'bottom']}>
917
+ <main>Only top and bottom safe area</main>
918
+ </SafeArea>
919
+ ```
920
+
921
+ **Props:**
922
+
923
+ - `edges` - Array of edges: `'top'` | `'right'` | `'bottom'` | `'left'` (default: all)
924
+
925
+ ## Theming & Customization
926
+
927
+ ### Quick Setup
928
+
929
+ ```svelte
930
+ <script>
931
+ // In your root layout (+layout.svelte)
932
+ import '@aspect-ops/exon-ui/styles';
933
+ </script>
934
+ ```
935
+
936
+ ### Custom Brand Colors
937
+
938
+ Override CSS variables in your app's global styles:
939
+
940
+ ```css
941
+ /* src/app.css or global styles */
942
+ :root {
943
+ /* Primary brand color */
944
+ --color-primary: #8b5cf6; /* Purple */
945
+ --color-primary-hover: #7c3aed;
946
+ --color-primary-active: #6d28d9;
947
+ --color-primary-bg: #ede9fe;
948
+
949
+ /* Secondary */
950
+ --color-secondary: #64748b;
951
+ --color-secondary-hover: #475569;
952
+
953
+ /* Destructive/Error */
954
+ --color-destructive: #dc2626;
955
+ --color-error: #dc2626;
956
+
957
+ /* Success */
958
+ --color-success: #16a34a;
959
+
960
+ /* Warning */
961
+ --color-warning: #d97706;
962
+ }
963
+ ```
964
+
965
+ ### Full Design Token Reference
966
+
967
+ ```css
968
+ :root {
969
+ /* ===== COLORS ===== */
970
+
971
+ /* Backgrounds */
972
+ --color-bg: #fcfcfc; /* Main background */
973
+ --color-bg-muted: #f9f9f9; /* Subtle background */
974
+ --color-bg-subtle: #f0f0f0; /* More contrast */
975
+ --color-bg-hover: #e8e8e8; /* Hover state */
976
+ --color-bg-active: #e0e0e0; /* Active/pressed */
977
+ --color-bg-elevated: #ffffff; /* Cards, modals */
978
+ --color-bg-card: #ffffff; /* Card backgrounds */
979
+
980
+ /* Text */
981
+ --color-text: #202020; /* Primary text */
982
+ --color-text-muted: #646464; /* Secondary text */
983
+ --color-text-subtle: #838383; /* Tertiary text */
984
+ --color-text-inverse: #ffffff; /* Text on dark backgrounds */
985
+
986
+ /* Borders */
987
+ --color-border: #d9d9d9;
988
+ --color-border-hover: #cecece;
989
+ --color-border-active: #bbbbbb;
990
+
991
+ /* ===== TYPOGRAPHY ===== */
992
+
993
+ --font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
994
+ --font-family-mono: ui-monospace, 'SF Mono', Menlo, Monaco, monospace;
995
+
996
+ /* Fluid type scale (responsive) */
997
+ --text-xs: clamp(0.69rem, 0.66rem + 0.14vw, 0.78rem); /* ~11-12px */
998
+ --text-sm: clamp(0.83rem, 0.79rem + 0.21vw, 0.97rem); /* ~13-16px */
999
+ --text-base: clamp(1rem, 0.93rem + 0.33vw, 1.125rem); /* 16-18px */
1000
+ --text-lg: clamp(1.13rem, 1.03rem + 0.47vw, 1.41rem); /* ~18-23px */
1001
+ --text-xl: clamp(1.27rem, 1.14rem + 0.65vw, 1.76rem); /* ~20-28px */
1002
+ --text-2xl: clamp(1.42rem, 1.25rem + 0.89vw, 2.2rem); /* ~23-35px */
1003
+ --text-3xl: clamp(1.6rem, 1.37rem + 1.19vw, 2.75rem); /* ~26-44px */
1004
+ --text-4xl: clamp(1.8rem, 1.49rem + 1.57vw, 3.43rem); /* ~29-55px */
1005
+
1006
+ /* Font weights */
1007
+ --font-normal: 400;
1008
+ --font-medium: 500;
1009
+ --font-semibold: 600;
1010
+ --font-bold: 700;
1011
+
1012
+ /* ===== SPACING ===== */
1013
+
1014
+ --space-xs: clamp(0.25rem, 0.23rem + 0.11vw, 0.31rem); /* 4-5px */
1015
+ --space-sm: clamp(0.5rem, 0.46rem + 0.22vw, 0.63rem); /* 8-10px */
1016
+ --space-md: clamp(1rem, 0.93rem + 0.33vw, 1.125rem); /* 16-18px */
1017
+ --space-lg: clamp(1.5rem, 1.39rem + 0.54vw, 1.75rem); /* 24-28px */
1018
+ --space-xl: clamp(2rem, 1.85rem + 0.76vw, 2.5rem); /* 32-40px */
1019
+ --space-2xl: clamp(3rem, 2.78rem + 1.09vw, 3.75rem); /* 48-60px */
1020
+
1021
+ /* ===== BORDER RADIUS ===== */
1022
+
1023
+ --radius-sm: 0.25rem; /* 4px */
1024
+ --radius-md: 0.375rem; /* 6px */
1025
+ --radius-lg: 0.5rem; /* 8px */
1026
+ --radius-xl: 0.75rem; /* 12px */
1027
+ --radius-2xl: 1rem; /* 16px */
1028
+ --radius-full: 9999px; /* Pill/circle */
1029
+
1030
+ /* ===== SHADOWS ===== */
1031
+
1032
+ --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1);
1033
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
1034
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
1035
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
1036
+
1037
+ /* ===== TRANSITIONS ===== */
1038
+
1039
+ --transition-fast: 150ms ease;
1040
+ --transition-base: 200ms ease;
1041
+ --transition-slow: 300ms ease;
1042
+
1043
+ /* ===== MOBILE ===== */
1044
+
1045
+ --touch-target-min: 44px;
1046
+ --safe-area-top: env(safe-area-inset-top, 0px);
1047
+ --safe-area-bottom: env(safe-area-inset-bottom, 0px);
1048
+ }
361
1049
  ```
362
1050
 
363
1051
  ### Dark Mode
364
1052
 
365
- Toggle dark mode by setting `data-theme="dark"` on the html element:
1053
+ The library supports dark mode via `data-theme` attribute or system preference:
366
1054
 
367
1055
  ```svelte
368
1056
  <script>
1057
+ let isDark = $state(false);
1058
+
369
1059
  function toggleTheme() {
370
- const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
371
- document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
1060
+ isDark = !isDark;
1061
+ document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
372
1062
  }
1063
+
1064
+ // Respect system preference on load
1065
+ import { onMount } from 'svelte';
1066
+ onMount(() => {
1067
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
1068
+ isDark = true;
1069
+ document.documentElement.setAttribute('data-theme', 'dark');
1070
+ }
1071
+ });
373
1072
  </script>
374
1073
 
375
- <button onclick={toggleTheme}>Toggle Theme</button>
1074
+ <Switch bind:checked={isDark} onchange={toggleTheme}>
1075
+ {#snippet children()}Dark Mode{/snippet}
1076
+ </Switch>
376
1077
  ```
377
1078
 
378
- Dark theme variables are automatically applied:
1079
+ Dark theme automatically adjusts all colors:
379
1080
 
380
1081
  ```css
381
1082
  [data-theme='dark'] {
382
- --color-bg: #111827;
383
- --color-bg-muted: #1f2937;
384
- --color-bg-elevated: #1f2937;
385
- --color-text: #f9fafb;
386
- --color-border: #374151;
1083
+ --color-bg: #111111;
1084
+ --color-bg-muted: #191919;
1085
+ --color-bg-elevated: #222222;
1086
+ --color-text: #eeeeee;
1087
+ --color-text-muted: #b4b4b4;
1088
+ --color-border: #3a3a3a;
1089
+ /* All other colors auto-adapt */
387
1090
  }
388
1091
  ```
389
1092
 
1093
+ ### Component-Level Customization
1094
+
1095
+ Override styles for specific components using CSS classes:
1096
+
1097
+ ```svelte
1098
+ <Button class="my-custom-button" variant="primary">Custom Button</Button>
1099
+
1100
+ <style>
1101
+ :global(.my-custom-button) {
1102
+ --color-primary: #10b981;
1103
+ border-radius: var(--radius-full);
1104
+ text-transform: uppercase;
1105
+ }
1106
+ </style>
1107
+ ```
1108
+
1109
+ ### Platform-Specific Styling
1110
+
1111
+ Components adapt to iOS/Android automatically. Set platform manually:
1112
+
1113
+ ```svelte
1114
+ <script>
1115
+ import { onMount } from 'svelte';
1116
+
1117
+ onMount(() => {
1118
+ // Auto-detect or set manually
1119
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
1120
+ document.documentElement.setAttribute('data-platform', isIOS ? 'ios' : 'android');
1121
+ });
1122
+ </script>
1123
+ ```
1124
+
390
1125
  ## Mobile / Capacitor Support
391
1126
 
392
1127
  Components are designed with mobile-first principles:
@@ -395,12 +1130,27 @@ Components are designed with mobile-first principles:
395
1130
  - **Safe area inset handling** for notched devices (iPhone, Android gesture bar)
396
1131
  - **Responsive breakpoints** for adaptive layouts
397
1132
  - **Hardware-accelerated animations** for smooth 60fps performance
1133
+ - **Haptic feedback** integration (requires Capacitor Haptics plugin)
1134
+ - **Platform-adaptive styling** for iOS and Android
398
1135
 
399
- ### Safe Area Example
1136
+ ### Capacitor Integration
1137
+
1138
+ ```bash
1139
+ # Install Capacitor plugins for full mobile support
1140
+ npm install @capacitor/haptics @capacitor/status-bar
1141
+ ```
400
1142
 
401
1143
  ```svelte
402
- <BottomNav {items} />
403
- <!-- Automatically includes padding-bottom: env(safe-area-inset-bottom) -->
1144
+ <script>
1145
+ import { SafeArea, BottomNav, PullToRefresh } from '@aspect-ops/exon-ui';
1146
+ </script>
1147
+
1148
+ <SafeArea edges={['top', 'bottom']}>
1149
+ <PullToRefresh onrefresh={handleRefresh}>
1150
+ <main>Your content here</main>
1151
+ </PullToRefresh>
1152
+ <BottomNav {items} />
1153
+ </SafeArea>
404
1154
  ```
405
1155
 
406
1156
  ## TypeScript