@aspect-ops/exon-ui 0.0.1 → 0.0.3

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 (178) hide show
  1. package/README.md +438 -0
  2. package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
  3. package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
  4. package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
  5. package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
  6. package/dist/components/ActionSheet/index.d.ts +3 -0
  7. package/dist/components/ActionSheet/index.js +2 -0
  8. package/dist/components/Alert/Alert.svelte +165 -0
  9. package/dist/components/Alert/Alert.svelte.d.ts +11 -0
  10. package/dist/components/Alert/index.d.ts +1 -0
  11. package/dist/components/Alert/index.js +1 -0
  12. package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
  13. package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
  14. package/dist/components/AspectRatio/index.d.ts +1 -0
  15. package/dist/components/AspectRatio/index.js +1 -0
  16. package/dist/components/Avatar/Avatar.svelte +147 -0
  17. package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
  18. package/dist/components/Avatar/AvatarGroup.svelte +153 -0
  19. package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
  20. package/dist/components/Avatar/index.d.ts +2 -0
  21. package/dist/components/Avatar/index.js +2 -0
  22. package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
  23. package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
  24. package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
  25. package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
  26. package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
  27. package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
  28. package/dist/components/BottomSheet/index.d.ts +3 -0
  29. package/dist/components/BottomSheet/index.js +3 -0
  30. package/dist/components/Box/Box.svelte +41 -0
  31. package/dist/components/Box/Box.svelte.d.ts +7 -0
  32. package/dist/components/Box/index.d.ts +1 -0
  33. package/dist/components/Box/index.js +1 -0
  34. package/dist/components/Card/Card.svelte +95 -0
  35. package/dist/components/Card/Card.svelte.d.ts +10 -0
  36. package/dist/components/Card/CardBody.svelte +32 -0
  37. package/dist/components/Card/CardBody.svelte.d.ts +7 -0
  38. package/dist/components/Card/CardFooter.svelte +34 -0
  39. package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
  40. package/dist/components/Card/CardHeader.svelte +67 -0
  41. package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
  42. package/dist/components/Card/index.d.ts +4 -0
  43. package/dist/components/Card/index.js +4 -0
  44. package/dist/components/Center/Center.svelte +28 -0
  45. package/dist/components/Center/Center.svelte.d.ts +8 -0
  46. package/dist/components/Center/index.d.ts +1 -0
  47. package/dist/components/Center/index.js +1 -0
  48. package/dist/components/Container/Container.svelte +58 -0
  49. package/dist/components/Container/Container.svelte.d.ts +10 -0
  50. package/dist/components/Container/index.d.ts +1 -0
  51. package/dist/components/Container/index.js +1 -0
  52. package/dist/components/Divider/Divider.svelte +38 -0
  53. package/dist/components/Divider/Divider.svelte.d.ts +9 -0
  54. package/dist/components/Divider/index.d.ts +1 -0
  55. package/dist/components/Divider/index.js +1 -0
  56. package/dist/components/EmptyState/EmptyState.svelte +164 -0
  57. package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
  58. package/dist/components/EmptyState/index.d.ts +1 -0
  59. package/dist/components/EmptyState/index.js +1 -0
  60. package/dist/components/FAB/FAB.svelte +242 -0
  61. package/dist/components/FAB/FAB.svelte.d.ts +9 -0
  62. package/dist/components/FAB/FABGroup.svelte +449 -0
  63. package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
  64. package/dist/components/FAB/index.d.ts +3 -0
  65. package/dist/components/FAB/index.js +2 -0
  66. package/dist/components/Grid/Grid.svelte +136 -0
  67. package/dist/components/Grid/Grid.svelte.d.ts +12 -0
  68. package/dist/components/Grid/GridItem.svelte +21 -0
  69. package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
  70. package/dist/components/Grid/index.d.ts +2 -0
  71. package/dist/components/Grid/index.js +2 -0
  72. package/dist/components/List/List.svelte +42 -0
  73. package/dist/components/List/List.svelte.d.ts +18 -0
  74. package/dist/components/List/ListItem.svelte +139 -0
  75. package/dist/components/List/ListItem.svelte.d.ts +36 -0
  76. package/dist/components/List/index.d.ts +2 -0
  77. package/dist/components/List/index.js +2 -0
  78. package/dist/components/Modal/Modal.svelte +204 -0
  79. package/dist/components/Modal/Modal.svelte.d.ts +7 -0
  80. package/dist/components/Modal/ModalBody.svelte +50 -0
  81. package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
  82. package/dist/components/Modal/ModalFooter.svelte +37 -0
  83. package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
  84. package/dist/components/Modal/ModalHeader.svelte +73 -0
  85. package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
  86. package/dist/components/Modal/index.d.ts +4 -0
  87. package/dist/components/Modal/index.js +4 -0
  88. package/dist/components/Popover/Popover.svelte +14 -0
  89. package/dist/components/Popover/Popover.svelte.d.ts +7 -0
  90. package/dist/components/Popover/PopoverContent.svelte +63 -0
  91. package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
  92. package/dist/components/Popover/PopoverTrigger.svelte +14 -0
  93. package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
  94. package/dist/components/Popover/index.d.ts +3 -0
  95. package/dist/components/Popover/index.js +3 -0
  96. package/dist/components/Progress/ProgressBar.svelte +86 -0
  97. package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
  98. package/dist/components/Progress/ProgressCircle.svelte +134 -0
  99. package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
  100. package/dist/components/Progress/Spinner.svelte +68 -0
  101. package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
  102. package/dist/components/Progress/index.d.ts +3 -0
  103. package/dist/components/Progress/index.js +3 -0
  104. package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
  105. package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
  106. package/dist/components/PullToRefresh/index.d.ts +1 -0
  107. package/dist/components/PullToRefresh/index.js +1 -0
  108. package/dist/components/SafeArea/SafeArea.svelte +33 -0
  109. package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
  110. package/dist/components/Select/Select.svelte +55 -12
  111. package/dist/components/Skeleton/Skeleton.svelte +59 -0
  112. package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
  113. package/dist/components/Skeleton/index.d.ts +1 -0
  114. package/dist/components/Skeleton/index.js +1 -0
  115. package/dist/components/Spacer/Spacer.svelte +56 -0
  116. package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
  117. package/dist/components/Spacer/index.d.ts +1 -0
  118. package/dist/components/Spacer/index.js +1 -0
  119. package/dist/components/Stack/Stack.svelte +117 -0
  120. package/dist/components/Stack/Stack.svelte.d.ts +13 -0
  121. package/dist/components/Stack/index.d.ts +1 -0
  122. package/dist/components/Stack/index.js +1 -0
  123. package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
  124. package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
  125. package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
  126. package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
  127. package/dist/components/SwipeActions/index.d.ts +2 -0
  128. package/dist/components/SwipeActions/index.js +2 -0
  129. package/dist/components/Switch/Switch.svelte +29 -9
  130. package/dist/components/Table/Table.svelte +175 -0
  131. package/dist/components/Table/Table.svelte.d.ts +38 -0
  132. package/dist/components/Table/TableBody.svelte +26 -0
  133. package/dist/components/Table/TableBody.svelte.d.ts +13 -0
  134. package/dist/components/Table/TableCell.svelte +85 -0
  135. package/dist/components/Table/TableCell.svelte.d.ts +28 -0
  136. package/dist/components/Table/TableHead.svelte +36 -0
  137. package/dist/components/Table/TableHead.svelte.d.ts +13 -0
  138. package/dist/components/Table/TableHeader.svelte +217 -0
  139. package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
  140. package/dist/components/Table/TableRow.svelte +92 -0
  141. package/dist/components/Table/TableRow.svelte.d.ts +28 -0
  142. package/dist/components/Table/index.d.ts +6 -0
  143. package/dist/components/Table/index.js +6 -0
  144. package/dist/components/Tag/Tag.svelte +189 -0
  145. package/dist/components/Tag/Tag.svelte.d.ts +13 -0
  146. package/dist/components/Tag/index.d.ts +1 -0
  147. package/dist/components/Tag/index.js +1 -0
  148. package/dist/components/Toast/Toast.svelte +241 -0
  149. package/dist/components/Toast/Toast.svelte.d.ts +18 -0
  150. package/dist/components/Toast/ToastContainer.svelte +110 -0
  151. package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
  152. package/dist/components/Toast/index.d.ts +3 -0
  153. package/dist/components/Toast/index.js +3 -0
  154. package/dist/components/Toast/toast.d.ts +13 -0
  155. package/dist/components/Toast/toast.js +55 -0
  156. package/dist/components/Tooltip/Tooltip.svelte +71 -0
  157. package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
  158. package/dist/components/Tooltip/index.d.ts +2 -0
  159. package/dist/components/Tooltip/index.js +1 -0
  160. package/dist/index.d.ts +29 -1
  161. package/dist/index.js +32 -0
  162. package/dist/styles/tokens.css +5 -0
  163. package/dist/types/data-display.d.ts +93 -0
  164. package/dist/types/data-display.js +1 -0
  165. package/dist/types/feedback.d.ts +92 -0
  166. package/dist/types/feedback.js +1 -0
  167. package/dist/types/index.d.ts +4 -0
  168. package/dist/types/layout.d.ts +57 -0
  169. package/dist/types/layout.js +1 -0
  170. package/dist/types/mobile.d.ts +91 -0
  171. package/dist/types/mobile.js +1 -0
  172. package/dist/utils/gestures.d.ts +219 -0
  173. package/dist/utils/gestures.js +492 -0
  174. package/dist/utils/haptics.d.ts +89 -0
  175. package/dist/utils/haptics.js +198 -0
  176. package/dist/utils/platform.d.ts +47 -0
  177. package/dist/utils/platform.js +156 -0
  178. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,438 @@
