@coinbase/cds-mcp-server 8.40.2 → 8.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/mcp-docs/mobile/components/Alert.txt +1 -1
  3. package/mcp-docs/mobile/components/Combobox.txt +45 -0
  4. package/mcp-docs/mobile/components/ContainedAssetCard.txt +1 -1
  5. package/mcp-docs/mobile/components/ContentCard.txt +220 -174
  6. package/mcp-docs/mobile/components/ContentCardBody.txt +127 -19
  7. package/mcp-docs/mobile/components/ContentCardFooter.txt +63 -1
  8. package/mcp-docs/mobile/components/ContentCardHeader.txt +71 -16
  9. package/mcp-docs/mobile/components/DataCard.txt +723 -0
  10. package/mcp-docs/mobile/components/DateInput.txt +4 -3
  11. package/mcp-docs/mobile/components/DatePicker.txt +4 -3
  12. package/mcp-docs/mobile/components/FloatingAssetCard.txt +1 -1
  13. package/mcp-docs/mobile/components/MediaCard.txt +526 -0
  14. package/mcp-docs/mobile/components/MessagingCard.txt +1025 -0
  15. package/mcp-docs/mobile/components/NudgeCard.txt +1 -1
  16. package/mcp-docs/mobile/components/Pictogram.txt +1 -1
  17. package/mcp-docs/mobile/components/Scrubber.txt +140 -0
  18. package/mcp-docs/mobile/components/SelectAlpha.txt +72 -4
  19. package/mcp-docs/mobile/components/SelectChipAlpha.txt +1 -0
  20. package/mcp-docs/mobile/components/TextInput.txt +4 -3
  21. package/mcp-docs/mobile/routes.txt +6 -3
  22. package/mcp-docs/web/components/Alert.txt +1 -1
  23. package/mcp-docs/web/components/Combobox.txt +47 -0
  24. package/mcp-docs/web/components/ContentCard.txt +419 -327
  25. package/mcp-docs/web/components/ContentCardBody.txt +91 -16
  26. package/mcp-docs/web/components/ContentCardFooter.txt +231 -165
  27. package/mcp-docs/web/components/ContentCardHeader.txt +237 -177
  28. package/mcp-docs/web/components/DataCard.txt +800 -0
  29. package/mcp-docs/web/components/DateInput.txt +8 -9
  30. package/mcp-docs/web/components/DatePicker.txt +8 -9
  31. package/mcp-docs/web/components/MediaCard.txt +559 -0
  32. package/mcp-docs/web/components/MessagingCard.txt +1054 -0
  33. package/mcp-docs/web/components/NudgeCard.txt +1 -1
  34. package/mcp-docs/web/components/Pictogram.txt +1 -1
  35. package/mcp-docs/web/components/ReferenceLine.txt +1 -0
  36. package/mcp-docs/web/components/Scrubber.txt +106 -0
  37. package/mcp-docs/web/components/SelectAlpha.txt +66 -4
  38. package/mcp-docs/web/components/SelectChipAlpha.txt +1 -0
  39. package/mcp-docs/web/components/TextInput.txt +8 -9
  40. package/mcp-docs/web/components/TileButton.txt +1 -1
  41. package/mcp-docs/web/components/Tooltip.txt +7 -8
  42. package/mcp-docs/web/routes.txt +3 -0
  43. package/package.json +1 -1
