@frosted-ui/react-native 0.0.1-canary.93 → 0.0.1-canary.95

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 (54) hide show
  1. package/README.md +18 -5
  2. package/dist/components/avatar.d.ts.map +1 -1
  3. package/dist/components/avatar.js +1 -0
  4. package/dist/components/avatar.js.map +1 -1
  5. package/dist/components/badge.d.ts.map +1 -1
  6. package/dist/components/badge.js +2 -0
  7. package/dist/components/badge.js.map +1 -1
  8. package/dist/components/button.js +1 -1
  9. package/dist/components/card.d.ts.map +1 -1
  10. package/dist/components/card.js +2 -1
  11. package/dist/components/card.js.map +1 -1
  12. package/dist/components/circular-progress.d.ts +21 -0
  13. package/dist/components/circular-progress.d.ts.map +1 -0
  14. package/dist/components/circular-progress.js +78 -0
  15. package/dist/components/circular-progress.js.map +1 -0
  16. package/dist/components/heading.d.ts +2 -2
  17. package/dist/components/heading.d.ts.map +1 -1
  18. package/dist/components/icon-button.js +1 -1
  19. package/dist/components/index.d.ts +4 -0
  20. package/dist/components/index.d.ts.map +1 -1
  21. package/dist/components/index.js +4 -0
  22. package/dist/components/index.js.map +1 -1
  23. package/dist/components/link.d.ts +19 -0
  24. package/dist/components/link.d.ts.map +1 -0
  25. package/dist/components/link.js +68 -0
  26. package/dist/components/link.js.map +1 -0
  27. package/dist/components/list.d.ts +37 -0
  28. package/dist/components/list.d.ts.map +1 -0
  29. package/dist/components/list.js +112 -0
  30. package/dist/components/list.js.map +1 -0
  31. package/dist/components/select.js +1 -1
  32. package/dist/components/separator.d.ts.map +1 -1
  33. package/dist/components/separator.js +2 -3
  34. package/dist/components/separator.js.map +1 -1
  35. package/dist/components/slider.d.ts +30 -0
  36. package/dist/components/slider.d.ts.map +1 -0
  37. package/dist/components/slider.js +248 -0
  38. package/dist/components/slider.js.map +1 -0
  39. package/dist/components/text-area.d.ts.map +1 -1
  40. package/dist/components/text-area.js +1 -1
  41. package/dist/components/text-area.js.map +1 -1
  42. package/dist/components/text-field.d.ts.map +1 -1
  43. package/dist/components/text-field.js +41 -3
  44. package/dist/components/text-field.js.map +1 -1
  45. package/dist/components/text.d.ts +2 -2
  46. package/dist/components/text.d.ts.map +1 -1
  47. package/dist/components/text.js +11 -2
  48. package/dist/components/text.js.map +1 -1
  49. package/docs/llm/COLOR_SYSTEM.md +799 -0
  50. package/docs/llm/COMPONENTS.md +1329 -0
  51. package/docs/llm/DESIGN_PATTERNS.md +2567 -0
  52. package/docs/llm/README.md +118 -0
  53. package/docs/llm/TYPOGRAPHY.md +516 -0
  54. package/package.json +9 -3