1
+ # @aspect-ops/exon-ui
2
+
3
+ A modern, accessible UI component library for **Svelte 5** with first-class support for **Capacitor** mobile apps.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@aspect-ops/exon-ui.svg)](https://www.npmjs.com/package/@aspect-ops/exon-ui)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - **Svelte 5** - Built with runes and modern Svelte patterns
11
+ - **Mobile-first** - Designed for Capacitor native app conversion
12
+ - **Accessible** - WCAG 2.1 compliant with proper ARIA attributes
13
+ - **Themeable** - CSS custom properties for light/dark themes
14
+ - **Tree-shakeable** - Import only what you need
15
+ - **TypeScript** - Full type definitions included
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @aspect-ops/exon-ui
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```svelte
26
+ <script>
27
+ import { Button, Typography } from '@aspect-ops/exon-ui';
28
+ import '@aspect-ops/exon-ui/styles';
29
+ </script>
30
+
31
+ <Typography variant="h1">Hello World</Typography>
32
+ <Button variant="primary">Click Me</Button>
33
+ ```
34
+
35
+ ## Components
36
+
37
+ ### Core Components
38
+
39
+ | Component | Description |
40
+ | ------------ | --------------------------------------------- |
41
+ | `Button` | Buttons with variants, sizes, loading states |
42
+ | `Typography` | Semantic text with heading and body variants |
43
+ | `Icon` | SVG icon component with size options |
44
+ | `Badge` | Status badges with color variants |
45
+ | `Link` | Accessible links with external link detection |
46
+
47
+ ### Form Components
48
+
49
+ | Component | Description |
50
+ | --------------- | ---------------------------------------- |
51
+ | `TextInput` | Text input with validation states |
52
+ | `Textarea` | Multi-line input with auto-resize |
53
+ | `Select` | Dropdown select with keyboard navigation |
54
+ | `Checkbox` | Single checkbox with indeterminate state |
55
+ | `CheckboxGroup` | Group of checkboxes with shared state |
56
+ | `Radio` | Single radio button |
57
+ | `RadioGroup` | Radio button group with orientation |
58
+ | `Switch` | Toggle switch component |
59
+ | `FormField` | Label wrapper with helper/error text |
60
+
61
+ ### Navigation Components
62
+
63
+ | Component | Description |
64
+ | ------------------------------------------------ | ---------------------------------- |
65
+ | `Tabs`, `TabList`, `TabTrigger`, `TabContent` | Accessible tabs with Bits UI |
66
+ | `Menu`, `MenuTrigger`, `MenuContent`, `MenuItem` | Dropdown menus with submenus |
67
+ | `Breadcrumbs`, `BreadcrumbItem` | Navigation breadcrumbs |
68
+ | `BottomNav`, `BottomNavItem` | Mobile bottom navigation |
69
+ | `Navbar`, `NavItem` | Responsive header with mobile menu |
70
+ | `Sidebar`, `SidebarItem`, `SidebarGroup` | Collapsible sidebar navigation |
71
+
72
+ ## Usage Examples
73
+
74
+ ### Button
75
+
76
+ ```svelte
77
+ <script>
78
+ import { Button } from '@aspect-ops/exon-ui';
79
+ </script>
80
+
81
+ <!-- Variants -->
82
+ <Button variant="primary">Primary</Button>
83
+ <Button variant="secondary">Secondary</Button>
84
+ <Button variant="outline">Outline</Button>
85
+ <Button variant="ghost">Ghost</Button>
86
+ <Button variant="destructive">Delete</Button>
87
+
88
+ <!-- Sizes -->
89
+ <Button size="sm">Small</Button>
90
+ <Button size="md">Medium</Button>
91
+ <Button size="lg">Large</Button>
92
+
93
+ <!-- States -->
94
+ <Button loading>Loading...</Button>
95
+ <Button disabled>Disabled</Button>
96
+ <Button fullWidth>Full Width</Button>
97
+ ```
98
+
99
+ ### Typography
100
+
101
+ ```svelte
102
+ <script>
103
+ import { Typography } from '@aspect-ops/exon-ui';
104
+ </script>
105
+
106
+ <Typography variant="h1">Heading 1</Typography>
107
+ <Typography variant="h2">Heading 2</Typography>
108
+ <Typography variant="body">Body text</Typography>
109
+ <Typography variant="body-sm">Small text</Typography>
110
+ <Typography variant="caption">Caption text</Typography>
111
+ ```
112
+
113
+ ### Form Components
114
+
115
+ ```svelte
116
+ <script>
117
+ import { FormField, TextInput, Select, Checkbox, Switch } from '@aspect-ops/exon-ui';
118
+
119
+ let email = $state('');
120
+ let country = $state('');
121
+ let newsletter = $state(false);
122
+ let darkMode = $state(false);
123
+ </script>
124
+
125
+ <FormField label="Email" required helperText="We'll never share your email">
126
+ <TextInput type="email" bind:value={email} placeholder="you@example.com" />
127
+ </FormField>
128
+
129
+ <FormField label="Country">
130
+ <Select
131
+ bind:value={country}
132
+ options={[
133
+ { value: 'us', label: 'United States' },
134
+ { value: 'uk', label: 'United Kingdom' },
135
+ { value: 'in', label: 'India' }
136
+ ]}
137
+ />
138
+ </FormField>
139
+
140
+ <Checkbox bind:checked={newsletter}>
141
+ {#snippet children()}
142
+ Subscribe to newsletter
143
+ {/snippet}
144
+ </Checkbox>
145
+
146
+ <Switch bind:checked={darkMode}>
147
+ {#snippet children()}
148
+ Dark Mode
149
+ {/snippet}
150
+ </Switch>
151
+ ```
152
+
153
+ ### Tabs
154
+
155
+ ```svelte
156
+ <script>
157
+ import { Tabs, TabList, TabTrigger, TabContent } from '@aspect-ops/exon-ui';
158
+
159
+ let activeTab = $state('tab1');
160
+ </script>
161
+
162
+ <Tabs bind:value={activeTab}>
163
+ <TabList>
164
+ <TabTrigger value="tab1">Account</TabTrigger>
165
+ <TabTrigger value="tab2">Security</TabTrigger>
166
+ <TabTrigger value="tab3">Notifications</TabTrigger>
167
+ </TabList>
168
+
169
+ <TabContent value="tab1">
170
+ <p>Account settings content</p>
171
+ </TabContent>
172
+ <TabContent value="tab2">
173
+ <p>Security settings content</p>
174
+ </TabContent>
175
+ <TabContent value="tab3">
176
+ <p>Notification preferences</p>
177
+ </TabContent>
178
+ </Tabs>
179
+
180
+ <!-- Scrollable tabs -->
181
+ <Tabs>
182
+ <TabList scrollable>
183
+ {#each Array(10) as _, i}
184
+ <TabTrigger value="tab-{i}">Tab {i + 1}</TabTrigger>
185
+ {/each}
186
+ </TabList>
187
+ </Tabs>
188
+
189
+ <!-- Vertical tabs -->
190
+ <Tabs orientation="vertical">
191
+ <TabList>
192
+ <TabTrigger value="v1">Item 1</TabTrigger>
193
+ <TabTrigger value="v2">Item 2</TabTrigger>
194
+ </TabList>
195
+ </Tabs>
196
+ ```
197
+
198
+ ### Menu / Dropdown
199
+
200
+ ```svelte
201
+ <script>
202
+ import {
203
+ Menu,
204
+ MenuTrigger,
205
+ MenuContent,
206
+ MenuItem,
207
+ MenuSeparator,
208
+ MenuSub,
209
+ MenuSubTrigger,
210
+ MenuSubContent
211
+ } from '@aspect-ops/exon-ui';
212
+ </script>
213
+
214
+ <Menu>
215
+ <MenuTrigger>
216
+ <button>Open Menu</button>
217
+ </MenuTrigger>
218
+ <MenuContent>
219
+ <MenuItem icon="📝">Edit</MenuItem>
220
+ <MenuItem icon="📋">Copy</MenuItem>
221
+ <MenuSeparator />
222
+ <MenuSub>
223
+ <MenuSubTrigger>More Options</MenuSubTrigger>
224
+ <MenuSubContent>
225
+ <MenuItem>Option 1</MenuItem>
226
+ <MenuItem>Option 2</MenuItem>
227
+ </MenuSubContent>
228
+ </MenuSub>
229
+ <MenuSeparator />
230
+ <MenuItem icon="🗑️">Delete</MenuItem>
231
+ </MenuContent>
232
+ </Menu>
233
+ ```
234
+
235
+ ### Breadcrumbs
236
+
237
+ ```svelte
238
+ <script>
239
+ import { Breadcrumbs } from '@aspect-ops/exon-ui';
240
+
241
+ const items = [
242
+ { label: 'Home', href: '/' },
243
+ { label: 'Products', href: '/products' },
244
+ { label: 'Category', href: '/products/category' },
245
+ { label: 'Current Page' }
246
+ ];
247
+ </script>
248
+
249
+ <Breadcrumbs {items} />
250
+
251
+ <!-- Custom separator -->
252
+ <Breadcrumbs {items}>
253
+ {#snippet separator()}
254
+
255
+ {/snippet}
256
+ </Breadcrumbs>
257
+ ```
258
+
259
+ ### Bottom Navigation (Mobile)
260
+
261
+ ```svelte
262
+ <script>
263
+ import { BottomNav } from '@aspect-ops/exon-ui';
264
+
265
+ let activeIndex = $state(0);
266
+
267
+ const items = [
268
+ { label: 'Home', icon: '🏠' },
269
+ { label: 'Search', icon: '🔍' },
270
+ { label: 'Inbox', icon: '📬', badge: 5 },
271
+ { label: 'Profile', icon: '👤' }
272
+ ];
273
+ </script>
274
+
275
+ <BottomNav {items} {activeIndex} onchange={(index) => (activeIndex = index)} />
276
+ ```
277
+
278
+ ### Navbar
279
+
280
+ ```svelte
281
+ <script>
282
+ import { Navbar, NavItem, Button } from '@aspect-ops/exon-ui';
283
+ </script>
284
+
285
+ <Navbar>
286
+ {#snippet logo()}
287
+ <span>MyApp</span>
288
+ {/snippet}
289
+
290
+ <NavItem label="Home" href="/" active />
291
+ <NavItem label="Products" href="/products" />
292
+ <NavItem label="About" href="/about" />
293
+
294
+ {#snippet actions()}
295
+ <Button size="sm">Sign In</Button>
296
+ {/snippet}
297
+ </Navbar>
298
+ ```
299
+
300
+ ### Sidebar
301
+
302
+ ```svelte
303
+ <script>
304
+ import { Sidebar, SidebarItem, SidebarGroup } from '@aspect-ops/exon-ui';
305
+
306
+ let collapsed = $state(false);
307
+ </script>
308
+
309
+ <Sidebar bind:collapsed>
310
+ <SidebarGroup label="Main">
311
+ <SidebarItem icon="🏠" label="Dashboard" active />
312
+ <SidebarItem icon="📊" label="Analytics" badge={3} />
313
+ <SidebarItem icon="📁" label="Projects" />
314
+ </SidebarGroup>
315
+
316
+ <SidebarGroup label="Settings" collapsible>
317
+ <SidebarItem icon="👤" label="Profile" />
318
+ <SidebarItem icon="🔒" label="Security" />
319
+ </SidebarGroup>
320
+ </Sidebar>
321
+ ```
322
+
323
+ ## Theming
324
+
325
+ ### CSS Custom Properties
326
+
327
+ Import the default styles and customize with CSS variables:
328
+
329
+ ```svelte
330
+ <script>
331
+ import '@aspect-ops/exon-ui/styles';
332
+ </script>
333
+
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;
359
+ }
360
+ </style>
361
+ ```
362
+
363
+ ### Dark Mode
364
+
365
+ Toggle dark mode by setting `data-theme="dark"` on the html element:
366
+
367
+ ```svelte
368
+ <script>
369
+ function toggleTheme() {
370
+ const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
371
+ document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
372
+ }
373
+ </script>
374
+
375
+ <button onclick={toggleTheme}>Toggle Theme</button>
376
+ ```
377
+
378
+ Dark theme variables are automatically applied:
379
+
380
+ ```css
381
+ [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;
387
+ }
388
+ ```
389
+
390
+ ## Mobile / Capacitor Support
391
+
392
+ Components are designed with mobile-first principles:
393
+
394
+ - **44px minimum touch targets** for buttons and interactive elements
395
+ - **Safe area inset handling** for notched devices (iPhone, Android gesture bar)
396
+ - **Responsive breakpoints** for adaptive layouts
397
+ - **Hardware-accelerated animations** for smooth 60fps performance
398
+
399
+ ### Safe Area Example
400
+
401
+ ```svelte
402
+ <BottomNav {items} />
403
+ <!-- Automatically includes padding-bottom: env(safe-area-inset-bottom) -->
404
+ ```
405
+
406
+ ## TypeScript
407
+
408
+ All components include TypeScript definitions:
409
+
410
+ ```typescript
411
+ import type {
412
+ ButtonProps,
413
+ ButtonVariant,
414
+ TypographyProps,
415
+ TabsProps,
416
+ MenuItemProps
417
+ } from '@aspect-ops/exon-ui';
418
+ ```
419
+
420
+ ## Browser Support
421
+
422
+ - Chrome/Edge 88+
423
+ - Firefox 78+
424
+ - Safari 14+
425
+ - iOS Safari 14+
426
+ - Chrome for Android 88+
427
+
428
+ ## Contributing
429
+
430
+ Contributions are welcome! Please read our contributing guidelines before submitting a PR.
431
+
432
+ ## License
433
+
434
+ MIT License - see [LICENSE](LICENSE) for details.
435
+
436
+ ---
437
+
438
+ Built with Svelte 5 and Bits UI
@@ -0,0 +1,270 @@
1
+ <script lang="ts">
2
+ import type { ActionSheetProps } from '../../types/index.js';
3
+ import { Dialog } from 'bits-ui';
4
+
5
+ interface Props extends ActionSheetProps {
6
+ /** Bindable open state */
7
+ open?: boolean;
8
+ /** Slot for ActionSheetItem children */
9
+ actions?: import('svelte').Snippet;
10
+ /** Default slot for custom content */
11
+ children?: import('svelte').Snippet;
12
+ }
13
+
14
+ let {
15
+ open = $bindable(false),
16
+ title,
17
+ description,
18
+ cancelLabel = 'Cancel',
19
+ showCancel = true,
20
+ closeOnSelect = true,
21
+ class: className = '',
22
+ actions,
23
+ children
24
+ }: Props = $props();
25
+
26
+ function handleOpenChange(value: boolean) {
27
+ open = value;
28
+ }
29
+
30
+ function handleBackdropClick() {
31
+ open = false;
32
+ }
33
+
34
+ function handleCancel() {
35
+ open = false;
36
+ }
37
+
38
+ // Provide context for ActionSheetItem to access closeOnSelect
39
+ import { setContext } from 'svelte';
40
+
41
+ setContext('actionsheet', {
42
+ get closeOnSelect() {
43
+ return closeOnSelect;
44
+ },
45
+ close: () => {
46
+ open = false;
47
+ }
48
+ });
49
+ </script>
50
+
51
+ <Dialog.Root {open} onOpenChange={handleOpenChange}>
52
+ <Dialog.Portal>
53
+ <Dialog.Overlay class="actionsheet-overlay" onclick={handleBackdropClick} />
54
+ <Dialog.Content
55
+ class="actionsheet-content {className}"
56
+ aria-labelledby={title ? 'actionsheet-title' : undefined}
57
+ >
58
+ <!-- Header section (optional) -->
59
+ {#if title || description}
60
+ <div class="actionsheet-header">
61
+ {#if title}
62
+ <Dialog.Title id="actionsheet-title" class="actionsheet-title">{title}</Dialog.Title>
63
+ {/if}
64
+ {#if description}
65
+ <Dialog.Description class="actionsheet-description">{description}</Dialog.Description>
66
+ {/if}
67
+ </div>
68
+ {/if}
69
+
70
+ <!-- Actions section -->
71
+ <div class="actionsheet-actions" role="group">
72
+ {#if actions}
73
+ {@render actions()}
74
+ {/if}
75
+ {#if children}
76
+ {@render children()}
77
+ {/if}
78
+ </div>
79
+
80
+ <!-- Cancel button (separated) -->
81
+ {#if showCancel}
82
+ <div class="actionsheet-cancel-container">
83
+ <button
84
+ type="button"
85
+ class="actionsheet-cancel"
86
+ onclick={handleCancel}
87
+ aria-label="Cancel"
88
+ >
89
+ {cancelLabel}
90
+ </button>
91
+ </div>
92
+ {/if}
93
+ </Dialog.Content>
94
+ </Dialog.Portal>
95
+ </Dialog.Root>
96
+
97
+ <style>
98
+ /* Overlay */
99
+ :global(.actionsheet-overlay) {
100
+ position: fixed;
101
+ inset: 0;
102
+ background: rgba(0, 0, 0, 0.4);
103
+ z-index: 100;
104
+ }
105
+
106
+ :global(.actionsheet-overlay[data-state='open']) {
107
+ animation: actionsheet-fade-in 200ms ease-out;
108
+ }
109
+
110
+ :global(.actionsheet-overlay[data-state='closed']) {
111
+ animation: actionsheet-fade-out 150ms ease-in;
112
+ }
113
+
114
+ @keyframes -global-actionsheet-fade-in {
115
+ from {
116
+ opacity: 0;
117
+ }
118
+ to {
119
+ opacity: 1;
120
+ }
121
+ }
122
+
123
+ @keyframes -global-actionsheet-fade-out {
124
+ from {
125
+ opacity: 1;
126
+ }
127
+ to {
128
+ opacity: 0;
129
+ }
130
+ }
131
+
132
+ /* Content */
133
+ :global(.actionsheet-content) {
134
+ /* F33: Must set font-family explicitly - portal doesn't inherit */
135
+ font-family: var(--font-family, system-ui, -apple-system, sans-serif);
136
+ position: fixed;
137
+ bottom: 0;
138
+ left: 0;
139
+ right: 0;
140
+ z-index: 101;
141
+ display: flex;
142
+ flex-direction: column;
143
+ max-height: 90vh;
144
+ padding: var(--space-sm, 0.5rem);
145
+ padding-bottom: calc(var(--space-sm, 0.5rem) + env(safe-area-inset-bottom, 0px));
146
+ }
147
+
148
+ :global(.actionsheet-content[data-state='open']) {
149
+ animation: actionsheet-slide-up 250ms cubic-bezier(0.32, 0.72, 0, 1);
150
+ }
151
+
152
+ :global(.actionsheet-content[data-state='closed']) {
153
+ animation: actionsheet-slide-down 200ms cubic-bezier(0.32, 0.72, 0, 1);
154
+ }
155
+
156
+ @keyframes -global-actionsheet-slide-up {
157
+ from {
158
+ opacity: 0;
159
+ transform: translateY(100%);
160
+ }
161
+ to {
162
+ opacity: 1;
163
+ transform: translateY(0);
164
+ }
165
+ }
166
+
167
+ @keyframes -global-actionsheet-slide-down {
168
+ from {
169
+ opacity: 1;
170
+ transform: translateY(0);
171
+ }
172
+ to {
173
+ opacity: 0;
174
+ transform: translateY(100%);
175
+ }
176
+ }
177
+
178
+ /* Header */
179
+ :global(.actionsheet-header) {
180
+ text-align: center;
181
+ padding: var(--space-md, 1rem) var(--space-lg, 1.5rem);
182
+ background: var(--color-bg, #ffffff);
183
+ border-radius: var(--radius-lg, 0.75rem) var(--radius-lg, 0.75rem) 0 0;
184
+ border-bottom: 1px solid var(--color-border, rgba(0, 0, 0, 0.1));
185
+ }
186
+
187
+ :global(.actionsheet-title) {
188
+ margin: 0;
189
+ font-size: var(--text-sm, 0.875rem);
190
+ font-weight: 600;
191
+ color: var(--color-text-secondary, #6b7280);
192
+ }
193
+
194
+ :global(.actionsheet-description) {
195
+ margin: var(--space-xs, 0.25rem) 0 0;
196
+ font-size: var(--text-xs, 0.75rem);
197
+ color: var(--color-text-muted, #9ca3af);
198
+ }
199
+
200
+ /* Actions container */
201
+ :global(.actionsheet-actions) {
202
+ display: flex;
203
+ flex-direction: column;
204
+ background: var(--color-bg, #ffffff);
205
+ border-radius: var(--radius-lg, 0.75rem);
206
+ overflow: hidden;
207
+ }
208
+
209
+ /* When there's a header, remove top border radius from actions */
210
+ :global(.actionsheet-header + .actionsheet-actions) {
211
+ border-radius: 0 0 var(--radius-lg, 0.75rem) var(--radius-lg, 0.75rem);
212
+ }
213
+
214
+ /* Cancel button container */
215
+ :global(.actionsheet-cancel-container) {
216
+ margin-top: var(--space-sm, 0.5rem);
217
+ }
218
+
219
+ /* Cancel button - iOS style */
220
+ :global(.actionsheet-cancel) {
221
+ /* F33: Font inheritance for portal */
222
+ font-family: var(--font-family, system-ui, -apple-system, sans-serif);
223
+ width: 100%;
224
+ /* F20: Minimum touch target */
225
+ min-height: 56px;
226
+ padding: var(--space-md, 1rem);
227
+ border: none;
228
+ border-radius: var(--radius-lg, 0.75rem);
229
+ background: var(--color-bg, #ffffff);
230
+ color: var(--color-primary, #3b82f6);
231
+ font-size: var(--text-lg, 1.125rem);
232
+ font-weight: 600;
233
+ cursor: pointer;
234
+ transition: background-color var(--transition-fast, 150ms ease);
235
+ -webkit-tap-highlight-color: transparent;
236
+ }
237
+
238
+ :global(.actionsheet-cancel:hover) {
239
+ background: var(--color-bg-hover, #f9fafb);
240
+ }
241
+
242
+ :global(.actionsheet-cancel:active) {
243
+ background: var(--color-bg-active, #f3f4f6);
244
+ }
245
+
246
+ :global(.actionsheet-cancel:focus-visible) {
247
+ outline: 2px solid var(--color-primary, #3b82f6);
248
+ outline-offset: 2px;
249
+ }
250
+
251
+ /* Platform-specific styling */
252
+ :global([data-platform='ios']) :global(.actionsheet-content) {
253
+ padding: var(--space-sm, 0.5rem);
254
+ }
255
+
256
+ :global([data-platform='android']) :global(.actionsheet-content) {
257
+ padding: 0;
258
+ padding-bottom: env(safe-area-inset-bottom, 0px);
259
+ }
260
+
261
+ :global([data-platform='android']) :global(.actionsheet-actions),
262
+ :global([data-platform='android']) :global(.actionsheet-cancel) {
263
+ border-radius: 0;
264
+ }
265
+
266
+ :global([data-platform='android']) :global(.actionsheet-cancel-container) {
267
+ margin-top: 0;
268
+ border-top: 1px solid var(--color-border, rgba(0, 0, 0, 0.1));
269
+ }
270
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { ActionSheetProps } from '../../types/index.js';
2
+ interface Props extends ActionSheetProps {
3
+ /** Bindable open state */
4
+ open?: boolean;
5
+ /** Slot for ActionSheetItem children */
6
+ actions?: import('svelte').Snippet;
7
+ /** Default slot for custom content */
8
+ children?: import('svelte').Snippet;
9
+ }
10
+ declare const ActionSheet: import("svelte").Component<Props, {}, "open">;
11
+ type ActionSheet = ReturnType<typeof ActionSheet>;
12
+ export default ActionSheet;