@@ -0,0 +1,800 @@
1
+ # DataCard
2
+
3
+ A flexible card component for displaying data with visualizations like progress bars and circles. It supports horizontal and vertical layouts with customizable thumbnails and title accessories.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { DataCard } from '@coinbase/cds-web/alpha/data-card'
9
+ ```
10
+
11
+ ## Examples
12
+
13
+ DataCard is a flexible card component for displaying data with visualizations. It provides a structured layout for thumbnails, titles, subtitles, and visualization content. Pass any visualization component as children, such as `ProgressBar`, `ProgressCircle`, `LineChart`, or custom content.
14
+
15
+ :::info Migrating from Legacy DataCard?
16
+ See the [Migration Guide](#migration-from-legacy-datacard) at the end of this page.
17
+ :::
18
+
19
+ ### Basic Examples
20
+
21
+ DataCard supports two layouts: `vertical` (stacked) and `horizontal` (side-by-side). Pass visualization components as children.
22
+
23
+ ```jsx live
24
+ function Example() {
25
+ const exampleThumbnail = (
26
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
27
+ );
28
+
29
+ return (
30
+ <VStack gap={2} width={480}>
31
+ <DataCard
32
+ layout="vertical"
33
+ subtitle="Progress indicator"
34
+ thumbnail={exampleThumbnail}
35
+ title="Progress Bar Card"
36
+ titleAccessory={
37
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
38
+ ↗ 25.25%
39
+ </Text>
40
+ }
41
+ >
42
+ <Box paddingTop={6}>
43
+ <ProgressBarWithFixedLabels
44
+ labelPlacement="below"
45
+ startLabel={{
46
+ value: 45,
47
+ render: (num) => (
48
+ <Text color="fgMuted" font="legal">
49
+ {num}%
50
+ </Text>
51
+ ),
52
+ }}
53
+ >
54
+ <ProgressBar accessibilityLabel="45% complete" progress={0.45} weight="semiheavy" />
55
+ </ProgressBarWithFixedLabels>
56
+ </Box>
57
+ </DataCard>
58
+ <DataCard
59
+ layout="horizontal"
60
+ subtitle="Circular progress"
61
+ thumbnail={exampleThumbnail}
62
+ title="Progress Circle Card"
63
+ titleAccessory={
64
+ <Text color="fgNegative" font="label1">
65
+ ↘ 3.12%
66
+ </Text>
67
+ }
68
+ >
69
+ <Box alignItems="center" height="100%">
70
+ <ProgressCircle
71
+ accessibilityLabel="60% complete"
72
+ progress={0.6}
73
+ size={100}
74
+ weight="heavy"
75
+ />
76
+ </Box>
77
+ </DataCard>
78
+ </VStack>
79
+ );
80
+ }
81
+ ```
82
+
83
+ ### With LineChart
84
+
85
+ DataCard can also display chart visualizations like LineChart for showing price trends or time-series data.
86
+
87
+ ```jsx live
88
+ function Example() {
89
+ const lineChartData = useMemo(
90
+ () => [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58, 42, 65, 78, 55, 40, 62],
91
+ [],
92
+ );
93
+
94
+ const lineChartSeries = useMemo(
95
+ () => [
96
+ {
97
+ id: 'price',
98
+ data: lineChartData,
99
+ color: 'var(--color-accentBoldBlue)',
100
+ },
101
+ ],
102
+ [lineChartData],
103
+ );
104
+
105
+ return (
106
+ <VStack gap={2} width={480}>
107
+ <DataCard
108
+ layout="vertical"
109
+ subtitle="Price trend"
110
+ thumbnail={
111
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
112
+ }
113
+ title="Line Chart Card"
114
+ >
115
+ <LineChart
116
+ showArea
117
+ accessibilityLabel="Ethereum price chart"
118
+ areaType="dotted"
119
+ height={120}
120
+ inset={0}
121
+ series={lineChartSeries}
122
+ />
123
+ </DataCard>
124
+ <DataCard
125
+ layout="vertical"
126
+ subtitle="Price trend"
127
+ thumbnail={
128
+ <RemoteImage alt="Bitcoin thumbnail" shape="circle" size="l" src={assets.btc.imageUrl} />
129
+ }
130
+ title="Chart with Trend"
131
+ titleAccessory={
132
+ <Text color="fgNegative" font="label1">
133
+ ↘ 5.8%
134
+ </Text>
135
+ }
136
+ >
137
+ <LineChart
138
+ showArea
139
+ accessibilityLabel="Bitcoin price chart"
140
+ areaType="dotted"
141
+ height={100}
142
+ inset={0}
143
+ series={lineChartSeries}
144
+ />
145
+ </DataCard>
146
+ <DataCard
147
+ renderAsPressable
148
+ as="a"
149
+ href="https://www.coinbase.com"
150
+ layout="vertical"
151
+ subtitle="Clickable line chart card"
152
+ target="_blank"
153
+ thumbnail={
154
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
155
+ }
156
+ title="Actionable Chart Card"
157
+ titleAccessory={
158
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
159
+ ↗ 8.5%
160
+ </Text>
161
+ }
162
+ >
163
+ <LineChart
164
+ showArea
165
+ accessibilityLabel="Ethereum price chart"
166
+ areaType="dotted"
167
+ height={120}
168
+ inset={0}
169
+ series={lineChartSeries}
170
+ />
171
+ </DataCard>
172
+ </VStack>
173
+ );
174
+ }
175
+ ```
176
+
177
+ ### Layout Variations
178
+
179
+ Use `layout="vertical"` for stacked layouts (thumbnail on left, visualization below) or `layout="horizontal"` for side-by-side layouts (header on left, visualization on right).
180
+
181
+ ```jsx live
182
+ function Example() {
183
+ const exampleThumbnail = (
184
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
185
+ );
186
+
187
+ return (
188
+ <VStack gap={2} width={480}>
189
+ <DataCard
190
+ layout="vertical"
191
+ subtitle="Vertical layout stacks content"
192
+ thumbnail={exampleThumbnail}
193
+ title="Vertical Layout"
194
+ >
195
+ <Box paddingTop={6}>
196
+ <ProgressBarWithFixedLabels
197
+ labelPlacement="below"
198
+ startLabel={{
199
+ value: 75,
200
+ render: (num) => (
201
+ <Text color="fgMuted" font="legal">
202
+ {num}%
203
+ </Text>
204
+ ),
205
+ }}
206
+ >
207
+ <ProgressBar accessibilityLabel="75% complete" progress={0.75} weight="semiheavy" />
208
+ </ProgressBarWithFixedLabels>
209
+ </Box>
210
+ </DataCard>
211
+ <DataCard
212
+ layout="horizontal"
213
+ subtitle="Horizontal layout places content side by side"
214
+ thumbnail={exampleThumbnail}
215
+ title="Horizontal Layout"
216
+ >
217
+ <Box alignItems="center" height="100%">
218
+ <ProgressCircle
219
+ accessibilityLabel="75% complete"
220
+ progress={0.75}
221
+ size={100}
222
+ weight="heavy"
223
+ />
224
+ </Box>
225
+ </DataCard>
226
+ </VStack>
227
+ );
228
+ }
229
+ ```
230
+
231
+ ### Title Accessory
232
+
233
+ Use `titleAccessory` to display supplementary information inline with the title, such as trends, percentages, or status indicators.
234
+
235
+ ```jsx live
236
+ function Example() {
237
+ const exampleThumbnail = (
238
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
239
+ );
240
+
241
+ return (
242
+ <VStack gap={2} width={480}>
243
+ <DataCard
244
+ layout="vertical"
245
+ subtitle="With positive trend"
246
+ thumbnail={exampleThumbnail}
247
+ title="Positive Trend"
248
+ titleAccessory={
249
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
250
+ ↗ 8.5%
251
+ </Text>
252
+ }
253
+ >
254
+ <Box paddingTop={6}>
255
+ <ProgressBarWithFixedLabels
256
+ labelPlacement="below"
257
+ startLabel={{
258
+ value: 90,
259
+ render: (num) => (
260
+ <Text color="fgMuted" font="legal">
261
+ {num}%
262
+ </Text>
263
+ ),
264
+ }}
265
+ >
266
+ <ProgressBar accessibilityLabel="90% complete" progress={0.9} weight="semiheavy" />
267
+ </ProgressBarWithFixedLabels>
268
+ </Box>
269
+ </DataCard>
270
+ <DataCard
271
+ layout="horizontal"
272
+ subtitle="With negative trend"
273
+ thumbnail={exampleThumbnail}
274
+ title="Negative Trend"
275
+ titleAccessory={
276
+ <Text color="fgNegative" font="label1">
277
+ ↘ 4.2%
278
+ </Text>
279
+ }
280
+ >
281
+ <Box alignItems="center" height="100%">
282
+ <ProgressCircle
283
+ accessibilityLabel="70% complete"
284
+ progress={0.7}
285
+ size={100}
286
+ weight="heavy"
287
+ />
288
+ </Box>
289
+ </DataCard>
290
+ </VStack>
291
+ );
292
+ }
293
+ ```
294
+
295
+ ### Interactive Cards
296
+
297
+ Use `renderAsPressable` to make the card interactive. You can render as a button with `onClick` or as a link with `as="a"` and `href`.
298
+
299
+ ```jsx live
300
+ function Example() {
301
+ const ref1 = useRef(null);
302
+ const ref2 = useRef(null);
303
+
304
+ const exampleThumbnail = (
305
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
306
+ );
307
+
308
+ return (
309
+ <VStack gap={2} width={480}>
310
+ <DataCard
311
+ ref={ref1}
312
+ renderAsPressable
313
+ aria-label="View progress details"
314
+ layout="vertical"
315
+ onClick={() => alert('Progress bar card clicked!')}
316
+ subtitle="Clickable progress card"
317
+ thumbnail={exampleThumbnail}
318
+ title="Click to View Details"
319
+ titleAccessory={
320
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
321
+ ↗ 8.5%
322
+ </Text>
323
+ }
324
+ >
325
+ <Box paddingTop={6}>
326
+ <ProgressBarWithFixedLabels
327
+ labelPlacement="below"
328
+ startLabel={{
329
+ value: 75,
330
+ render: (num) => (
331
+ <Text color="fgMuted" font="legal">
332
+ {num}%
333
+ </Text>
334
+ ),
335
+ }}
336
+ >
337
+ <ProgressBar accessibilityLabel="75% complete" progress={0.75} weight="semiheavy" />
338
+ </ProgressBarWithFixedLabels>
339
+ </Box>
340
+ </DataCard>
341
+ <DataCard
342
+ ref={ref2}
343
+ renderAsPressable
344
+ aria-label="Open Coinbase in new tab"
345
+ as="a"
346
+ href="https://www.coinbase.com"
347
+ layout="horizontal"
348
+ subtitle="Card with link"
349
+ target="_blank"
350
+ thumbnail={exampleThumbnail}
351
+ title="Open in New Tab"
352
+ titleAccessory={
353
+ <Text color="fgMuted" font="label1">
354
+ External
355
+ </Text>
356
+ }
357
+ >
358
+ <Box alignItems="center" height="100%">
359
+ <ProgressCircle
360
+ accessibilityLabel="85% complete"
361
+ progress={0.85}
362
+ size={100}
363
+ weight="heavy"
364
+ />
365
+ </Box>
366
+ </DataCard>
367
+ </VStack>
368
+ );
369
+ }
370
+ ```
371
+
372
+ ### Style Customization
373
+
374
+ Use `styles` and `classNames` props to customize specific parts of the card layout.
375
+
376
+ ```jsx live
377
+ function Example() {
378
+ const exampleThumbnail = (
379
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
380
+ );
381
+
382
+ return (
383
+ <VStack gap={2} width={480}>
384
+ <DataCard
385
+ layout="vertical"
386
+ styles={{
387
+ root: { borderWidth: 2, borderColor: '#0066FF' },
388
+ }}
389
+ subtitle="Custom border"
390
+ thumbnail={exampleThumbnail}
391
+ title="Custom Root Styles"
392
+ >
393
+ <Box paddingTop={6}>
394
+ <ProgressBarWithFixedLabels
395
+ labelPlacement="below"
396
+ startLabel={{
397
+ value: 50,
398
+ render: (num) => (
399
+ <Text color="fgMuted" font="legal">
400
+ {num}%
401
+ </Text>
402
+ ),
403
+ }}
404
+ >
405
+ <ProgressBar accessibilityLabel="50% complete" progress={0.5} weight="semiheavy" />
406
+ </ProgressBarWithFixedLabels>
407
+ </Box>
408
+ </DataCard>
409
+ <DataCard
410
+ layout="horizontal"
411
+ styles={{
412
+ root: { backgroundColor: '#F5F5F5' },
413
+ headerContainer: { paddingInlineStart: 'var(--space-4)' },
414
+ }}
415
+ subtitle="Custom background and padding"
416
+ thumbnail={exampleThumbnail}
417
+ title="Custom Layout Styles"
418
+ >
419
+ <Box alignItems="center" height="100%">
420
+ <ProgressCircle
421
+ accessibilityLabel="70% complete"
422
+ progress={0.7}
423
+ size={100}
424
+ weight="heavy"
425
+ />
426
+ </Box>
427
+ </DataCard>
428
+ </VStack>
429
+ );
430
+ }
431
+ ```
432
+
433
+ ### Multiple Cards
434
+
435
+ DataCards work well in lists or dashboards to display multiple data points.
436
+
437
+ ```jsx live
438
+ function Example() {
439
+ const exampleThumbnail = (
440
+ <RemoteImage alt="Ethereum thumbnail" shape="circle" size="l" src={ethBackground} />
441
+ );
442
+
443
+ return (
444
+ <VStack gap={2} width={480}>
445
+ <DataCard
446
+ layout="vertical"
447
+ subtitle="Daily goal progress"
448
+ thumbnail={exampleThumbnail}
449
+ title="Steps Today"
450
+ titleAccessory={
451
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
452
+ 6,500 / 10,000
453
+ </Text>
454
+ }
455
+ >
456
+ <Box paddingTop={6}>
457
+ <ProgressBarWithFixedLabels
458
+ labelPlacement="below"
459
+ startLabel={{
460
+ value: 65,
461
+ render: (num) => (
462
+ <Text color="fgMuted" font="legal">
463
+ {num}%
464
+ </Text>
465
+ ),
466
+ }}
467
+ >
468
+ <ProgressBar accessibilityLabel="65% complete" progress={0.65} weight="semiheavy" />
469
+ </ProgressBarWithFixedLabels>
470
+ </Box>
471
+ </DataCard>
472
+ <DataCard
473
+ layout="horizontal"
474
+ subtitle="Below target this week"
475
+ thumbnail={exampleThumbnail}
476
+ title="Workout Goal"
477
+ titleAccessory={
478
+ <Text color="fgNegative" font="label1">
479
+ 2 / 7 days
480
+ </Text>
481
+ }
482
+ >
483
+ <Box alignItems="center" height="100%">
484
+ <ProgressCircle
485
+ accessibilityLabel="29% complete"
486
+ progress={0.29}
487
+ size={100}
488
+ weight="heavy"
489
+ />
490
+ </Box>
491
+ </DataCard>
492
+ </VStack>
493
+ );
494
+ }
495
+ ```
496
+
497
+ ### Accessibility
498
+
499
+ Ensure all visualization components have appropriate `accessibilityLabel` props to convey the progress information to screen readers.
500
+
501
+ #### Interactive Cards
502
+
503
+ When making DataCard interactive with `renderAsPressable`:
504
+
505
+ - If `as` is set to `"button"` or `"a"`, `renderAsPressable` defaults to `true` automatically. Add an `accessibilityLabel` to summarize the card's content for screen reader users, ensuring all visual text of the card is included in the label (e.g., `accessibilityLabel="ETH Holdings, 45% progress, View details"`)
506
+
507
+ ```jsx live
508
+ <DataCard
509
+ renderAsPressable
510
+ accessibilityLabel="ETH Holdings, 45% progress, View details"
511
+ as="button"
512
+ onClick={() => handleClick()}
513
+ title="ETH Holdings"
514
+ subtitle="45% progress"
515
+ width={480}
516
+ >
517
+ <Box paddingTop={6}>
518
+ <ProgressBarWithFixedLabels
519
+ labelPlacement="below"
520
+ startLabel={{
521
+ value: 45,
522
+ render: (num) => (
523
+ <Text color="fgMuted" font="legal">
524
+ {num}%
525
+ </Text>
526
+ ),
527
+ }}
528
+ >
529
+ <ProgressBar accessibilityLabel="45% complete" progress={0.45} weight="semiheavy" />
530
+ </ProgressBarWithFixedLabels>
531
+ </Box>
532
+ </DataCard>
533
+ ```
534
+
535
+ :::warning Avoid Nested Interactive Elements
536
+ Don't place buttons or links inside an interactive card, as this creates accessibility issues for screen reader users and can cause unexpected behavior when clicking.
537
+ :::
538
+
539
+ #### Heading Semantics
540
+
541
+ By default, the `title` prop renders as a `<div>`. If you need the title to be a proper heading element for document structure, pass a custom `Text` node with the `as` prop:
542
+
543
+ ```jsx
544
+ <DataCard
545
+ title={
546
+ <Text as="h3" font="headline">
547
+ Card Title
548
+ </Text>
549
+ }
550
+ // ...other props
551
+ />
552
+ ```
553
+
554
+ #### Color Contrast for Gain/Loss Text
555
+
556
+ When displaying gain or loss percentages in DataCard, be aware of color contrast differences between light and dark modes.
557
+
558
+ **Why this matters:** DataCard uses `bgAlternate` as its background color. In **light mode**, the semantic `fgPositive` token does not meet WCAG AA contrast requirements:
559
+
560
+ | Mode | Color | Background | Contrast Ratio | WCAG AA (4.5:1) |
561
+ | ----- | ------------------------ | ------------------------ | -------------- | --------------- |
562
+ | Light | `fgPositive` (`green60`) | `bgAlternate` (`gray10`) | ~3.6:1 | ❌ Fails |
563
+ | Light | `green70` | `bgAlternate` (`gray10`) | ~4.8:1 | ✅ Passes |
564
+ | Dark | `fgPositive` (`green60`) | `bgAlternate` (`gray5`) | ~6.2:1 | ✅ Passes |
565
+
566
+ **Recommendation:**
567
+
568
+ - **Light mode**: Use `green70` for positive values instead of `fgPositive`
569
+ - **Dark mode**: `fgPositive` meets WCAG AA requirements and can be used as-is
570
+ - **Both modes**: `fgNegative` meets WCAG AA requirements
571
+
572
+ **On web**, use CSS variables for light mode compatibility:
573
+
574
+ ```jsx
575
+ {
576
+ /* Gain text */
577
+ }
578
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
579
+ ↗ 12.5%
580
+ </Text>;
581
+
582
+ {
583
+ /* Loss text */
584
+ }
585
+ <Text color="fgNegative" font="label1">
586
+ ↘ 3.2%
587
+ </Text>;
588
+ ```
589
+
590
+ ```jsx live
591
+ function Example() {
592
+ const exampleThumbnail = (
593
+ <RemoteImage alt="Ethereum logo" shape="circle" size="l" src={ethBackground} />
594
+ );
595
+
596
+ return (
597
+ <VStack gap={2} width={480}>
598
+ <DataCard
599
+ layout="vertical"
600
+ subtitle="Portfolio allocation"
601
+ thumbnail={exampleThumbnail}
602
+ title="ETH Holdings"
603
+ titleAccessory={
604
+ <Text dangerouslySetColor="rgb(var(--green70))" font="label1">
605
+ ↗ 12.5%
606
+ </Text>
607
+ }
608
+ >
609
+ <Box paddingTop={6}>
610
+ <ProgressBarWithFixedLabels
611
+ labelPlacement="below"
612
+ startLabel={{
613
+ value: 80,
614
+ render: (num) => (
615
+ <Text color="fgMuted" font="legal">
616
+ {num}%
617
+ </Text>
618
+ ),
619
+ }}
620
+ >
621
+ <ProgressBar
622
+ accessibilityLabel="ETH holdings at 80% of target, currently $4,000 of $5,000 goal"
623
+ progress={0.8}
624
+ weight="semiheavy"
625
+ />
626
+ </ProgressBarWithFixedLabels>
627
+ </Box>
628
+ </DataCard>
629
+ </VStack>
630
+ );
631
+ }
632
+ ```
633
+
634
+ ### Migration from Legacy DataCard
635
+
636
+ The new `DataCard` from `@coinbase/cds-web/alpha/data-card` replaces the legacy `DataCard`. The new version provides more flexibility with custom layouts and visualization components.
637
+
638
+ **Before:**
639
+
640
+ ```jsx
641
+ import { DataCard } from '@coinbase/cds-web/cards/DataCard';
642
+
643
+ <DataCard
644
+ title="Progress"
645
+ description="45% complete"
646
+ progress={0.45}
647
+ progressVariant="bar"
648
+ startLabel={45}
649
+ />;
650
+ ```
651
+
652
+ **After:**
653
+
654
+ ```jsx
655
+ import { DataCard } from '@coinbase/cds-web/alpha/data-card';
656
+
657
+ <DataCard
658
+ title="Progress"
659
+ subtitle="45% complete"
660
+ layout="vertical"
661
+ thumbnail={<RemoteImage src={assetUrl} shape="circle" size="l" />}
662
+ >
663
+ <Box paddingTop={6}>
664
+ <ProgressBarWithFixedLabels
665
+ startLabel={{
666
+ value: 45,
667
+ render: (num) => (
668
+ <Text color="fgMuted" font="legal">
669
+ {num}%
670
+ </Text>
671
+ ),
672
+ }}
673
+ labelPlacement="below"
674
+ >
675
+ <ProgressBar accessibilityLabel="45% complete" progress={0.45} weight="semiheavy" />
676
+ </ProgressBarWithFixedLabels>
677
+ </Box>
678
+ </DataCard>;
679
+ ```
680
+
681
+ ## Props
682
+
683
+ | Prop | Type | Required | Default | Description |
684
+ | --- | --- | --- | --- | --- |
685
+ | `layout` | `horizontal \| vertical` | Yes | `'vertical'` | Layout orientation of the card. Horizontal places header and visualization side by side, vertical stacks them. |
686
+ | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | Yes | `-` | Text or React node to display as the card title. Use a Text component to override default color and font. |
687
+ | `alignContent` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| space-between \| space-around \| space-evenly>` | No | `-` | - |
688
+ | `alignItems` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
689
+ | `alignSelf` | `ResponsiveProp<center \| normal \| auto \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
690
+ | `as` | `symbol \| object \| style \| div \| a \| abbr \| address \| area \| article \| aside \| audio \| b \| base \| bdi \| bdo \| big \| blockquote \| body \| br \| button \| canvas \| caption \| center \| cite \| code \| col \| colgroup \| data \| datalist \| dd \| del \| details \| dfn \| dialog \| dl \| dt \| em \| embed \| fieldset \| figcaption \| figure \| footer \| form \| h1 \| h2 \| h3 \| h4 \| h5 \| h6 \| head \| header \| hgroup \| hr \| html \| i \| iframe \| img \| input \| ins \| kbd \| keygen \| label \| legend \| li \| link \| main \| map \| mark \| menu \| menuitem \| meta \| meter \| nav \| noindex \| noscript \| ol \| optgroup \| option \| output \| p \| param \| picture \| pre \| progress \| q \| rp \| rt \| ruby \| s \| samp \| search \| slot \| script \| section \| select \| small \| source \| span \| strong \| sub \| summary \| sup \| table \| template \| tbody \| td \| textarea \| tfoot \| th \| thead \| time \| title \| tr \| track \| u \| ul \| var \| video \| wbr \| webview \| svg \| animate \| animateMotion \| animateTransform \| circle \| clipPath \| defs \| desc \| ellipse \| feBlend \| feColorMatrix \| feComponentTransfer \| feComposite \| feConvolveMatrix \| feDiffuseLighting \| feDisplacementMap \| feDistantLight \| feDropShadow \| feFlood \| feFuncA \| feFuncB \| feFuncG \| feFuncR \| feGaussianBlur \| feImage \| feMerge \| feMergeNode \| feMorphology \| feOffset \| fePointLight \| feSpecularLighting \| feSpotLight \| feTile \| feTurbulence \| filter \| foreignObject \| g \| image \| line \| linearGradient \| marker \| mask \| metadata \| mpath \| path \| pattern \| polygon \| polyline \| radialGradient \| rect \| set \| stop \| switch \| text \| textPath \| tspan \| use \| view \| ComponentClass<any, any> \| FunctionComponent<any>` | No | `-` | The underlying element or component the polymorphic component will render. Changing as also changes the inherited native props (e.g. href for as=a) and the expected ref type. |
691
+ | `aspectRatio` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
692
+ | `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | Background color of the overlay (element being interacted with). |
693
+ | `blendStyles` | `InteractableBlendStyles` | No | `-` | - |
694
+ | `block` | `boolean` | No | `-` | Set element to block and expand to 100% width. |
695
+ | `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
696
+ | `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
697
+ | `borderBottomWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
698
+ | `borderColor` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | Border color of the element. |
699
+ | `borderEndWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
700
+ | `borderRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
701
+ | `borderStartWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
702
+ | `borderTopLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
703
+ | `borderTopRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
704
+ | `borderTopWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
705
+ | `borderWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
706
+ | `bordered` | `boolean` | No | `-` | Add a border around all sides of the box. |
707
+ | `borderedBottom` | `boolean` | No | `-` | Add a border to the bottom side of the box. |
708
+ | `borderedEnd` | `boolean` | No | `-` | Add a border to the trailing side of the box. |
709
+ | `borderedHorizontal` | `boolean` | No | `-` | Add a border to the leading and trailing sides of the box. |
710
+ | `borderedStart` | `boolean` | No | `-` | Add a border to the leading side of the box. |
711
+ | `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
712
+ | `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
713
+ | `bottom` | `ResponsiveProp<Bottom<string \| number>>` | No | `-` | - |
714
+ | `children` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Child node to display as the visualization (e.g., ProgressBar or ProgressCircle). |
715
+ | `className` | `string` | No | `-` | Apply class names to the outer container. |
716
+ | `classNames` | `({ layoutContainer?: string; headerContainer?: string \| undefined; textContainer?: string \| undefined; titleContainer?: string \| undefined; } & { root?: string \| undefined; }) \| undefined` | No | `-` | - |
717
+ | `color` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
718
+ | `columnGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
719
+ | `dangerouslySetBackground` | `string` | No | `-` | - |
720
+ | `disabled` | `boolean` | No | `-` | Is the element currently disabled. |
721
+ | `display` | `ResponsiveProp<grid \| revert \| none \| block \| inline \| inline-block \| flex \| inline-flex \| inline-grid \| contents \| flow-root \| list-item>` | No | `-` | - |
722
+ | `elevation` | `0 \| 1 \| 2` | No | `-` | - |
723
+ | `flexBasis` | `ResponsiveProp<FlexBasis<string \| number>>` | No | `-` | - |
724
+ | `flexDirection` | `ResponsiveProp<column \| row \| row-reverse \| column-reverse>` | No | `-` | - |
725
+ | `flexGrow` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
726
+ | `flexShrink` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
727
+ | `flexWrap` | `ResponsiveProp<nowrap \| wrap \| wrap-reverse>` | No | `-` | - |
728
+ | `focusable` | `boolean` | No | `-` | - |
729
+ | `font` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | - |
730
+ | `fontFamily` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | - |
731
+ | `fontSize` | `ResponsiveProp<FontSize \| inherit>` | No | `-` | - |
732
+ | `fontWeight` | `ResponsiveProp<FontWeight \| inherit>` | No | `-` | - |
733
+ | `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
734
+ | `grid` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
735
+ | `gridArea` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
736
+ | `gridAutoColumns` | `ResponsiveProp<GridAutoColumns<string \| number>>` | No | `-` | - |
737
+ | `gridAutoFlow` | `inherit \| revert \| row \| column \| -moz-initial \| initial \| revert-layer \| unset \| dense` | No | `-` | - |
738
+ | `gridAutoRows` | `ResponsiveProp<GridAutoRows<string \| number>>` | No | `-` | - |
739
+ | `gridColumn` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
740
+ | `gridColumnEnd` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
741
+ | `gridColumnStart` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
742
+ | `gridRow` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
743
+ | `gridRowEnd` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
744
+ | `gridRowStart` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
745
+ | `gridTemplate` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
746
+ | `gridTemplateAreas` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
747
+ | `gridTemplateColumns` | `ResponsiveProp<GridTemplateColumns<string \| number>>` | No | `-` | - |
748
+ | `gridTemplateRows` | `ResponsiveProp<GridTemplateRows<string \| number>>` | No | `-` | - |
749
+ | `height` | `ResponsiveProp<Height<string \| number>>` | No | `-` | - |
750
+ | `justifyContent` | `ResponsiveProp<left \| right \| center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| space-between \| space-around \| space-evenly>` | No | `-` | - |
751
+ | `left` | `ResponsiveProp<Left<string \| number>>` | No | `-` | - |
752
+ | `lineHeight` | `ResponsiveProp<LineHeight \| inherit>` | No | `-` | - |
753
+ | `loading` | `boolean` | No | `-` | Is the element currenty loading. When set to true, will disable element from press and keyboard events |
754
+ | `margin` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
755
+ | `marginBottom` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
756
+ | `marginEnd` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
757
+ | `marginStart` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
758
+ | `marginTop` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
759
+ | `marginX` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
760
+ | `marginY` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
761
+ | `maxHeight` | `ResponsiveProp<MaxHeight<string \| number>>` | No | `-` | - |
762
+ | `maxWidth` | `ResponsiveProp<MaxWidth<string \| number>>` | No | `-` | - |
763
+ | `minHeight` | `ResponsiveProp<MinHeight<string \| number>>` | No | `-` | - |
764
+ | `minWidth` | `ResponsiveProp<MinWidth<string \| number>>` | No | `-` | - |
765
+ | `noScaleOnPress` | `boolean` | No | `-` | Dont scale element on press. |
766
+ | `opacity` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
767
+ | `overflow` | `ResponsiveProp<hidden \| auto \| visible \| clip \| scroll>` | No | `-` | - |
768
+ | `padding` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
769
+ | `paddingBottom` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
770
+ | `paddingEnd` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
771
+ | `paddingStart` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
772
+ | `paddingTop` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
773
+ | `paddingX` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
774
+ | `paddingY` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
775
+ | `pin` | `top \| bottom \| left \| right \| all` | No | `-` | Direction in which to absolutely pin the box. |
776
+ | `position` | `ResponsiveProp<fixed \| static \| relative \| absolute \| sticky>` | No | `-` | - |
777
+ | `pressed` | `boolean` | No | `-` | Is the element being pressed. Primarily a mobile feature, but can be used on the web. |
778
+ | `ref` | `any` | No | `-` | - |
779
+ | `renderAsPressable` | `boolean` | No | `true if `as` is 'button' or 'a', otherwise false` | If true, the CardRoot will be rendered as a Pressable component. When false, renders as an HStack for layout purposes. |
780
+ | `right` | `ResponsiveProp<Right<string \| number>>` | No | `-` | - |
781
+ | `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
782
+ | `style` | `CSSProperties` | No | `-` | - |
783
+ | `styles` | `({ layoutContainer?: CSSProperties; headerContainer?: CSSProperties \| undefined; textContainer?: CSSProperties \| undefined; titleContainer?: CSSProperties \| undefined; } & { root?: CSSProperties \| undefined; }) \| undefined` | No | `-` | - |
784
+ | `subtitle` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Text or React node to display as the card subtitle. Use a Text component to override default color and font. |
785
+ | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
786
+ | `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
787
+ | `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
788
+ | `textTransform` | `ResponsiveProp<capitalize \| lowercase \| none \| uppercase>` | No | `-` | - |
789
+ | `thumbnail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to display as a thumbnail in the header area. |
790
+ | `titleAccessory` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to display as a title accessory. |
791
+ | `top` | `ResponsiveProp<Top<string \| number>>` | No | `-` | - |
792
+ | `transform` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
793
+ | `transparentWhileInactive` | `boolean` | No | `-` | Mark the background and border as transparent until the element is interacted with (hovered, pressed, etc). Must be used in conjunction with the pressed prop |
794
+ | `transparentWhilePressed` | `boolean` | No | `-` | Mark the background and border as transparent even while element is interacted with (elevation underlay issue). Must be used in conjunction with the pressed prop |
795
+ | `userSelect` | `ResponsiveProp<text \| none \| auto \| all>` | No | `-` | - |
796
+ | `visibility` | `ResponsiveProp<hidden \| visible>` | No | `-` | - |
797
+ | `width` | `ResponsiveProp<Width<string \| number>>` | No | `-` | - |
798
+ | `zIndex` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
799
+
800
+