@@ -0,0 +1,2567 @@
1
+ # Frosted UI Design Patterns Guide
2
+
3
+ > **For Design Engineer AI Agents**: This guide covers UX patterns, layout composition, and visual design principles for building polished, user-friendly apps with Frosted UI.
4
+
5
+ ---
6
+
7
+ ## Core Design Principles
8
+
9
+ ### 1. Consistency Over Creativity
10
+
11
+ Use Frosted UI's built-in variants, sizes, and colors. Don't override component styles unless absolutely necessary. The design system exists to ensure visual consistency.
12
+
13
+ ### 2. Hierarchy Through Size & Weight
14
+
15
+ Establish clear visual hierarchy using:
16
+
17
+ - **Typography scale** (`size="1"` to `size="9"`) for text importance
18
+ - **Font weight** (`weight="bold"` for headings, `weight="medium"` for labels, `weight="regular"` for body)
19
+ - **Component sizes** — larger sizes draw more attention
20
+
21
+ ### 3. Color With Purpose
22
+
23
+ - Use **accent color** for interactive elements and primary actions
24
+ - Use **gray** for secondary/supporting UI
25
+ - Use **semantic colors** (`danger`, `warning`, `success`, `info`) only for status and feedback
26
+ - Don't use color decoratively — every color should communicate something
27
+
28
+ ### 4. Colored Sections
29
+
30
+ When creating a themed section (e.g., promotional banner, newsletter signup), use the palette's alpha shades for a cohesive look:
31
+
32
+ | Element | Token | Example |
33
+ | -------------- | ---------------------------- | ----------------------- |
34
+ | Background | `palette.a2` | Card/section background |
35
+ | Border | `palette.a5` | Subtle themed border |
36
+ | Text (body) | `palette.a11` | Body text, descriptions |
37
+ | Text (heading) | `palette.a12` | High-contrast headings |
38
+ | Form inputs | `variant="soft" color="..."` | Match the section color |
39
+
40
+ ### 5. Whitespace Is Your Friend
41
+
42
+ Generous spacing improves readability and touch targets. Use consistent gaps:
43
+
44
+ - `4px` — tight (related items)
45
+ - `8px` — standard (within groups)
46
+ - `12-16px` — comfortable (between groups)
47
+ - `24-32px` — sections
48
+
49
+ ---
50
+
51
+ ## Spacing Scale
52
+
53
+ Use these values for `gap`, `padding`, and `margin`:
54
+
55
+ | Value | Use Case |
56
+ | ----- | -------------------------------------------------- |
57
+ | `4` | Tight spacing: icon + text, badge content |
58
+ | `8` | Standard spacing: form fields, list items internal |
59
+ | `12` | Comfortable: between related groups |
60
+ | `16` | Section padding, card padding |
61
+ | `24` | Between distinct sections |
62
+ | `32` | Major section breaks, screen padding |
63
+
64
+ ---
65
+
66
+ ## Responsive Design
67
+
68
+ Apps built with Frosted UI should be mobile-first but work well on web/desktop. Choose the right layout strategy based on app complexity.
69
+
70
+ ### When to Use Each Layout Strategy
71
+
72
+ | App Type | Layout Strategy | Example |
73
+ | ----------------------------------- | ----------------- | ------------------------------- |
74
+ | Landing pages, forms, settings | **Centered** | Max-width container, centered |
75
+ | E-commerce, marketplace, dashboards | **Adaptive Grid** | Single column → multi-column |
76
+ | Chat, feed, detail views | **Centered** | Content-focused, easy reading |
77
+ | File browsers, admin panels | **Adaptive Grid** | Utilize full screen real estate |
78
+
79
+ ---
80
+
81
+ ### Strategy 1: Centered Layout (Simple Apps)
82
+
83
+ Best for: landing pages, forms, articles, settings, detail views.
84
+
85
+ ```tsx
86
+ import { useWindowDimensions } from 'react-native';
87
+
88
+ const MAX_CONTENT_WIDTH = 600;
89
+ const BREAKPOINT = 768;
90
+
91
+ function useResponsiveLayout() {
92
+ const { width } = useWindowDimensions();
93
+ const isWide = width >= BREAKPOINT;
94
+ const horizontalPadding = isWide ? Math.max(24, (width - MAX_CONTENT_WIDTH) / 2) : 16;
95
+
96
+ return { isWide, horizontalPadding, screenWidth: width };
97
+ }
98
+
99
+ function SimpleScreen() {
100
+ const { colors } = useThemeTokens();
101
+ const { horizontalPadding, isWide } = useResponsiveLayout();
102
+
103
+ return (
104
+ <ScrollView
105
+ style={{ flex: 1, backgroundColor: colors.background }}
106
+ contentContainerStyle={{
107
+ paddingHorizontal: horizontalPadding,
108
+ paddingVertical: 16,
109
+ gap: 24,
110
+ maxWidth: isWide ? MAX_CONTENT_WIDTH + horizontalPadding * 2 : undefined,
111
+ alignSelf: isWide ? 'center' : undefined,
112
+ width: '100%',
113
+ }}>
114
+ {/* Content */}
115
+ </ScrollView>
116
+ );
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ### Strategy 2: Adaptive Grid (Complex Apps)
123
+
124
+ Best for: e-commerce, marketplaces, dashboards, productivity apps, file browsers.
125
+
126
+ ```tsx
127
+ import { useWindowDimensions } from 'react-native';
128
+
129
+ // Breakpoints
130
+ const TABLET = 768;
131
+ const DESKTOP = 1024;
132
+ const WIDE = 1280;
133
+
134
+ function useAdaptiveLayout() {
135
+ const { width } = useWindowDimensions();
136
+
137
+ // Calculate columns based on screen width
138
+ const getColumns = (minItemWidth: number, maxColumns: number = 4) => {
139
+ const availableWidth = width - 32; // Account for padding
140
+ const columns = Math.floor(availableWidth / minItemWidth);
141
+ return Math.max(1, Math.min(columns, maxColumns));
142
+ };
143
+
144
+ return {
145
+ screenWidth: width,
146
+ isTablet: width >= TABLET,
147
+ isDesktop: width >= DESKTOP,
148
+ isWide: width >= WIDE,
149
+ getColumns,
150
+ padding: width >= TABLET ? 24 : 16,
151
+ };
152
+ }
153
+ ```
154
+
155
+ #### Product Grid Example
156
+
157
+ ```tsx
158
+ function ProductGridScreen() {
159
+ const { colors } = useThemeTokens();
160
+ const { getColumns, padding, isDesktop } = useAdaptiveLayout();
161
+
162
+ // Min 200px per item for comfortable cards, max 3 columns
163
+ const columns = getColumns(200, 3);
164
+ const gap = 16;
165
+
166
+ const products = [...]; // Your product data
167
+
168
+ return (
169
+ <ScrollView
170
+ style={{ flex: 1, backgroundColor: colors.background }}
171
+ contentContainerStyle={{
172
+ padding,
173
+ gap: 24,
174
+ maxWidth: isDesktop ? 1200 : undefined,
175
+ alignSelf: isDesktop ? 'center' : undefined,
176
+ width: '100%',
177
+ }}>
178
+ {/* Header */}
179
+ <View style={{ gap: 4 }}>
180
+ <Heading size="5">Products</Heading>
181
+ <Text color="gray">{products.length} items</Text>
182
+ </View>
183
+
184
+ {/* Responsive Grid */}
185
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap }}>
186
+ {products.map((product) => (
187
+ <View
188
+ key={product.id}
189
+ style={{
190
+ flexGrow: 1,
191
+ flexBasis: columns === 1 ? '100%' : `${Math.floor(100 / columns) - 2}%`,
192
+ maxWidth: columns === 1 ? '100%' : `${Math.floor(100 / columns) - 1}%`,
193
+ }}>
194
+ <ProductCard product={product} />
195
+ </View>
196
+ ))}
197
+ </View>
198
+ </ScrollView>
199
+ );
200
+ }
201
+ ```
202
+
203
+ #### Simpler Grid with Fixed Breakpoints
204
+
205
+ ```tsx
206
+ function SimpleGrid({ items, renderItem }) {
207
+ const { width } = useWindowDimensions();
208
+
209
+ // Simple breakpoint-based columns
210
+ const columns = width >= 1024 ? 3 : width >= 600 ? 2 : 1;
211
+ const gap = 16;
212
+
213
+ return (
214
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap }}>
215
+ {items.map((item, index) => (
216
+ <View
217
+ key={item.id ?? index}
218
+ style={{
219
+ flexGrow: 1,
220
+ flexBasis: columns === 1 ? '100%' : `${Math.floor(100 / columns) - 2}%`,
221
+ maxWidth: columns === 1 ? '100%' : `${Math.floor(100 / columns) - 1}%`,
222
+ }}>
223
+ {renderItem(item)}
224
+ </View>
225
+ ))}
226
+ </View>
227
+ );
228
+ }
229
+ ```
230
+
231
+ ---
232
+
233
+ ### Strategy 3: Hybrid Layout (Mixed Content)
234
+
235
+ Best for: screens with both full-width and constrained sections.
236
+
237
+ ```tsx
238
+ function HybridScreen() {
239
+ const { colors } = useThemeTokens();
240
+ const { isDesktop, padding, getColumns } = useAdaptiveLayout();
241
+
242
+ const maxContentWidth = 800;
243
+
244
+ return (
245
+ <ScrollView style={{ flex: 1, backgroundColor: colors.background }}>
246
+ {/* Full-width hero/banner */}
247
+ <View style={{ padding, backgroundColor: colors.palettes.accent.a2 }}>
248
+ <View
249
+ style={{
250
+ maxWidth: maxContentWidth,
251
+ alignSelf: 'center',
252
+ width: '100%',
253
+ }}>
254
+ <Heading size="6">Welcome Back</Heading>
255
+ <Text color="gray">Your dashboard overview</Text>
256
+ </View>
257
+ </View>
258
+
259
+ {/* Constrained content area */}
260
+ <View
261
+ style={{
262
+ padding,
263
+ gap: 24,
264
+ maxWidth: isDesktop ? 1200 : undefined,
265
+ alignSelf: 'center',
266
+ width: '100%',
267
+ }}>
268
+ {/* Stats Grid - adapts columns */}
269
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 12 }}>
270
+ {stats.map((stat) => (
271
+ <View key={stat.label} style={{ flex: 1, minWidth: 150 }}>
272
+ <StatCard {...stat} />
273
+ </View>
274
+ ))}
275
+ </View>
276
+
277
+ {/* Two-column layout on desktop */}
278
+ <View
279
+ style={{
280
+ flexDirection: isDesktop ? 'row' : 'column',
281
+ gap: 16,
282
+ }}>
283
+ <View style={{ flex: isDesktop ? 2 : 1 }}>
284
+ <Card>
285
+ <Heading size="4">Recent Activity</Heading>
286
+ {/* Activity list */}
287
+ </Card>
288
+ </View>
289
+ <View style={{ flex: 1 }}>
290
+ <Card>
291
+ <Heading size="4">Quick Actions</Heading>
292
+ {/* Actions */}
293
+ </Card>
294
+ </View>
295
+ </View>
296
+ </View>
297
+ </ScrollView>
298
+ );
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ ### Responsive Design Principles
305
+
306
+ | Principle | Mobile | Tablet (768px+) | Desktop (1024px+) |
307
+ | --------------------- | --------------- | --------------- | ----------------- |
308
+ | **Grid columns** | 1-2 | 2-3 | 3-4 |
309
+ | **Content max-width** | Full | Full or 800px | 1200px |
310
+ | **Side padding** | 16px | 24px | 24-48px |
311
+ | **Card arrangement** | Stacked | Side-by-side | Multi-column |
312
+ | **Touch targets** | 44px+ (size 3+) | Same | Same |
313
+ | **Component sizes** | Don't change | Don't change | Don't change |
314
+
315
+ ### Do's and Don'ts
316
+
317
+ **Do:**
318
+
319
+ - Use `flexWrap: 'wrap'` for responsive grids
320
+ - Set `minWidth` on grid items to control breakpoints
321
+ - Use `flex: 1` for equal-width columns
322
+ - Constrain max-width on very wide screens (1200-1400px)
323
+ - Keep consistent gap/padding at each breakpoint
324
+
325
+ **Don't:**
326
+
327
+ - Change button sizes based on screen width
328
+ - Use different font sizes for mobile vs desktop
329
+ - Create completely different layouts (keep hierarchy similar)
330
+ - Forget touch targets — desktop users may have touchscreens
331
+
332
+ ---
333
+
334
+ ## Layout Patterns
335
+
336
+ ### Screen Structure
337
+
338
+ ```tsx
339
+ <View style={{ flex: 1, backgroundColor: colors.background }}>
340
+ {/* Header */}
341
+ <View style={{ paddingHorizontal: 16, paddingVertical: 12 }}>
342
+ <Heading size="6">Screen Title</Heading>
343
+ </View>
344
+
345
+ {/* Content */}
346
+ <ScrollView contentContainerStyle={{ padding: 16, gap: 24 }}>{/* Sections go here */}</ScrollView>
347
+
348
+ {/* Footer (optional - for primary actions) */}
349
+ <View style={{ padding: 16, borderTopWidth: 1, borderTopColor: colors.stroke }}>
350
+ <Button variant="solid">
351
+ <Text>Primary Action</Text>
352
+ </Button>
353
+ </View>
354
+ </View>
355
+ ```
356
+
357
+ ### Section with Header
358
+
359
+ ```tsx
360
+ <View style={{ gap: 12 }}>
361
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
362
+ <Heading size="4">Section Title</Heading>
363
+ <Button variant="ghost" size="1">
364
+ <Text>See All</Text>
365
+ </Button>
366
+ </View>
367
+ <Card>{/* Section content */}</Card>
368
+ </View>
369
+ ```
370
+
371
+ ### Title + Description Pattern
372
+
373
+ Use this pattern for headings with supporting text:
374
+
375
+ ```tsx
376
+ <View style={{ gap: 4 }}>
377
+ <Heading size="5">Welcome back</Heading>
378
+ <Text size="3" color="gray">
379
+ Here's what's happening today
380
+ </Text>
381
+ </View>
382
+ ```
383
+
384
+ ### Form Field Pattern
385
+
386
+ ```tsx
387
+ <View style={{ gap: 6 }}>
388
+ <Label nativeID="field-id">Field Label</Label>
389
+ <TextField.Input placeholder="Placeholder..." aria-labelledby="field-id" />
390
+ <Text size="1" color="gray">
391
+ Helper text explaining the field
392
+ </Text>
393
+ </View>
394
+ ```
395
+
396
+ ### Form Section Pattern
397
+
398
+ Group related fields together:
399
+
400
+ ```tsx
401
+ <View style={{ gap: 16 }}>
402
+ <Heading size="3">Personal Information</Heading>
403
+
404
+ <View style={{ gap: 12 }}>
405
+ {/* First Name */}
406
+ <View style={{ gap: 6 }}>
407
+ <Label nativeID="first-name">First Name</Label>
408
+ <TextField.Input placeholder="John" aria-labelledby="first-name" />
409
+ </View>
410
+
411
+ {/* Last Name */}
412
+ <View style={{ gap: 6 }}>
413
+ <Label nativeID="last-name">Last Name</Label>
414
+ <TextField.Input placeholder="Doe" aria-labelledby="last-name" />
415
+ </View>
416
+
417
+ {/* Email */}
418
+ <View style={{ gap: 6 }}>
419
+ <Label nativeID="email">Email</Label>
420
+ <TextField.Input
421
+ placeholder="john@example.com"
422
+ aria-labelledby="email"
423
+ keyboardType="email-address"
424
+ />
425
+ </View>
426
+ </View>
427
+ </View>
428
+ ```
429
+
430
+ ---
431
+
432
+ ## List Patterns
433
+
434
+ > **Use the `List` component** for structured lists with items, slots, and separators. It renders a `Card` internally with proper padding and handles press states automatically.
435
+
436
+ ### Basic List
437
+
438
+ ```tsx
439
+ <List.Root>
440
+ {users.map((user, index) => (
441
+ <React.Fragment key={user.id}>
442
+ {index > 0 && <List.Separator />}
443
+ <List.Item onPress={() => {}}>
444
+ <List.ItemSlot>
445
+ <Avatar fallback={user.name} size="3" />
446
+ </List.ItemSlot>
447
+ <List.ItemContent>
448
+ <List.ItemTitle>{user.name}</List.ItemTitle>
449
+ <List.ItemDescription>{user.email}</List.ItemDescription>
450
+ </List.ItemContent>
451
+ <List.ItemSlot>
452
+ <Badge color={user.status === 'Active' ? 'success' : 'warning'} size="1">
453
+ <Text>{user.status}</Text>
454
+ </Badge>
455
+ </List.ItemSlot>
456
+ <List.ItemSlot>
457
+ <Icon as={ChevronRight} size={16} color={colors.palettes.gray.a8} />
458
+ </List.ItemSlot>
459
+ </List.Item>
460
+ </React.Fragment>
461
+ ))}
462
+ </List.Root>
463
+ ```
464
+
465
+ ### Settings List
466
+
467
+ ```tsx
468
+ <List.Root>
469
+ {/* Switch setting */}
470
+ <List.Item>
471
+ <List.ItemSlot>
472
+ <View style={iconBoxStyle}>
473
+ <Icon as={Bell} size={20} color={colors.palettes.blue.a11} />
474
+ </View>
475
+ </List.ItemSlot>
476
+ <List.ItemContent>
477
+ <List.ItemTitle>Notifications</List.ItemTitle>
478
+ </List.ItemContent>
479
+ <List.ItemSlot>
480
+ <Switch checked={enabled} onCheckedChange={setEnabled} />
481
+ </List.ItemSlot>
482
+ </List.Item>
483
+
484
+ <List.Separator />
485
+
486
+ {/* Pressable setting with checkbox */}
487
+ <List.Item onPress={() => setDarkMode(!darkMode)}>
488
+ <List.ItemSlot>
489
+ <View style={iconBoxStyle}>
490
+ <Icon as={Settings} size={20} color={colors.palettes.purple.a11} />
491
+ </View>
492
+ </List.ItemSlot>
493
+ <List.ItemContent>
494
+ <List.ItemTitle>Dark Mode</List.ItemTitle>
495
+ </List.ItemContent>
496
+ <List.ItemSlot>
497
+ <Checkbox checked={darkMode} onCheckedChange={setDarkMode} />
498
+ </List.ItemSlot>
499
+ </List.Item>
500
+ </List.Root>
501
+ ```
502
+
503
+ ### Settings with Slider Controls
504
+
505
+ For settings that require a value selection (volume, brightness, text size):
506
+
507
+ ```tsx
508
+ const [brightness, setBrightness] = React.useState(75);
509
+ const [volume, setVolume] = React.useState(80);
510
+
511
+ <Card>
512
+ <View style={{ gap: 20 }}>
513
+ <Heading size="3">Display Settings</Heading>
514
+
515
+ {/* Brightness */}
516
+ <View style={{ gap: 8 }}>
517
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
518
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
519
+ <Icon as={Sun} size={18} color={colors.palettes.amber.a11} />
520
+ <Text weight="medium">Brightness</Text>
521
+ </View>
522
+ <Text size="2" color="gray">
523
+ {brightness}%
524
+ </Text>
525
+ </View>
526
+ <Slider value={brightness} onValueChange={setBrightness} color="amber" />
527
+ </View>
528
+
529
+ <Separator size="4" />
530
+
531
+ {/* Volume */}
532
+ <View style={{ gap: 8 }}>
533
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
534
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
535
+ <Icon as={Volume2} size={18} color={colors.palettes.blue.a11} />
536
+ <Text weight="medium">Volume</Text>
537
+ </View>
538
+ <Text size="2" color="gray">
539
+ {volume}%
540
+ </Text>
541
+ </View>
542
+ <Slider value={volume} onValueChange={setVolume} color="blue" />
543
+ </View>
544
+ </View>
545
+ </Card>;
546
+ ```
547
+
548
+ **Key patterns:**
549
+
550
+ - Show current value as percentage or label on the right
551
+ - Use colored icons that match the slider's `color` prop
552
+ - Use `Separator` between slider groups
553
+ - Label + value row above, slider below
554
+
555
+ ### List with RadioGroup (Shipping Options)
556
+
557
+ ```tsx
558
+ <RadioGroup.Root value={selected} onValueChange={setSelected}>
559
+ <List.Root>
560
+ {options.map((option, index) => (
561
+ <React.Fragment key={option.id}>
562
+ {index > 0 && <List.Separator />}
563
+ <List.Item onPress={() => setSelected(option.id)}>
564
+ <List.ItemSlot>
565
+ <RadioGroup.Item value={option.id} />
566
+ </List.ItemSlot>
567
+ <List.ItemSlot>
568
+ <View style={iconBoxStyle}>
569
+ <Icon as={option.icon} size={20} />
570
+ </View>
571
+ </List.ItemSlot>
572
+ <List.ItemContent>
573
+ <List.ItemTitle>{option.name}</List.ItemTitle>
574
+ <List.ItemDescription>{option.time}</List.ItemDescription>
575
+ </List.ItemContent>
576
+ <List.ItemSlot>
577
+ <Text weight="medium" color={option.price === 'Free' ? 'success' : undefined}>
578
+ {option.price}
579
+ </Text>
580
+ </List.ItemSlot>
581
+ </List.Item>
582
+ </React.Fragment>
583
+ ))}
584
+ </List.Root>
585
+ </RadioGroup.Root>
586
+ ```
587
+
588
+ ### List Variants
589
+
590
+ Use the `variant` prop on `List.Root` to match different contexts:
591
+
592
+ ```tsx
593
+ // Default surface style (bordered, elevated)
594
+ <List.Root variant="surface">...</List.Root>
595
+
596
+ // Soft background for highlighted lists
597
+ <List.Root variant="soft">...</List.Root>
598
+
599
+ // Ghost for minimal style
600
+ <List.Root variant="ghost">...</List.Root>
601
+ ```
602
+
603
+ ### Leaderboard
604
+
605
+ ```tsx
606
+ <List.Root variant="soft">
607
+ {entries.map((entry, index) => (
608
+ <React.Fragment key={entry.rank}>
609
+ {index > 0 && <List.Separator />}
610
+ <List.Item style={entry.isUser ? { backgroundColor: colors.palettes.gray.a3 } : undefined}>
611
+ <List.ItemSlot>
612
+ <Text size="2" weight="bold" style={{ width: 24, textAlign: 'center' }}>
613
+ {entry.rank}
614
+ </Text>
615
+ </List.ItemSlot>
616
+ <List.ItemSlot>
617
+ <Avatar fallback={entry.avatar} size="2" color={entry.color} />
618
+ </List.ItemSlot>
619
+ <List.ItemContent>
620
+ <Text weight={entry.isUser ? 'bold' : 'medium'}>{entry.name}</Text>
621
+ </List.ItemContent>
622
+ <List.ItemSlot>
623
+ <Text weight="medium" color={entry.color}>
624
+ {entry.points.toLocaleString()} pts
625
+ </Text>
626
+ </List.ItemSlot>
627
+ </List.Item>
628
+ </React.Fragment>
629
+ ))}
630
+ </List.Root>
631
+ ```
632
+
633
+ ---
634
+
635
+ ## Card Patterns
636
+
637
+ ### Card Variants
638
+
639
+ Cards come in three variants. Choose based on visual weight needed:
640
+
641
+ | Variant | Visual Style | When to Use |
642
+ | --------- | --------------------------------------- | -------------------------------------------------------- |
643
+ | `surface` | Solid background, border, subtle shadow | **Default** — Elevated content like messages, profiles |
644
+ | `soft` | Translucent tinted background | Highlighted sections, tips, promotions, feature callouts |
645
+ | `ghost` | No background or border (just padding) | Section grouping, layout containers, minimal UI |
646
+
647
+ #### Surface — Message Card (default)
648
+
649
+ ```tsx
650
+ <Card variant="surface">
651
+ <View style={{ flexDirection: 'row', gap: 12 }}>
652
+ <Avatar fallback="SJ" color="blue" size="3" />
653
+ <View style={{ flex: 1, gap: 4 }}>
654
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
655
+ <Text weight="medium">Sarah Johnson</Text>
656
+ <Text size="1" color="gray">
657
+ 2m ago
658
+ </Text>
659
+ </View>
660
+ <Text size="3" color="gray">
661
+ Hey! Just finished the design review. The new dashboard looks amazing! 🎉
662
+ </Text>
663
+ </View>
664
+ </View>
665
+ </Card>
666
+ ```
667
+
668
+ #### Soft — Pro Tip / Feature Highlight
669
+
670
+ ```tsx
671
+ <Card variant="soft">
672
+ <View style={{ flexDirection: 'row', gap: 12, alignItems: 'flex-start' }}>
673
+ <View
674
+ style={{
675
+ width: 40,
676
+ height: 40,
677
+ borderRadius: 10,
678
+ backgroundColor: colors.palettes.amber.a3,
679
+ alignItems: 'center',
680
+ justifyContent: 'center',
681
+ }}>
682
+ <Icon as={Lightbulb} size={20} color={colors.palettes.amber.a11} />
683
+ </View>
684
+ <View style={{ flex: 1, gap: 4 }}>
685
+ <Text weight="medium">Pro Tip</Text>
686
+ <Text size="3" color="gray">
687
+ Enable notifications to stay updated on new messages and activity from your team.
688
+ </Text>
689
+ <Button variant="ghost" size="2" style={{ alignSelf: 'flex-start', marginTop: 4 }}>
690
+ <Text>Enable Notifications</Text>
691
+ <Icon as={ChevronRight} size={16} />
692
+ </Button>
693
+ </View>
694
+ </View>
695
+ </Card>
696
+ ```
697
+
698
+ #### Ghost — Section Container
699
+
700
+ Use `ghost` when you need layout grouping but no visual container:
701
+
702
+ ```tsx
703
+ <Card variant="ghost" style={{ padding: 0 }}>
704
+ <View style={{ gap: 12 }}>
705
+ {/* Section header */}
706
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
707
+ <Heading size="4">Recent Activity</Heading>
708
+ <Button variant="ghost" size="2">
709
+ <Text>See All</Text>
710
+ </Button>
711
+ </View>
712
+
713
+ {/* Content in a surface card (has overflow: hidden for separators) */}
714
+ <Card variant="surface" style={{ padding: 0 }}>
715
+ {items.map((item, index, arr) => (
716
+ <View key={index}>
717
+ <Pressable
718
+ style={({ pressed }) => ({
719
+ flexDirection: 'row',
720
+ alignItems: 'center',
721
+ gap: 12,
722
+ paddingHorizontal: 16,
723
+ paddingVertical: 14,
724
+ backgroundColor: pressed ? colors.palettes.gray.a3 : 'transparent',
725
+ })}>
726
+ <Avatar fallback={item.initials} size="3" color={item.color} />
727
+ <View style={{ flex: 1, gap: 2 }}>
728
+ <Text size="2" weight="medium" numberOfLines={1}>
729
+ {item.name}
730
+ </Text>
731
+ <Text size="2" color="gray" numberOfLines={1}>
732
+ {item.action}
733
+ </Text>
734
+ </View>
735
+ <Text size="1" color="gray">
736
+ {item.time}
737
+ </Text>
738
+ </Pressable>
739
+ {index < arr.length - 1 && <Separator size="4" />}
740
+ </View>
741
+ ))}
742
+ </Card>
743
+ </View>
744
+ </Card>
745
+ ```
746
+
747
+ > **Tip**: Card has `overflow: 'hidden'` by default, so full-width separators won't overflow the rounded corners.
748
+
749
+ ### Info Card
750
+
751
+ ```tsx
752
+ <Card>
753
+ <View style={{ gap: 12 }}>
754
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
755
+ <Icon as={Info} size={16} />
756
+ <Text weight="medium">Card Title</Text>
757
+ </View>
758
+ <Text size="3" color="gray">
759
+ Supporting description text that provides more context.
760
+ </Text>
761
+ <View style={{ flexDirection: 'row', gap: 8 }}>
762
+ <Button variant="soft" color="gray" size="2">
763
+ <Text>Dismiss</Text>
764
+ </Button>
765
+ <Button variant="solid" size="2">
766
+ <Text>Action</Text>
767
+ </Button>
768
+ </View>
769
+ </View>
770
+ </Card>
771
+ ```
772
+
773
+ ### Stat Card
774
+
775
+ ```tsx
776
+ <Card>
777
+ <View style={{ gap: 4 }}>
778
+ <Text size="1" color="gray">
779
+ Total Revenue
780
+ </Text>
781
+ <Heading size="6">$12,345</Heading>
782
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
783
+ <Badge color="success" size="1">
784
+ <Text>+12%</Text>
785
+ </Badge>
786
+ <Text size="1" color="gray">
787
+ vs last month
788
+ </Text>
789
+ </View>
790
+ </View>
791
+ </Card>
792
+ ```
793
+
794
+ ### Buy Box (E-commerce)
795
+
796
+ Use `size="4"` buttons for prominent CTAs in e-commerce and conversion-focused screens:
797
+
798
+ ```tsx
799
+ <Card>
800
+ <View style={{ gap: 16 }}>
801
+ {/* Product Image */}
802
+ <View
803
+ style={{
804
+ height: 200,
805
+ backgroundColor: colors.palettes.gray.a3,
806
+ borderRadius: 8,
807
+ alignItems: 'center',
808
+ justifyContent: 'center',
809
+ }}>
810
+ <Text color="gray">Product Image</Text>
811
+ </View>
812
+
813
+ {/* Product Info */}
814
+ <View style={{ gap: 8 }}>
815
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
816
+ <Badge color="success" size="1">
817
+ <Text>In Stock</Text>
818
+ </Badge>
819
+ <Badge variant="soft" color="gray" size="1">
820
+ <Text>Free Shipping</Text>
821
+ </Badge>
822
+ </View>
823
+ <Heading size="5">Premium Wireless Headphones</Heading>
824
+ <Text size="3" color="gray">
825
+ High-fidelity audio with active noise cancellation and 30-hour battery life.
826
+ </Text>
827
+ </View>
828
+
829
+ {/* Price */}
830
+ <View style={{ gap: 4 }}>
831
+ <View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 8 }}>
832
+ <Heading size="6">$299</Heading>
833
+ <Text size="2" color="gray" style={{ textDecorationLine: 'line-through' }}>
834
+ $349
835
+ </Text>
836
+ </View>
837
+ <Text size="1" color="success">
838
+ Save $50 (14% off)
839
+ </Text>
840
+ </View>
841
+
842
+ <Separator size="4" />
843
+
844
+ {/* CTA Buttons - Use size="4" for prominent actions */}
845
+ <View style={{ gap: 12 }}>
846
+ <Button variant="solid" size="4">
847
+ <Text>Add to Cart</Text>
848
+ </Button>
849
+ <Button variant="soft" color="gray" size="4">
850
+ <Icon as={Heart} size={18} />
851
+ <Text>Add to Wishlist</Text>
852
+ </Button>
853
+ </View>
854
+ </View>
855
+ </Card>
856
+ ```
857
+
858
+ > **Tip**: Use `size="4"` buttons for important conversion actions like "Add to Cart", "Buy Now", "Subscribe", or "Sign Up". The larger touch target and visual weight helps drive conversions.
859
+
860
+ ### Promotional Banner (Apple-like)
861
+
862
+ Create clean, premium promotional sections with structured layout:
863
+
864
+ ```tsx
865
+ <Card
866
+ style={{
867
+ padding: 0,
868
+ backgroundColor: colors.palettes.pink.a2,
869
+ borderWidth: 1,
870
+ borderColor: colors.palettes.pink.a4,
871
+ }}>
872
+ <View style={{ padding: 20, gap: 16 }}>
873
+ {/* Header row with badge + timer */}
874
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
875
+ <Badge color="pink" variant="soft" size="1">
876
+ <Icon as={Zap} size={10} />
877
+ <Text>Limited Time</Text>
878
+ </Badge>
879
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
880
+ <View
881
+ style={{
882
+ width: 6,
883
+ height: 6,
884
+ borderRadius: 3,
885
+ backgroundColor: colors.palettes.pink['9'],
886
+ }}
887
+ />
888
+ <Text size="1" weight="medium" style={{ color: colors.palettes.pink.a11 }}>
889
+ Ends in 02:34:56
890
+ </Text>
891
+ </View>
892
+ </View>
893
+
894
+ {/* Main content */}
895
+ <View style={{ gap: 4 }}>
896
+ <Text size="5" weight="bold" style={{ color: colors.palettes.pink.a12 }}>
897
+ Flash Sale
898
+ </Text>
899
+ <Text size="3" style={{ color: colors.palettes.pink.a11 }}>
900
+ Up to 50% off on selected items. Don't miss out.
901
+ </Text>
902
+ </View>
903
+
904
+ {/* CTA */}
905
+ <Button variant="solid" color="pink" size="3">
906
+ <Text>Shop Now</Text>
907
+ <Icon as={ChevronRight} size={16} />
908
+ </Button>
909
+ </View>
910
+ </Card>
911
+ ```
912
+
913
+ > **Key Pattern**: Use a consistent color palette (e.g., pink) across background (`a2`), border (`a4`), text (`a11`, `a12`), and button for a cohesive, premium feel.
914
+
915
+ ### Achievement Card (Apple-like)
916
+
917
+ For gamification elements like achievements, badges, or milestones:
918
+
919
+ ```tsx
920
+ <Card style={{ padding: 0, borderWidth: 1, borderColor: colors.stroke }}>
921
+ {/* Header section with award */}
922
+ <View
923
+ style={{
924
+ paddingVertical: 24,
925
+ paddingHorizontal: 20,
926
+ alignItems: 'center',
927
+ gap: 16,
928
+ backgroundColor: colors.palettes.gray.a2,
929
+ borderBottomWidth: 1,
930
+ borderBottomColor: colors.stroke,
931
+ }}>
932
+ {/* Centered badge with glow effect */}
933
+ <Badge
934
+ size="2"
935
+ color="amber"
936
+ variant="soft"
937
+ style={{
938
+ alignSelf: 'center',
939
+ shadowColor: colors.palettes.amber['9'],
940
+ shadowOffset: { width: 0, height: 0 },
941
+ shadowOpacity: 0.3,
942
+ shadowRadius: 12,
943
+ elevation: 4,
944
+ }}>
945
+ <Icon as={Award} size={14} />
946
+ <Text>First Purchase</Text>
947
+ </Badge>
948
+
949
+ <View style={{ gap: 4, alignItems: 'center' }}>
950
+ <Text size="4" weight="bold">
951
+ Achievement Unlocked!
952
+ </Text>
953
+ <Text size="2" color="gray" style={{ textAlign: 'center' }}>
954
+ You've made your first purchase and earned 100 bonus points.
955
+ </Text>
956
+ </View>
957
+ </View>
958
+
959
+ {/* Stats row */}
960
+ <View style={{ flexDirection: 'row', paddingVertical: 16, paddingHorizontal: 20 }}>
961
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
962
+ <Text size="4" weight="bold">
963
+ 100
964
+ </Text>
965
+ <Text size="1" color="gray">
966
+ Points Earned
967
+ </Text>
968
+ </View>
969
+ <View style={{ width: 1, backgroundColor: colors.stroke }} />
970
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
971
+ <Text size="4" weight="bold">
972
+ 3
973
+ </Text>
974
+ <Text size="1" color="gray">
975
+ Achievements
976
+ </Text>
977
+ </View>
978
+ <View style={{ width: 1, backgroundColor: colors.stroke }} />
979
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
980
+ <Text size="4" weight="bold">
981
+ Gold
982
+ </Text>
983
+ <Text size="1" color="gray">
984
+ Next Tier
985
+ </Text>
986
+ </View>
987
+ </View>
988
+ </Card>
989
+ ```
990
+
991
+ > **Key Pattern**: Use `colors.stroke` for subtle borders between sections. Center important elements using `alignSelf: 'center'`. Add shadow to badges for a "glow" effect.
992
+
993
+ ### Newsletter Signup (Themed)
994
+
995
+ Use the palette's alpha shades to create cohesive themed sections:
996
+
997
+ ```tsx
998
+ <Card
999
+ style={{
1000
+ backgroundColor: colors.palettes.accent.a2,
1001
+ borderWidth: 1,
1002
+ borderColor: colors.palettes.accent.a5,
1003
+ }}>
1004
+ <View style={{ gap: 16 }}>
1005
+ <View style={{ gap: 4 }}>
1006
+ <Text size="4" weight="bold" style={{ color: colors.palettes.accent.a12 }}>
1007
+ Stay in the loop
1008
+ </Text>
1009
+ <Text size="3" style={{ color: colors.palettes.accent.a11 }}>
1010
+ Get weekly updates on new features and tips.
1011
+ </Text>
1012
+ </View>
1013
+
1014
+ <TextField.Root variant="soft" color="accent">
1015
+ <TextField.Input placeholder="Enter your email" keyboardType="email-address" />
1016
+ </TextField.Root>
1017
+
1018
+ <Button variant="solid" size="3">
1019
+ <Text>Subscribe</Text>
1020
+ </Button>
1021
+
1022
+ <Text size="1" style={{ color: colors.palettes.accent.a11 }}>
1023
+ No spam, unsubscribe anytime.
1024
+ </Text>
1025
+ </View>
1026
+ </Card>
1027
+ ```
1028
+
1029
+ ---
1030
+
1031
+ ## Button Placement
1032
+
1033
+ ### Primary Action at Bottom
1034
+
1035
+ For screens with a clear primary action, place it at the bottom:
1036
+
1037
+ ```tsx
1038
+ <View style={{ flex: 1 }}>
1039
+ <ScrollView style={{ flex: 1 }}>{/* Content */}</ScrollView>
1040
+ <View style={{ padding: 16, gap: 8 }}>
1041
+ <Button variant="solid" size="3">
1042
+ <Text>Continue</Text>
1043
+ </Button>
1044
+ </View>
1045
+ </View>
1046
+ ```
1047
+
1048
+ ### Action Pairs
1049
+
1050
+ When you have two actions (primary + secondary):
1051
+
1052
+ ```tsx
1053
+ <View style={{ flexDirection: 'row', gap: 8 }}>
1054
+ <Button variant="soft" color="gray" style={{ flex: 1 }}>
1055
+ <Text>Cancel</Text>
1056
+ </Button>
1057
+ <Button variant="solid" style={{ flex: 1 }}>
1058
+ <Text>Confirm</Text>
1059
+ </Button>
1060
+ </View>
1061
+ ```
1062
+
1063
+ ### Inline Actions
1064
+
1065
+ For less prominent actions within content:
1066
+
1067
+ ```tsx
1068
+ <View style={{ flexDirection: 'row', gap: 8 }}>
1069
+ <Button variant="ghost" size="2">
1070
+ <Icon as={Heart} size={16} />
1071
+ <Text>Like</Text>
1072
+ </Button>
1073
+ <Button variant="ghost" size="2">
1074
+ <Icon as={MessageCircle} size={16} />
1075
+ <Text>Comment</Text>
1076
+ </Button>
1077
+ <Button variant="ghost" size="2">
1078
+ <Icon as={Share} size={16} />
1079
+ <Text>Share</Text>
1080
+ </Button>
1081
+ </View>
1082
+ ```
1083
+
1084
+ ---
1085
+
1086
+ ## Empty States
1087
+
1088
+ Always design for empty states:
1089
+
1090
+ ```tsx
1091
+ <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', padding: 32, gap: 16 }}>
1092
+ <View
1093
+ style={{
1094
+ width: 64,
1095
+ height: 64,
1096
+ borderRadius: 32,
1097
+ backgroundColor: colors.palettes.gray.a3,
1098
+ alignItems: 'center',
1099
+ justifyContent: 'center',
1100
+ }}>
1101
+ <Icon as={Inbox} size={32} color={colors.palettes.gray.a11} />
1102
+ </View>
1103
+ <View style={{ gap: 4, alignItems: 'center' }}>
1104
+ <Heading size="4">No messages yet</Heading>
1105
+ <Text size="3" color="gray" style={{ textAlign: 'center' }}>
1106
+ When you receive messages, they'll appear here
1107
+ </Text>
1108
+ </View>
1109
+ <Button variant="solid">
1110
+ <Text>Start a conversation</Text>
1111
+ </Button>
1112
+ </View>
1113
+ ```
1114
+
1115
+ ---
1116
+
1117
+ ## Loading States
1118
+
1119
+ ### Skeleton Loading
1120
+
1121
+ Match skeleton dimensions to actual content:
1122
+
1123
+ ```tsx
1124
+ {
1125
+ isLoading ? (
1126
+ <View style={{ gap: 12 }}>
1127
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
1128
+ <Skeleton.Avatar size="2" />
1129
+ <View style={{ flex: 1, gap: 4 }}>
1130
+ <Skeleton.Text size="2" style={{ width: '60%' }} />
1131
+ <Skeleton.Text size="1" style={{ width: '40%' }} />
1132
+ </View>
1133
+ </View>
1134
+ </View>
1135
+ ) : (
1136
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
1137
+ <Avatar fallback={user.name} size="2" />
1138
+ <View style={{ gap: 4 }}>
1139
+ <Text weight="medium">{user.name}</Text>
1140
+ <Text size="1" color="gray">
1141
+ {user.email}
1142
+ </Text>
1143
+ </View>
1144
+ </View>
1145
+ );
1146
+ }
1147
+ ```
1148
+
1149
+ ### Button Loading
1150
+
1151
+ The Spinner component wraps content and automatically shows/hides based on the `loading` prop:
1152
+
1153
+ ```tsx
1154
+ <Button variant="solid" disabled={isLoading} onPress={handleSubmit}>
1155
+ <Spinner loading={isLoading} size="1">
1156
+ <Text>Submit</Text>
1157
+ </Spinner>
1158
+ </Button>
1159
+ ```
1160
+
1161
+ ### Page Loading
1162
+
1163
+ ```tsx
1164
+ <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
1165
+ <Spinner size="4" />
1166
+ </View>
1167
+ ```
1168
+
1169
+ ---
1170
+
1171
+ ## Feedback & Status
1172
+
1173
+ ### Success State
1174
+
1175
+ ```tsx
1176
+ <Callout.Root color="success">
1177
+ <Callout.Icon>
1178
+ <Icon as={CheckCircle} size={16} />
1179
+ </Callout.Icon>
1180
+ <Callout.Text>
1181
+ <Text>Your changes have been saved successfully.</Text>
1182
+ </Callout.Text>
1183
+ </Callout.Root>
1184
+ ```
1185
+
1186
+ ### Error State
1187
+
1188
+ ```tsx
1189
+ <Callout.Root color="danger">
1190
+ <Callout.Icon>
1191
+ <Icon as={AlertCircle} size={16} />
1192
+ </Callout.Icon>
1193
+ <Callout.Text>
1194
+ <Text>Something went wrong. Please try again.</Text>
1195
+ </Callout.Text>
1196
+ </Callout.Root>
1197
+ ```
1198
+
1199
+ ### Inline Validation Error
1200
+
1201
+ ```tsx
1202
+ <View style={{ gap: 6 }}>
1203
+ <Label nativeID="email">Email</Label>
1204
+ <TextField.Input
1205
+ placeholder="you@example.com"
1206
+ aria-labelledby="email"
1207
+ style={{ borderColor: colors.palettes.danger['7'] }}
1208
+ />
1209
+ <Text size="1" color="danger">
1210
+ Please enter a valid email address
1211
+ </Text>
1212
+ </View>
1213
+ ```
1214
+
1215
+ ---
1216
+
1217
+ ## Modal & Dialog Best Practices
1218
+
1219
+ ### Dialog Content Structure
1220
+
1221
+ ```tsx
1222
+ <Dialog.Content>
1223
+ {/* Title + Description */}
1224
+ <Dialog.Title>Confirm Action</Dialog.Title>
1225
+ <Dialog.Description>Are you sure you want to proceed?</Dialog.Description>
1226
+
1227
+ {/* Optional: Form or additional content */}
1228
+ <View style={{ gap: 12, marginVertical: 16 }}>{/* Content */}</View>
1229
+
1230
+ {/* Actions - always at bottom, right-aligned */}
1231
+ <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
1232
+ <Dialog.Close>
1233
+ <Button variant="soft" color="gray">
1234
+ <Text>Cancel</Text>
1235
+ </Button>
1236
+ </Dialog.Close>
1237
+ <Dialog.Close>
1238
+ <Button variant="solid">
1239
+ <Text>Confirm</Text>
1240
+ </Button>
1241
+ </Dialog.Close>
1242
+ </View>
1243
+ </Dialog.Content>
1244
+ ```
1245
+
1246
+ ### Destructive Dialog
1247
+
1248
+ ```tsx
1249
+ <AlertDialog.Content>
1250
+ <AlertDialog.Header>
1251
+ <AlertDialog.Title>Delete Account</AlertDialog.Title>
1252
+ <AlertDialog.Description>
1253
+ This will permanently delete your account and all associated data. This action cannot be
1254
+ undone.
1255
+ </AlertDialog.Description>
1256
+ </AlertDialog.Header>
1257
+ <AlertDialog.Footer>
1258
+ <AlertDialog.Cancel>
1259
+ <Button variant="soft" color="gray">
1260
+ <Text>Cancel</Text>
1261
+ </Button>
1262
+ </AlertDialog.Cancel>
1263
+ <AlertDialog.Action>
1264
+ <Button variant="solid" color="danger">
1265
+ <Text>Delete Account</Text>
1266
+ </Button>
1267
+ </AlertDialog.Action>
1268
+ </AlertDialog.Footer>
1269
+ </AlertDialog.Content>
1270
+ ```
1271
+
1272
+ ---
1273
+
1274
+ ## Navigation Patterns
1275
+
1276
+ ### Header with Back Button
1277
+
1278
+ ```tsx
1279
+ <View
1280
+ style={{
1281
+ flexDirection: 'row',
1282
+ alignItems: 'center',
1283
+ paddingHorizontal: 8,
1284
+ paddingVertical: 12,
1285
+ gap: 8,
1286
+ }}>
1287
+ <IconButton variant="ghost" onPress={goBack}>
1288
+ <Icon as={ArrowLeft} size={20} />
1289
+ </IconButton>
1290
+ <Heading size="4" style={{ flex: 1 }}>
1291
+ Page Title
1292
+ </Heading>
1293
+ <IconButton variant="ghost">
1294
+ <Icon as={MoreHorizontal} size={20} />
1295
+ </IconButton>
1296
+ </View>
1297
+ ```
1298
+
1299
+ ### Tab Navigation
1300
+
1301
+ ```tsx
1302
+ <Tabs.Root value={activeTab} onValueChange={setActiveTab}>
1303
+ <Tabs.List>
1304
+ <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
1305
+ <Tabs.Trigger value="activity">Activity</Tabs.Trigger>
1306
+ <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
1307
+ </Tabs.List>
1308
+
1309
+ <Tabs.Content value="overview">{/* Overview content */}</Tabs.Content>
1310
+ <Tabs.Content value="activity">{/* Activity content */}</Tabs.Content>
1311
+ <Tabs.Content value="settings">{/* Settings content */}</Tabs.Content>
1312
+ </Tabs.Root>
1313
+ ```
1314
+
1315
+ ### Segmented Control
1316
+
1317
+ For switching between mutually exclusive views:
1318
+
1319
+ ```tsx
1320
+ const [view, setView] = React.useState('list');
1321
+
1322
+ <SegmentedControl.Root value={view} onValueChange={setView}>
1323
+ <SegmentedControl.List>
1324
+ <SegmentedControl.Trigger value="list">List</SegmentedControl.Trigger>
1325
+ <SegmentedControl.Trigger value="grid">Grid</SegmentedControl.Trigger>
1326
+ <SegmentedControl.Trigger value="table">Table</SegmentedControl.Trigger>
1327
+ </SegmentedControl.List>
1328
+ </SegmentedControl.Root>;
1329
+ ```
1330
+
1331
+ ### Radio Group
1332
+
1333
+ For selecting one option from a list:
1334
+
1335
+ ```tsx
1336
+ const [selected, setSelected] = React.useState('option1');
1337
+
1338
+ <RadioGroup.Root value={selected} onValueChange={setSelected}>
1339
+ <View style={{ gap: 8 }}>
1340
+ <Pressable
1341
+ style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}
1342
+ onPress={() => setSelected('option1')}>
1343
+ <RadioGroup.Item value="option1" />
1344
+ <Text>Option 1</Text>
1345
+ </Pressable>
1346
+ <Pressable
1347
+ style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}
1348
+ onPress={() => setSelected('option2')}>
1349
+ <RadioGroup.Item value="option2" />
1350
+ <Text>Option 2</Text>
1351
+ </Pressable>
1352
+ </View>
1353
+ </RadioGroup.Root>;
1354
+ ```
1355
+
1356
+ ### Search Field
1357
+
1358
+ ```tsx
1359
+ <TextField.Root>
1360
+ <TextField.Slot>
1361
+ <Icon as={Search} size={16} />
1362
+ </TextField.Slot>
1363
+ <TextField.Input placeholder="Search..." />
1364
+ <TextField.Slot>
1365
+ <IconButton variant="ghost" size="1">
1366
+ <Icon as={X} size={14} />
1367
+ </IconButton>
1368
+ </TextField.Slot>
1369
+ </TextField.Root>
1370
+ ```
1371
+
1372
+ ---
1373
+
1374
+ ## Accessibility Checklist
1375
+
1376
+ - [ ] All interactive elements have sufficient touch targets (minimum 44×44px, use `size="3"` or larger)
1377
+ - [ ] Form inputs have associated `<Label>` with `nativeID` and `aria-labelledby`
1378
+ - [ ] Color is not the only way to convey information (add icons or text)
1379
+ - [ ] Text has sufficient contrast (Frosted UI handles this automatically)
1380
+ - [ ] Loading states are announced (use `aria-busy`)
1381
+ - [ ] Error messages are associated with inputs (use `aria-describedby`)
1382
+
1383
+ ---
1384
+
1385
+ ## Visual Polish Tips
1386
+
1387
+ ### 1. Align Everything
1388
+
1389
+ Use consistent padding and alignment. If your screen padding is 16px, all content should align to that grid.
1390
+
1391
+ ### 2. Group Related Items
1392
+
1393
+ Use `gap` to show relationships:
1394
+
1395
+ - Tighter gaps (4-8px) = closely related
1396
+ - Larger gaps (16-24px) = separate groups
1397
+
1398
+ ### 3. Use Cards to Elevate
1399
+
1400
+ Wrap distinct content sections in `<Card>` to create visual separation and hierarchy.
1401
+
1402
+ ### 4. Consistent Icon Sizes
1403
+
1404
+ - `14-16px` — inline with text, buttons
1405
+ - `18-20px` — list items, navigation
1406
+ - `24-32px` — feature icons, empty states
1407
+
1408
+ ### 5. Icon Colors
1409
+
1410
+ When icons are standalone (not inside Frosted UI components):
1411
+
1412
+ ```tsx
1413
+ // Gray icons (neutral)
1414
+ <Icon as={Settings} size={20} color={colors.palettes.gray.a11} />
1415
+
1416
+ // Colored icons (e.g., in icon boxes)
1417
+ <View style={{ backgroundColor: colors.palettes.blue.a3 }}>
1418
+ <Icon as={Bell} size={20} color={colors.palettes.blue.a11} />
1419
+ </View>
1420
+
1421
+ // Semantic icons
1422
+ <Icon as={AlertCircle} size={20} color={colors.palettes.danger['9']} />
1423
+ ```
1424
+
1425
+ > **Important**: Use `palette.a11` for colored icons, not `palette['9']`. The alpha shades adapt better to light/dark mode.
1426
+
1427
+ ### 5. Balance Whitespace
1428
+
1429
+ If something feels cramped, add padding. If something feels disconnected, reduce gaps. Trust your visual instincts.
1430
+
1431
+ ---
1432
+
1433
+ ## Store & Marketing Patterns
1434
+
1435
+ ### Pricing Tier
1436
+
1437
+ ```tsx
1438
+ <Card>
1439
+ <View style={{ gap: 16 }}>
1440
+ <Badge color="accent" size="1" style={{ alignSelf: 'flex-start' }}>
1441
+ <Text>MOST POPULAR</Text>
1442
+ </Badge>
1443
+
1444
+ <View style={{ gap: 4 }}>
1445
+ <Text size="3" weight="bold">
1446
+ Pro Plan
1447
+ </Text>
1448
+ <View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 4 }}>
1449
+ <Text size="7" weight="bold">
1450
+ $19
1451
+ </Text>
1452
+ <Text size="2" color="gray">
1453
+ /month
1454
+ </Text>
1455
+ </View>
1456
+ </View>
1457
+
1458
+ <Separator size="4" />
1459
+
1460
+ <View style={{ gap: 12 }}>
1461
+ {['Unlimited projects', 'Advanced analytics', 'Priority support', 'Custom integrations'].map(
1462
+ (feature) => (
1463
+ <View key={feature} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
1464
+ <View
1465
+ style={{
1466
+ width: 20,
1467
+ height: 20,
1468
+ borderRadius: 10,
1469
+ backgroundColor: colors.palettes.success.a3,
1470
+ alignItems: 'center',
1471
+ justifyContent: 'center',
1472
+ }}>
1473
+ <Icon as={Check} size={12} color={colors.palettes.success['9']} />
1474
+ </View>
1475
+ <Text size="2">{feature}</Text>
1476
+ </View>
1477
+ )
1478
+ )}
1479
+ </View>
1480
+
1481
+ <Button variant="solid" size="4">
1482
+ <Text>Get Started</Text>
1483
+ </Button>
1484
+ </View>
1485
+ </Card>
1486
+ ```
1487
+
1488
+ ### Testimonial
1489
+
1490
+ ```tsx
1491
+ <Card>
1492
+ <View style={{ gap: 16 }}>
1493
+ <Icon as={Quote} size={32} color={colors.palettes.gray.a6} />
1494
+
1495
+ <Text size="3" style={{ fontStyle: 'italic' }}>
1496
+ "This product has completely transformed how our team works. We've seen a 40% increase in
1497
+ productivity and the support team is incredibly responsive."
1498
+ </Text>
1499
+
1500
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
1501
+ {[1, 2, 3, 4, 5].map((star) => (
1502
+ <Icon
1503
+ key={star}
1504
+ as={Star}
1505
+ size={16}
1506
+ color={colors.palettes.amber['9']}
1507
+ fill={colors.palettes.amber['9']}
1508
+ />
1509
+ ))}
1510
+ </View>
1511
+
1512
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
1513
+ <Avatar fallback="JD" size="3" color="blue" />
1514
+ <View style={{ gap: 2 }}>
1515
+ <Text weight="medium">Jennifer Davis</Text>
1516
+ <Text size="1" color="gray">
1517
+ CTO at TechCorp
1518
+ </Text>
1519
+ </View>
1520
+ </View>
1521
+ </View>
1522
+ </Card>
1523
+ ```
1524
+
1525
+ ### Feature Showcase
1526
+
1527
+ ```tsx
1528
+ const features = [
1529
+ { icon: Zap, title: 'Lightning Fast', description: 'Sub-100ms response times', color: 'amber' },
1530
+ { icon: Users, title: 'Team Collaboration', description: 'Real-time sync', color: 'blue' },
1531
+ { icon: Sparkles, title: 'AI Powered', description: 'Smart suggestions', color: 'purple' },
1532
+ ];
1533
+
1534
+ <View style={{ gap: 12 }}>
1535
+ {features.map((feature) => (
1536
+ <Card key={feature.title} variant="soft">
1537
+ <View style={{ flexDirection: 'row', gap: 16 }}>
1538
+ <View
1539
+ style={{
1540
+ width: 48,
1541
+ height: 48,
1542
+ borderRadius: 12,
1543
+ backgroundColor: colors.palettes[feature.color].a3,
1544
+ alignItems: 'center',
1545
+ justifyContent: 'center',
1546
+ }}>
1547
+ <Icon as={feature.icon} size={24} color={colors.palettes[feature.color].a11} />
1548
+ </View>
1549
+ <View style={{ flex: 1, gap: 4 }}>
1550
+ <Text weight="medium">{feature.title}</Text>
1551
+ <Text size="2" color="gray">
1552
+ {feature.description}
1553
+ </Text>
1554
+ </View>
1555
+ </View>
1556
+ </Card>
1557
+ ))}
1558
+ </View>;
1559
+ ```
1560
+
1561
+ ### App Stats
1562
+
1563
+ ```tsx
1564
+ const stats = [
1565
+ { value: '10M+', label: 'Downloads', icon: Download },
1566
+ { value: '4.8★', label: 'Rating', icon: Star },
1567
+ { value: '#1', label: 'Top Charts', icon: Trophy },
1568
+ ];
1569
+
1570
+ <View style={{ flexDirection: 'row', gap: 12 }}>
1571
+ {stats.map((stat) => (
1572
+ <Card key={stat.label} style={{ flex: 1, alignItems: 'center' }}>
1573
+ <View style={{ alignItems: 'center', gap: 8 }}>
1574
+ <Icon as={stat.icon} size={24} color={colors.palettes.accent.a11} />
1575
+ <Text size="4" weight="bold">
1576
+ {stat.value}
1577
+ </Text>
1578
+ <Text size="1" color="gray">
1579
+ {stat.label}
1580
+ </Text>
1581
+ </View>
1582
+ </Card>
1583
+ ))}
1584
+ </View>;
1585
+ ```
1586
+
1587
+ ---
1588
+
1589
+ ## Apple-like Design Principles
1590
+
1591
+ When creating premium, polished interfaces, follow these principles:
1592
+
1593
+ ### 1. Structure Over Decoration
1594
+
1595
+ - Use clear sections with subtle borders (`colors.stroke`)
1596
+ - Separate header areas with different background shades (`gray.a2`)
1597
+ - Avoid gratuitous gradients or shadows
1598
+
1599
+ ### 2. Consistent Color Theming
1600
+
1601
+ When theming a section:
1602
+
1603
+ ```tsx
1604
+ // Use a single palette consistently
1605
+ backgroundColor: colors.palettes.pink.a2, // Subtle background
1606
+ borderColor: colors.palettes.pink.a4, // Border
1607
+ color: colors.palettes.pink.a11, // Body text
1608
+ color: colors.palettes.pink.a12, // Heading
1609
+ ```
1610
+
1611
+ ### 3. Subtle Emphasis
1612
+
1613
+ - Use `shadowRadius: 12` with `shadowOpacity: 0.3` for a soft "glow"
1614
+ - Add small indicator dots (6×6) for live/active states
1615
+ - Use `weight="medium"` more than `weight="bold"` for cleaner text
1616
+
1617
+ ### 4. Centered Focal Points
1618
+
1619
+ For achievements, promotions, or highlights:
1620
+
1621
+ ```tsx
1622
+ <Badge style={{ alignSelf: 'center' }}>
1623
+ <Icon as={Award} size={14} />
1624
+ <Text>Achievement Name</Text>
1625
+ </Badge>
1626
+ ```
1627
+
1628
+ ### 5. Stats Rows
1629
+
1630
+ Display multiple metrics in a clean horizontal layout:
1631
+
1632
+ ```tsx
1633
+ <View style={{ flexDirection: 'row' }}>
1634
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
1635
+ <Text size="4" weight="bold">
1636
+ 100
1637
+ </Text>
1638
+ <Text size="1" color="gray">
1639
+ Points
1640
+ </Text>
1641
+ </View>
1642
+ <View style={{ width: 1, backgroundColor: colors.stroke }} />
1643
+ {/* More stats... */}
1644
+ </View>
1645
+ ```
1646
+
1647
+ ---
1648
+
1649
+ ## Common Mistakes to Avoid
1650
+
1651
+ | Mistake | Better Approach |
1652
+ | --------------------------- | ------------------------------------------------------ |
1653
+ | Using many different colors | Stick to accent + gray + semantic colors |
1654
+ | Inconsistent spacing | Use the spacing scale (4, 8, 12, 16, 24, 32) |
1655
+ | Too many primary buttons | One `solid` button per view/section |
1656
+ | Overriding component styles | Use built-in variants and props |
1657
+ | Missing loading states | Always show feedback during async operations |
1658
+ | Missing empty states | Design what users see with no data |
1659
+ | Text without hierarchy | Use Heading for titles, Text with size/weight for body |
1660
+ | Cramped touch targets | Use `size="2"` or `size="3"` for interactive elements |
1661
+
1662
+ ---
1663
+
1664
+ ## Quick Reference: Common Compositions
1665
+
1666
+ | Pattern | Components Used |
1667
+ | ------------ | ------------------------------------------------- |
1668
+ | Page header | `<Heading>` + optional `<Text>` description |
1669
+ | Form field | `<Label>` + `<TextField.Input>` + helper `<Text>` |
1670
+ | List item | `<Avatar>` + `<Text>` stack + `<Badge>` or action |
1671
+ | Card action | `<Card>` + content + `<Button>` row |
1672
+ | Empty state | Icon + `<Heading>` + `<Text>` + `<Button>` |
1673
+ | Settings row | Label + `<Switch>` or `<Select>` |
1674
+ | Dialog | Title + Description + content + button row |
1675
+ | Feedback | `<Callout>` with semantic color |
1676
+
1677
+ ---
1678
+
1679
+ ## E-commerce Patterns
1680
+
1681
+ ### Product Card
1682
+
1683
+ ```tsx
1684
+ <Card style={{ padding: 0 }}>
1685
+ {/* Product Image */}
1686
+ <View style={{ height: 200, backgroundColor: colors.palettes.gray.a3 }} />
1687
+
1688
+ <View style={{ padding: 16, gap: 12 }}>
1689
+ {/* Category + Rating */}
1690
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
1691
+ <Badge variant="soft" color="gray" size="1">
1692
+ <Text>Electronics</Text>
1693
+ </Badge>
1694
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
1695
+ <Icon
1696
+ as={Star}
1697
+ size={14}
1698
+ color={colors.palettes.amber['9']}
1699
+ fill={colors.palettes.amber['9']}
1700
+ />
1701
+ <Text size="1" weight="medium">
1702
+ 4.8
1703
+ </Text>
1704
+ <Text size="1" color="gray">
1705
+ (128)
1706
+ </Text>
1707
+ </View>
1708
+ </View>
1709
+
1710
+ {/* Title */}
1711
+ <Text size="3" weight="medium" numberOfLines={2}>
1712
+ Wireless Noise-Cancelling Headphones
1713
+ </Text>
1714
+
1715
+ {/* Price */}
1716
+ <View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 8 }}>
1717
+ <Text size="4" weight="bold">
1718
+ $299
1719
+ </Text>
1720
+ <Text size="2" color="gray" style={{ textDecorationLine: 'line-through' }}>
1721
+ $349
1722
+ </Text>
1723
+ </View>
1724
+
1725
+ {/* Quick action */}
1726
+ <Button variant="solid" size="3">
1727
+ <Text>Add to Cart</Text>
1728
+ </Button>
1729
+ </View>
1730
+ </Card>
1731
+ ```
1732
+
1733
+ ### Cart Item
1734
+
1735
+ ```tsx
1736
+ <View
1737
+ style={{
1738
+ flexDirection: 'row',
1739
+ gap: 12,
1740
+ paddingVertical: 16,
1741
+ borderBottomWidth: 1,
1742
+ borderBottomColor: colors.stroke,
1743
+ }}>
1744
+ {/* Thumbnail */}
1745
+ <View
1746
+ style={{
1747
+ width: 80,
1748
+ height: 80,
1749
+ borderRadius: 8,
1750
+ backgroundColor: colors.palettes.gray.a3,
1751
+ }}
1752
+ />
1753
+
1754
+ {/* Details */}
1755
+ <View style={{ flex: 1, gap: 4 }}>
1756
+ <Text size="2" weight="medium" numberOfLines={2}>
1757
+ Premium Wireless Headphones
1758
+ </Text>
1759
+ <Text size="1" color="gray">
1760
+ Black · Qty: 1
1761
+ </Text>
1762
+ <Text size="3" weight="bold">
1763
+ $299
1764
+ </Text>
1765
+ </View>
1766
+
1767
+ {/* Remove button */}
1768
+ <IconButton variant="ghost" size="2" color="gray">
1769
+ <Icon as={Trash2} size={16} />
1770
+ </IconButton>
1771
+ </View>
1772
+ ```
1773
+
1774
+ ### Order Summary
1775
+
1776
+ ```tsx
1777
+ <Card>
1778
+ <View style={{ gap: 16 }}>
1779
+ <Heading size="4">Order Summary</Heading>
1780
+
1781
+ <View style={{ gap: 12 }}>
1782
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
1783
+ <Text color="gray">Subtotal (3 items)</Text>
1784
+ <Text weight="medium">$239.97</Text>
1785
+ </View>
1786
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
1787
+ <Text color="gray">Shipping</Text>
1788
+ <Text weight="medium" color="success">
1789
+ Free
1790
+ </Text>
1791
+ </View>
1792
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
1793
+ <Text color="gray">Tax</Text>
1794
+ <Text weight="medium">$19.20</Text>
1795
+ </View>
1796
+ </View>
1797
+
1798
+ <Separator size="4" />
1799
+
1800
+ {/* Discount Code */}
1801
+ <View style={{ flexDirection: 'row', gap: 8 }}>
1802
+ <View style={{ flex: 1 }}>
1803
+ <TextField.Input placeholder="Discount code" />
1804
+ </View>
1805
+ <Button variant="surface">
1806
+ <Text>Apply</Text>
1807
+ </Button>
1808
+ </View>
1809
+
1810
+ <Separator size="4" />
1811
+
1812
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
1813
+ <Text size="4" weight="bold">
1814
+ Total
1815
+ </Text>
1816
+ <Text size="5" weight="bold">
1817
+ $259.17
1818
+ </Text>
1819
+ </View>
1820
+
1821
+ <Button variant="solid" size="4">
1822
+ <Text>Checkout</Text>
1823
+ </Button>
1824
+ </View>
1825
+ </Card>
1826
+ ```
1827
+
1828
+ ### Shipping Options
1829
+
1830
+ Use `List` with `RadioGroup` for selection lists like shipping methods:
1831
+
1832
+ ```tsx
1833
+ const [selected, setSelected] = React.useState('standard');
1834
+
1835
+ const options = [
1836
+ {
1837
+ id: 'standard',
1838
+ name: 'Standard Shipping',
1839
+ price: 'Free',
1840
+ time: '5-7 business days',
1841
+ icon: Truck,
1842
+ },
1843
+ { id: 'express', name: 'Express Shipping', price: '$9.99', time: '2-3 business days', icon: Zap },
1844
+ { id: 'overnight', name: 'Overnight', price: '$24.99', time: 'Next business day', icon: Clock },
1845
+ ];
1846
+
1847
+ <RadioGroup.Root value={selected} onValueChange={setSelected}>
1848
+ <List.Root>
1849
+ {options.map((option, index) => (
1850
+ <React.Fragment key={option.id}>
1851
+ {index > 0 && <List.Separator />}
1852
+ <List.Item onPress={() => setSelected(option.id)}>
1853
+ <List.ItemSlot>
1854
+ <RadioGroup.Item value={option.id} />
1855
+ </List.ItemSlot>
1856
+ <List.ItemSlot>
1857
+ <View style={iconBoxStyle}>
1858
+ <Icon as={option.icon} size={20} />
1859
+ </View>
1860
+ </List.ItemSlot>
1861
+ <List.ItemContent>
1862
+ <List.ItemTitle>{option.name}</List.ItemTitle>
1863
+ <List.ItemDescription>{option.time}</List.ItemDescription>
1864
+ </List.ItemContent>
1865
+ <List.ItemSlot>
1866
+ <Text weight="medium" color={option.price === 'Free' ? 'success' : undefined}>
1867
+ {option.price}
1868
+ </Text>
1869
+ </List.ItemSlot>
1870
+ </List.Item>
1871
+ </React.Fragment>
1872
+ ))}
1873
+ </List.Root>
1874
+ </RadioGroup.Root>;
1875
+ ```
1876
+
1877
+ ### Payment Method
1878
+
1879
+ ```tsx
1880
+ const [selected, setSelected] = React.useState('visa');
1881
+
1882
+ <RadioGroup.Root value={selected} onValueChange={setSelected}>
1883
+ <List.Root>
1884
+ {[
1885
+ { id: 'visa', name: 'Visa', last4: '4242', expiry: '12/25' },
1886
+ { id: 'mastercard', name: 'Mastercard', last4: '8888', expiry: '03/26' },
1887
+ ].map((card, index) => (
1888
+ <React.Fragment key={card.id}>
1889
+ {index > 0 && <List.Separator />}
1890
+ <List.Item onPress={() => setSelected(card.id)}>
1891
+ <List.ItemSlot>
1892
+ <RadioGroup.Item value={card.id} />
1893
+ </List.ItemSlot>
1894
+ <List.ItemSlot>
1895
+ <View style={cardIconStyle}>
1896
+ <Icon as={CreditCard} size={20} color={colors.palettes.gray.a11} />
1897
+ </View>
1898
+ </List.ItemSlot>
1899
+ <List.ItemContent>
1900
+ <List.ItemTitle>
1901
+ {card.name} •••• {card.last4}
1902
+ </List.ItemTitle>
1903
+ <List.ItemDescription>Expires {card.expiry}</List.ItemDescription>
1904
+ </List.ItemContent>
1905
+ </List.Item>
1906
+ </React.Fragment>
1907
+ ))}
1908
+
1909
+ <List.Separator />
1910
+
1911
+ {/* Add new card option */}
1912
+ <List.Item onPress={() => {}}>
1913
+ <List.ItemSlot>
1914
+ <View style={addButtonStyle}>
1915
+ <Icon as={Plus} size={14} color={colors.palettes.accent.a11} />
1916
+ </View>
1917
+ </List.ItemSlot>
1918
+ <List.ItemContent>
1919
+ <Text weight="medium" color="accent">
1920
+ Add new card
1921
+ </Text>
1922
+ </List.ItemContent>
1923
+ </List.Item>
1924
+ </List.Root>
1925
+ </RadioGroup.Root>;
1926
+ ```
1927
+
1928
+ ### Order Status (Timeline)
1929
+
1930
+ ```tsx
1931
+ const steps = [
1932
+ { id: 'ordered', label: 'Ordered', date: 'Dec 15', completed: true },
1933
+ { id: 'shipped', label: 'Shipped', date: 'Dec 16', completed: true },
1934
+ { id: 'transit', label: 'In Transit', date: 'Dec 17', completed: true, active: true },
1935
+ { id: 'delivered', label: 'Delivered', date: 'Dec 19', completed: false },
1936
+ ];
1937
+
1938
+ <Card>
1939
+ <View style={{ gap: 16 }}>
1940
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
1941
+ <Heading size="4">Order Status</Heading>
1942
+ <Badge color="info" size="1">
1943
+ <Text>In Transit</Text>
1944
+ </Badge>
1945
+ </View>
1946
+
1947
+ <View style={{ gap: 0 }}>
1948
+ {steps.map((step, index) => (
1949
+ <View key={step.id} style={{ flexDirection: 'row', gap: 12 }}>
1950
+ {/* Timeline */}
1951
+ <View style={{ alignItems: 'center', width: 24 }}>
1952
+ <View
1953
+ style={{
1954
+ width: 24,
1955
+ height: 24,
1956
+ borderRadius: 12,
1957
+ backgroundColor: step.completed
1958
+ ? colors.palettes.success['9']
1959
+ : colors.palettes.gray.a4,
1960
+ alignItems: 'center',
1961
+ justifyContent: 'center',
1962
+ }}>
1963
+ {step.completed && <Icon as={Check} size={14} color="white" />}
1964
+ </View>
1965
+ {index < steps.length - 1 && (
1966
+ <View
1967
+ style={{
1968
+ width: 2,
1969
+ height: 32,
1970
+ backgroundColor: steps[index + 1].completed
1971
+ ? colors.palettes.success['9']
1972
+ : colors.palettes.gray.a4,
1973
+ }}
1974
+ />
1975
+ )}
1976
+ </View>
1977
+ {/* Content */}
1978
+ <View style={{ flex: 1, paddingBottom: index < steps.length - 1 ? 16 : 0 }}>
1979
+ <Text weight={step.active ? 'bold' : 'medium'}>{step.label}</Text>
1980
+ <Text size="1" color="gray">
1981
+ {step.date}
1982
+ </Text>
1983
+ </View>
1984
+ </View>
1985
+ ))}
1986
+ </View>
1987
+ </View>
1988
+ </Card>;
1989
+ ```
1990
+
1991
+ ### Product Review
1992
+
1993
+ ```tsx
1994
+ <Card>
1995
+ <View style={{ gap: 12 }}>
1996
+ <View
1997
+ style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start' }}>
1998
+ <View style={{ flexDirection: 'row', gap: 12 }}>
1999
+ <Avatar fallback="MJ" size="3" color="blue" />
2000
+ <View style={{ gap: 2 }}>
2001
+ <Text weight="medium">Michael Johnson</Text>
2002
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
2003
+ {[1, 2, 3, 4, 5].map((star) => (
2004
+ <Icon
2005
+ key={star}
2006
+ as={Star}
2007
+ size={12}
2008
+ color={star <= 5 ? colors.palettes.amber['9'] : colors.palettes.gray.a6}
2009
+ fill={star <= 5 ? colors.palettes.amber['9'] : 'transparent'}
2010
+ />
2011
+ ))}
2012
+ </View>
2013
+ </View>
2014
+ </View>
2015
+ <Text size="1" color="gray">
2016
+ 2 days ago
2017
+ </Text>
2018
+ </View>
2019
+
2020
+ <Text size="3" color="gray">
2021
+ Absolutely love these headphones! The noise cancellation is incredible and the battery life
2022
+ exceeds expectations. Highly recommend for anyone looking for premium audio quality.
2023
+ </Text>
2024
+
2025
+ <Button variant="ghost" size="2" style={{ alignSelf: 'flex-start' }}>
2026
+ <Icon as={ThumbsUp} size={14} />
2027
+ <Text>Helpful (24)</Text>
2028
+ </Button>
2029
+ </View>
2030
+ </Card>
2031
+ ```
2032
+
2033
+ ### Wishlist Item
2034
+
2035
+ ```tsx
2036
+ <Card style={{ padding: 0 }}>
2037
+ <View style={{ flexDirection: 'row', padding: 16, gap: 12 }}>
2038
+ <View
2039
+ style={{
2040
+ width: 80,
2041
+ height: 80,
2042
+ backgroundColor: colors.palettes.gray.a3,
2043
+ borderRadius: 8,
2044
+ alignItems: 'center',
2045
+ justifyContent: 'center',
2046
+ }}>
2047
+ <Icon as={ShoppingBag} size={32} color={colors.palettes.gray.a8} />
2048
+ </View>
2049
+ <View style={{ flex: 1, gap: 4 }}>
2050
+ <Text size="2" weight="medium" numberOfLines={2}>
2051
+ Premium Leather Wallet
2052
+ </Text>
2053
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
2054
+ {[1, 2, 3, 4, 5].map((star) => (
2055
+ <Icon
2056
+ key={star}
2057
+ as={Star}
2058
+ size={10}
2059
+ color={star <= 4 ? colors.palettes.amber['9'] : colors.palettes.gray.a6}
2060
+ fill={star <= 4 ? colors.palettes.amber['9'] : 'transparent'}
2061
+ />
2062
+ ))}
2063
+ <Text size="0" color="gray">
2064
+ (89)
2065
+ </Text>
2066
+ </View>
2067
+ <Text size="3" weight="bold">
2068
+ $49.99
2069
+ </Text>
2070
+ </View>
2071
+ </View>
2072
+ <Separator size="4" />
2073
+ <View style={{ flexDirection: 'row', paddingHorizontal: 16, paddingVertical: 12, gap: 8 }}>
2074
+ <Button variant="solid" size="2" style={{ flex: 1 }}>
2075
+ <Icon as={ShoppingCart} size={14} />
2076
+ <Text>Move to Cart</Text>
2077
+ </Button>
2078
+ <IconButton variant="surface" size="2" color="danger">
2079
+ <Icon as={Trash2} size={16} />
2080
+ </IconButton>
2081
+ </View>
2082
+ </Card>
2083
+ ```
2084
+
2085
+ ---
2086
+
2087
+ ## Profile & Social Patterns
2088
+
2089
+ ### Team Member Card
2090
+
2091
+ ```tsx
2092
+ <Card>
2093
+ <View style={{ alignItems: 'center', gap: 12 }}>
2094
+ <Avatar fallback="AK" size="7" color="blue" />
2095
+ <View style={{ alignItems: 'center', gap: 4 }}>
2096
+ <Text size="3" weight="bold">
2097
+ Alex Kim
2098
+ </Text>
2099
+ <Text size="2" color="gray">
2100
+ Senior Designer
2101
+ </Text>
2102
+ </View>
2103
+ <View style={{ flexDirection: 'row', gap: 8 }}>
2104
+ <IconButton variant="soft" size="2" color="gray">
2105
+ <Icon as={Twitter} size={16} />
2106
+ </IconButton>
2107
+ <IconButton variant="soft" size="2" color="gray">
2108
+ <Icon as={Linkedin} size={16} />
2109
+ </IconButton>
2110
+ <IconButton variant="soft" size="2" color="gray">
2111
+ <Icon as={Send} size={16} />
2112
+ </IconButton>
2113
+ </View>
2114
+ </View>
2115
+ </Card>
2116
+ ```
2117
+
2118
+ ### User Stats Row
2119
+
2120
+ ```tsx
2121
+ <View style={{ flexDirection: 'row' }}>
2122
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
2123
+ <Text size="5" weight="bold">
2124
+ 2.4k
2125
+ </Text>
2126
+ <Text size="1" color="gray">
2127
+ Followers
2128
+ </Text>
2129
+ </View>
2130
+ <View style={{ width: 1, backgroundColor: colors.stroke }} />
2131
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
2132
+ <Text size="5" weight="bold">
2133
+ 486
2134
+ </Text>
2135
+ <Text size="1" color="gray">
2136
+ Following
2137
+ </Text>
2138
+ </View>
2139
+ <View style={{ width: 1, backgroundColor: colors.stroke }} />
2140
+ <View style={{ flex: 1, alignItems: 'center', gap: 2 }}>
2141
+ <Text size="5" weight="bold">
2142
+ 12
2143
+ </Text>
2144
+ <Text size="1" color="gray">
2145
+ Projects
2146
+ </Text>
2147
+ </View>
2148
+ </View>
2149
+ ```
2150
+
2151
+ ### Social Post
2152
+
2153
+ ```tsx
2154
+ const [liked, setLiked] = React.useState(false);
2155
+ const [likes, setLikes] = React.useState(42);
2156
+
2157
+ <Card style={{ padding: 0 }}>
2158
+ {/* Header */}
2159
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, padding: 16 }}>
2160
+ <Avatar fallback="EW" size="3" color="purple" />
2161
+ <View style={{ flex: 1, gap: 2 }}>
2162
+ <Text weight="medium">Emma Wilson</Text>
2163
+ <Text size="1" color="gray">
2164
+ 2 hours ago
2165
+ </Text>
2166
+ </View>
2167
+ <IconButton variant="ghost" size="2">
2168
+ <Icon as={MoreHorizontal} size={18} />
2169
+ </IconButton>
2170
+ </View>
2171
+
2172
+ {/* Content */}
2173
+ <View style={{ paddingHorizontal: 16, paddingBottom: 12 }}>
2174
+ <Text size="3">
2175
+ Just finished my morning run! 🏃‍♀️ Nothing beats starting the day with some exercise.
2176
+ </Text>
2177
+ </View>
2178
+
2179
+ {/* Image placeholder */}
2180
+ <View
2181
+ style={{
2182
+ height: 200,
2183
+ backgroundColor: colors.palettes.gray.a3,
2184
+ alignItems: 'center',
2185
+ justifyContent: 'center',
2186
+ }}>
2187
+ <Icon as={MapPin} size={48} color={colors.palettes.gray.a8} />
2188
+ </View>
2189
+
2190
+ {/* Actions */}
2191
+ <View style={{ flexDirection: 'row', padding: 12, gap: 16 }}>
2192
+ <Button
2193
+ variant="ghost"
2194
+ size="2"
2195
+ color={liked ? 'danger' : 'gray'}
2196
+ onPress={() => {
2197
+ setLiked(!liked);
2198
+ setLikes(liked ? likes - 1 : likes + 1);
2199
+ }}>
2200
+ <Icon as={Heart} size={18} />
2201
+ <Text>{likes}</Text>
2202
+ </Button>
2203
+ <Button variant="ghost" size="2" color="gray">
2204
+ <Icon as={MessageCircle} size={18} />
2205
+ <Text>12</Text>
2206
+ </Button>
2207
+ <Button variant="ghost" size="2" color="gray">
2208
+ <Icon as={Share} size={18} />
2209
+ </Button>
2210
+ </View>
2211
+ </Card>;
2212
+ ```
2213
+
2214
+ ---
2215
+
2216
+ ## Gamification Patterns
2217
+
2218
+ ### Streak Counter
2219
+
2220
+ ```tsx
2221
+ <Card>
2222
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
2223
+ <View
2224
+ style={{
2225
+ width: 56,
2226
+ height: 56,
2227
+ borderRadius: 14,
2228
+ backgroundColor: colors.palettes.orange.a3,
2229
+ alignItems: 'center',
2230
+ justifyContent: 'center',
2231
+ }}>
2232
+ <Icon as={Flame} size={28} color={colors.palettes.orange['9']} />
2233
+ </View>
2234
+ <View style={{ flex: 1, gap: 4 }}>
2235
+ <View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 4 }}>
2236
+ <Text size="6" weight="bold">
2237
+ 7
2238
+ </Text>
2239
+ <Text size="3" weight="medium">
2240
+ Day Streak
2241
+ </Text>
2242
+ </View>
2243
+ <Text size="2" color="gray">
2244
+ Keep it up! You're on fire 🔥
2245
+ </Text>
2246
+ </View>
2247
+ </View>
2248
+ </Card>
2249
+ ```
2250
+
2251
+ ### Leaderboard
2252
+
2253
+ ```tsx
2254
+ const entries = [
2255
+ { rank: 1, name: 'Sarah Chen', points: 12450, avatar: 'SC', color: 'pink' },
2256
+ { rank: 2, name: 'Alex Kim', points: 11200, avatar: 'AK', color: 'blue' },
2257
+ { rank: 3, name: 'Jordan Lee', points: 10890, avatar: 'JL', color: 'green' },
2258
+ { rank: 4, name: 'You', points: 9540, avatar: 'ME', color: 'accent', isUser: true },
2259
+ ];
2260
+
2261
+ <List.Root variant="soft">
2262
+ {entries.map((entry, index) => (
2263
+ <React.Fragment key={entry.rank}>
2264
+ {index > 0 && <List.Separator />}
2265
+ <List.Item style={entry.isUser ? { backgroundColor: colors.palettes.gray.a3 } : undefined}>
2266
+ <List.ItemSlot>
2267
+ <Text
2268
+ size="2"
2269
+ weight="bold"
2270
+ style={{ width: 24, textAlign: 'center' }}
2271
+ color={entry.rank <= 3 ? undefined : 'gray'}>
2272
+ {entry.rank}
2273
+ </Text>
2274
+ </List.ItemSlot>
2275
+ <List.ItemSlot>
2276
+ <Avatar fallback={entry.avatar} size="2" color={entry.color} />
2277
+ </List.ItemSlot>
2278
+ <List.ItemContent>
2279
+ <Text weight={entry.isUser ? 'bold' : 'medium'}>{entry.name}</Text>
2280
+ </List.ItemContent>
2281
+ <List.ItemSlot>
2282
+ <Text weight="medium" color={entry.color}>
2283
+ {entry.points.toLocaleString()} pts
2284
+ </Text>
2285
+ </List.ItemSlot>
2286
+ </List.Item>
2287
+ </React.Fragment>
2288
+ ))}
2289
+ </List.Root>;
2290
+ ```
2291
+
2292
+ ### XP Progress
2293
+
2294
+ ```tsx
2295
+ <Card>
2296
+ <View style={{ gap: 12 }}>
2297
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
2298
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2299
+ <View
2300
+ style={{
2301
+ width: 32,
2302
+ height: 32,
2303
+ borderRadius: 8,
2304
+ backgroundColor: colors.palettes.purple['9'],
2305
+ alignItems: 'center',
2306
+ justifyContent: 'center',
2307
+ }}>
2308
+ <Text size="2" weight="bold" style={{ color: 'white' }}>
2309
+ 12
2310
+ </Text>
2311
+ </View>
2312
+ <Text weight="medium">Level 12</Text>
2313
+ </View>
2314
+ <Badge color="purple" size="1">
2315
+ <Icon as={Sparkles} size={10} />
2316
+ <Text>250 XP to go</Text>
2317
+ </Badge>
2318
+ </View>
2319
+ <Progress value={75} size="2" color="purple" />
2320
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
2321
+ <Text size="1" color="gray">
2322
+ 750 / 1,000 XP
2323
+ </Text>
2324
+ <Text size="1" color="gray">
2325
+ Next: Level 13
2326
+ </Text>
2327
+ </View>
2328
+ </View>
2329
+ </Card>
2330
+ ```
2331
+
2332
+ ### System Health (CircularProgress)
2333
+
2334
+ Use `CircularProgress` for dashboard-style metrics:
2335
+
2336
+ ```tsx
2337
+ <Card>
2338
+ <View style={{ gap: 16 }}>
2339
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
2340
+ <Heading size="3">System Health</Heading>
2341
+ <Badge color="success" size="1">
2342
+ <Text>All Systems Normal</Text>
2343
+ </Badge>
2344
+ </View>
2345
+
2346
+ {/* Circular progress indicators in a row */}
2347
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
2348
+ {[
2349
+ { label: 'CPU', value: 42, color: 'cyan' },
2350
+ { label: 'Memory', value: 68, color: 'violet' },
2351
+ { label: 'Disk', value: 85, color: 'orange' },
2352
+ { label: 'Network', value: 23, color: 'lime' },
2353
+ ].map((metric) => (
2354
+ <View key={metric.label} style={{ alignItems: 'center', gap: 8 }}>
2355
+ <View style={{ position: 'relative', alignItems: 'center', justifyContent: 'center' }}>
2356
+ <CircularProgress size="6" value={metric.value} color={metric.color} />
2357
+ <View style={{ position: 'absolute' }}>
2358
+ <Text size="1" weight="bold">{metric.value}</Text>
2359
+ </View>
2360
+ </View>
2361
+ <Text size="1" color="gray">{metric.label}</Text>
2362
+ </View>
2363
+ ))}
2364
+ </View>
2365
+ </View>
2366
+ </Card>
2367
+ ```
2368
+
2369
+ > **Tip**: Place text inside circular progress using absolute positioning. Use size `"5"` or larger when displaying values inside.
2370
+
2371
+ ### Daily Challenge
2372
+
2373
+ ```tsx
2374
+ <Card>
2375
+ <View style={{ gap: 12 }}>
2376
+ <View
2377
+ style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start' }}>
2378
+ <View style={{ flexDirection: 'row', gap: 12 }}>
2379
+ <View
2380
+ style={{
2381
+ width: 48,
2382
+ height: 48,
2383
+ borderRadius: 12,
2384
+ backgroundColor: colors.palettes.cyan.a3,
2385
+ alignItems: 'center',
2386
+ justifyContent: 'center',
2387
+ }}>
2388
+ <Icon as={Timer} size={24} color={colors.palettes.cyan.a11} />
2389
+ </View>
2390
+ <View style={{ gap: 2 }}>
2391
+ <Text size="1" color="gray" weight="medium">
2392
+ DAILY CHALLENGE
2393
+ </Text>
2394
+ <Text weight="medium">Complete 5 lessons</Text>
2395
+ </View>
2396
+ </View>
2397
+ <Badge color="amber" size="1">
2398
+ <Icon as={Gift} size={10} />
2399
+ <Text>+50 XP</Text>
2400
+ </Badge>
2401
+ </View>
2402
+ <Progress value={60} size="2" color="cyan" />
2403
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
2404
+ <Text size="1" color="gray">
2405
+ 3 of 5 completed
2406
+ </Text>
2407
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
2408
+ <Icon as={Clock} size={12} color={colors.palettes.gray.a11} />
2409
+ <Text size="1" color="gray">
2410
+ 8h remaining
2411
+ </Text>
2412
+ </View>
2413
+ </View>
2414
+ </View>
2415
+ </Card>
2416
+ ```
2417
+
2418
+ ---
2419
+
2420
+ ## Media Patterns
2421
+
2422
+ ### Now Playing (Music Player)
2423
+
2424
+ ```tsx
2425
+ const [isPlaying, setIsPlaying] = React.useState(true);
2426
+ const [position, setPosition] = React.useState(84); // seconds
2427
+ const duration = 225; // 3:45 in seconds
2428
+
2429
+ const formatTime = (seconds: number) => {
2430
+ const mins = Math.floor(seconds / 60);
2431
+ const secs = seconds % 60;
2432
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
2433
+ };
2434
+
2435
+ <Card>
2436
+ <View style={{ gap: 16 }}>
2437
+ {/* Album Art + Info */}
2438
+ <View style={{ flexDirection: 'row', gap: 16 }}>
2439
+ <View
2440
+ style={{
2441
+ width: 80,
2442
+ height: 80,
2443
+ borderRadius: 8,
2444
+ backgroundColor: colors.palettes.pink.a3,
2445
+ alignItems: 'center',
2446
+ justifyContent: 'center',
2447
+ }}>
2448
+ <Icon as={Music} size={32} color={colors.palettes.pink.a11} />
2449
+ </View>
2450
+ <View style={{ flex: 1, justifyContent: 'center', gap: 4 }}>
2451
+ <Text size="3" weight="bold" numberOfLines={1}>
2452
+ Midnight Dreams
2453
+ </Text>
2454
+ <Text size="2" color="gray" numberOfLines={1}>
2455
+ Aurora Synth
2456
+ </Text>
2457
+ <Text size="1" color="gray">
2458
+ Neon Horizons • 2024
2459
+ </Text>
2460
+ </View>
2461
+ <IconButton variant="ghost" size="2">
2462
+ <Icon as={Heart} size={20} />
2463
+ </IconButton>
2464
+ </View>
2465
+
2466
+ {/* Playback Position - Interactive Slider */}
2467
+ <View style={{ gap: 4 }}>
2468
+ <Slider value={position} onValueChange={setPosition} min={0} max={duration} size="1" />
2469
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
2470
+ <Text size="0" color="gray">
2471
+ {formatTime(position)}
2472
+ </Text>
2473
+ <Text size="0" color="gray">
2474
+ {formatTime(duration)}
2475
+ </Text>
2476
+ </View>
2477
+ </View>
2478
+
2479
+ {/* Controls */}
2480
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 24 }}>
2481
+ <IconButton variant="ghost" size="3">
2482
+ <Icon as={SkipBack} size={24} />
2483
+ </IconButton>
2484
+ <IconButton variant="solid" size="4" onPress={() => setIsPlaying(!isPlaying)}>
2485
+ <Icon as={isPlaying ? Pause : Play} size={24} />
2486
+ </IconButton>
2487
+ <IconButton variant="ghost" size="3">
2488
+ <Icon as={SkipForward} size={24} />
2489
+ </IconButton>
2490
+ </View>
2491
+ </View>
2492
+ </Card>;
2493
+ ```
2494
+
2495
+ > **Tip**: Use `Slider` for interactive playback control (user can seek). Use `Progress` for read-only progress display.
2496
+
2497
+ ### Poll Card
2498
+
2499
+ ```tsx
2500
+ const [voted, setVoted] = React.useState(null);
2501
+
2502
+ const options = [
2503
+ { id: 'react', label: 'React Native', votes: 156 },
2504
+ { id: 'flutter', label: 'Flutter', votes: 89 },
2505
+ { id: 'native', label: 'Native (Swift/Kotlin)', votes: 67 },
2506
+ ];
2507
+
2508
+ const totalVotes = options.reduce((sum, opt) => sum + opt.votes, 0);
2509
+
2510
+ <Card>
2511
+ <View style={{ gap: 16 }}>
2512
+ <View style={{ gap: 8 }}>
2513
+ <Text size="3" weight="medium">
2514
+ What's your preferred mobile framework?
2515
+ </Text>
2516
+ <Text size="1" color="gray">
2517
+ {totalVotes} votes • 2 days left
2518
+ </Text>
2519
+ </View>
2520
+
2521
+ <View style={{ gap: 8 }}>
2522
+ {options.map((option) => {
2523
+ const percentage = Math.round((option.votes / totalVotes) * 100);
2524
+ const isSelected = voted === option.id;
2525
+
2526
+ return (
2527
+ <Pressable
2528
+ key={option.id}
2529
+ onPress={() => !voted && setVoted(option.id)}
2530
+ style={{
2531
+ borderRadius: 8,
2532
+ overflow: 'hidden',
2533
+ borderWidth: 1,
2534
+ borderColor: isSelected ? colors.palettes.accent['8'] : colors.stroke,
2535
+ }}>
2536
+ {/* Progress background */}
2537
+ <View
2538
+ style={{
2539
+ position: 'absolute',
2540
+ left: 0,
2541
+ top: 0,
2542
+ bottom: 0,
2543
+ width: voted ? `${percentage}%` : '0%',
2544
+ backgroundColor: isSelected ? colors.palettes.accent.a3 : colors.palettes.gray.a3,
2545
+ }}
2546
+ />
2547
+ <View
2548
+ style={{
2549
+ flexDirection: 'row',
2550
+ alignItems: 'center',
2551
+ justifyContent: 'space-between',
2552
+ padding: 12,
2553
+ }}>
2554
+ <Text weight={isSelected ? 'medium' : 'regular'}>{option.label}</Text>
2555
+ {voted && (
2556
+ <Text size="2" weight="medium">
2557
+ {percentage}%
2558
+ </Text>
2559
+ )}
2560
+ </View>
2561
+ </Pressable>
2562
+ );
2563
+ })}
2564
+ </View>
2565
+ </View>
2566
+ </Card>;
2567
+ ```