@coinbase/cds-mcp-server 8.41.0 → 8.43.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 (51) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/mcp-docs/mobile/components/Banner.txt +1 -1
  3. package/mcp-docs/mobile/components/Button.txt +2 -2
  4. package/mcp-docs/mobile/components/Carousel.txt +381 -72
  5. package/mcp-docs/mobile/components/CellMedia.txt +1 -1
  6. package/mcp-docs/mobile/components/ContainedAssetCard.txt +1 -1
  7. package/mcp-docs/mobile/components/ContentCard.txt +220 -174
  8. package/mcp-docs/mobile/components/ContentCardBody.txt +127 -19
  9. package/mcp-docs/mobile/components/ContentCardFooter.txt +63 -1
  10. package/mcp-docs/mobile/components/ContentCardHeader.txt +71 -16
  11. package/mcp-docs/mobile/components/DataCard.txt +723 -0
  12. package/mcp-docs/mobile/components/DotSymbol.txt +1 -1
  13. package/mcp-docs/mobile/components/FloatingAssetCard.txt +1 -1
  14. package/mcp-docs/mobile/components/Icon.txt +1 -1
  15. package/mcp-docs/mobile/components/IconButton.txt +1 -1
  16. package/mcp-docs/mobile/components/MediaCard.txt +526 -0
  17. package/mcp-docs/mobile/components/MessagingCard.txt +1025 -0
  18. package/mcp-docs/mobile/components/Scrubber.txt +140 -0
  19. package/mcp-docs/mobile/routes.txt +6 -3
  20. package/mcp-docs/web/components/Banner.txt +1 -1
  21. package/mcp-docs/web/components/Button.txt +2 -2
  22. package/mcp-docs/web/components/Carousel.txt +346 -62
  23. package/mcp-docs/web/components/CellMedia.txt +1 -1
  24. package/mcp-docs/web/components/Checkbox.txt +1 -1
  25. package/mcp-docs/web/components/CheckboxCell.txt +1 -1
  26. package/mcp-docs/web/components/Chip.txt +1 -1
  27. package/mcp-docs/web/components/ContentCard.txt +419 -327
  28. package/mcp-docs/web/components/ContentCardBody.txt +91 -16
  29. package/mcp-docs/web/components/ContentCardFooter.txt +231 -165
  30. package/mcp-docs/web/components/ContentCardHeader.txt +237 -177
  31. package/mcp-docs/web/components/DataCard.txt +800 -0
  32. package/mcp-docs/web/components/DateInput.txt +1 -1
  33. package/mcp-docs/web/components/DatePicker.txt +1 -1
  34. package/mcp-docs/web/components/DotSymbol.txt +1 -1
  35. package/mcp-docs/web/components/Icon.txt +1 -1
  36. package/mcp-docs/web/components/IconButton.txt +1 -1
  37. package/mcp-docs/web/components/InputChip.txt +1 -1
  38. package/mcp-docs/web/components/MediaCard.txt +559 -0
  39. package/mcp-docs/web/components/MediaChip.txt +1 -1
  40. package/mcp-docs/web/components/MessagingCard.txt +1054 -0
  41. package/mcp-docs/web/components/Radio.txt +1 -1
  42. package/mcp-docs/web/components/RadioCell.txt +1 -1
  43. package/mcp-docs/web/components/ReferenceLine.txt +1 -0
  44. package/mcp-docs/web/components/Scrubber.txt +106 -0
  45. package/mcp-docs/web/components/SearchInput.txt +1 -1
  46. package/mcp-docs/web/components/SelectChip.txt +1 -1
  47. package/mcp-docs/web/components/SidebarItem.txt +2 -2
  48. package/mcp-docs/web/components/Switch.txt +1 -1
  49. package/mcp-docs/web/components/TextInput.txt +1 -1
  50. package/mcp-docs/web/routes.txt +3 -0
  51. package/package.json +1 -1
@@ -10,7 +10,7 @@ import { Carousel } from '@coinbase/cds-mobile/carousel/Carousel'
10
10
 
11
11
  ## Examples
12
12
 
13
- ### Basic Example
13
+ ### Basics
14
14
 
15
15
  Carousels are a great way to showcase a list of items in a compact and engaging way.
16
16
  By default, Carousels have navigation and pagination enabled.
@@ -23,18 +23,40 @@ You can also set the `styles` prop to control the styles of the carousel, such a
23
23
  ```jsx
24
24
  function MyCarousel() {
25
25
  const theme = useTheme();
26
+
27
+ function SquareAssetCard({ imageUrl, name, onPress }) {
28
+ return (
29
+ <ContainedAssetCard
30
+ description={
31
+ <Text font="label2" color="fgPositive" numberOfLines={2}>
32
+ ↗6.37%
33
+ </Text>
34
+ }
35
+ header={<RemoteImage height={32} source={imageUrl} width={32} />}
36
+ onPress={onPress}
37
+ subtitle={name}
38
+ title="$0.87"
39
+ />
40
+ );
41
+ }
42
+
26
43
  return (
27
44
  <Carousel
28
- hidePagination
45
+ loop
46
+ paginationVariant="dot"
29
47
  title="Explore Assets"
30
48
  styles={{
31
- root: { paddingInline: theme.space[2] },
49
+ root: { paddingHorizontal: theme.space[2] },
32
50
  carousel: { gap: theme.space[1] },
33
51
  }}
34
52
  >
35
- {Object.values(assets).map((asset, index) => (
36
- <CarouselItem key={asset.symbol} id={asset.symbol}>
37
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
53
+ {Object.values(assets).map((asset) => (
54
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
55
+ <SquareAssetCard
56
+ imageUrl={asset.imageUrl}
57
+ name={asset.symbol}
58
+ onPress={() => console.log(`${asset.symbol} clicked`)}
59
+ />
38
60
  </CarouselItem>
39
61
  ))}
40
62
  </Carousel>
@@ -80,10 +102,10 @@ function DynamicSizingCarousel() {
80
102
 
81
103
  return (
82
104
  <Carousel
83
- hidePagination
105
+ paginationVariant="dot"
84
106
  title="Learn more"
85
107
  styles={{
86
- root: { paddingInline: horizontalPadding },
108
+ root: { paddingHorizontal: horizontalPadding },
87
109
  carousel: { gap: horizontalGap },
88
110
  }}
89
111
  >
@@ -96,8 +118,8 @@ function DynamicSizingCarousel() {
96
118
  <Pictogram dimension="64x64" name="recurringPurchases" />
97
119
  </Box>
98
120
  }
99
- minWidth="0"
100
- onActionPress={NoopFn}
121
+ minWidth={0}
122
+ onActionPress={() => console.log('Get started pressed')}
101
123
  title="Recurring Buy"
102
124
  width="100%"
103
125
  />
@@ -107,7 +129,7 @@ function DynamicSizingCarousel() {
107
129
  action="Start earning"
108
130
  dangerouslySetBackground="rgb(var(--purple70))"
109
131
  description={
110
- <Text as="p" font="label2" numberOfLines={3} color="fgInverse">
132
+ <Text font="label2" numberOfLines={3} color="fgInverse">
111
133
  Earn staking rewards on ETH by holding it on Coinbase
112
134
  </Text>
113
135
  }
@@ -116,10 +138,10 @@ function DynamicSizingCarousel() {
116
138
  <RemoteImage height={174} source="/img/feature.png" />
117
139
  </Box>
118
140
  }
119
- minWidth="0"
120
- onActionPress={NoopFn}
141
+ minWidth={0}
142
+ onActionPress={() => console.log('Start earning pressed')}
121
143
  title={
122
- <Text color="fgInverse" as="h3" font="headline">
144
+ <Text color="fgInverse" font="headline">
123
145
  Up to 3.29% APR on ETHs
124
146
  </Text>
125
147
  }
@@ -161,10 +183,10 @@ function ResponsiveSizingCarousel() {
161
183
 
162
184
  return (
163
185
  <Carousel
164
- hidePagination
186
+ paginationVariant="dot"
165
187
  title="Learn more"
166
188
  styles={{
167
- root: { paddingInline: horizontalPadding },
189
+ root: { paddingHorizontal: horizontalPadding },
168
190
  carousel: { gap: horizontalGap },
169
191
  }}
170
192
  drag="free"
@@ -236,10 +258,10 @@ function VariedSizingCarousel() {
236
258
 
237
259
  return (
238
260
  <Carousel
239
- hidePagination
261
+ paginationVariant="dot"
240
262
  title="Varied Sizing"
241
263
  styles={{
242
- root: { paddingInline: horizontalPadding },
264
+ root: { paddingHorizontal: horizontalPadding },
243
265
  carousel: { gap: horizontalGap },
244
266
  }}
245
267
  >
@@ -254,8 +276,12 @@ function VariedSizingCarousel() {
254
276
  minWidth={0}
255
277
  />
256
278
  </CarouselItem>
257
- <CarouselItem id="btc">
258
- <SquareAssetCard imageUrl={assets.btc.imageUrl} name="BTC" />
279
+ <CarouselItem id="btc" accessibilityLabel="Bitcoin">
280
+ <SquareAssetCard
281
+ imageUrl={assets.btc.imageUrl}
282
+ name="BTC"
283
+ onPress={() => console.log('BTC clicked')}
284
+ />
259
285
  </CarouselItem>
260
286
  <CarouselItem id="secure-your-account" width={itemWidth}>
261
287
  <NudgeCard
@@ -268,8 +294,12 @@ function VariedSizingCarousel() {
268
294
  minWidth={0}
269
295
  />
270
296
  </CarouselItem>
271
- <CarouselItem id="eth">
272
- <SquareAssetCard imageUrl={assets.eth.imageUrl} name="ETH" />
297
+ <CarouselItem id="eth" accessibilityLabel="Ethereum">
298
+ <SquareAssetCard
299
+ imageUrl={assets.eth.imageUrl}
300
+ name="ETH"
301
+ onPress={() => console.log('ETH clicked')}
302
+ />
273
303
  </CarouselItem>
274
304
  <CarouselItem id="complete-your-profile" width={itemWidth}>
275
305
  <NudgeCard
@@ -282,8 +312,12 @@ function VariedSizingCarousel() {
282
312
  minWidth={0}
283
313
  />
284
314
  </CarouselItem>
285
- <CarouselItem id="ltc">
286
- <SquareAssetCard imageUrl={assets.ltc.imageUrl} name="LTC" />
315
+ <CarouselItem id="ltc" accessibilityLabel="Litecoin">
316
+ <SquareAssetCard
317
+ imageUrl={assets.ltc.imageUrl}
318
+ name="LTC"
319
+ onPress={() => console.log('LTC clicked')}
320
+ />
287
321
  </CarouselItem>
288
322
  </Carousel>
289
323
  );
@@ -297,20 +331,28 @@ When set to `snap`, upon release the carousel will snap to either the nearest it
297
331
 
298
332
  ```jsx
299
333
  function DragCarousel() {
334
+ const theme = useTheme();
335
+ const horizontalPadding = theme.space[2];
336
+ const horizontalGap = theme.space[1];
337
+
300
338
  return (
301
339
  <Carousel
302
340
  title="Explore Assets"
303
- hidePagination
341
+ paginationVariant="dot"
304
342
  drag="free"
305
343
  styles={{
306
- root: { paddingInline: horizontalPadding },
344
+ root: { paddingHorizontal: horizontalPadding },
307
345
  carousel: { gap: horizontalGap },
308
346
  }}
309
347
  snapMode="item"
310
348
  >
311
- {Object.values(assets).map((asset, index) => (
312
- <CarouselItem key={asset.symbol} id={asset.symbol}>
313
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
349
+ {Object.values(assets).map((asset) => (
350
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
351
+ <SquareAssetCard
352
+ imageUrl={asset.imageUrl}
353
+ name={asset.symbol}
354
+ onPress={() => console.log(`${asset.symbol} clicked`)}
355
+ />
314
356
  </CarouselItem>
315
357
  ))}
316
358
  </Carousel>
@@ -326,18 +368,27 @@ When set to `item`, the carousel will snap to the nearest item.
326
368
 
327
369
  ```jsx
328
370
  function SquareItemsCarousel() {
371
+ const theme = useTheme();
372
+ const horizontalPadding = theme.space[2];
373
+ const horizontalGap = theme.space[1];
374
+
329
375
  return (
330
376
  <Carousel
331
377
  title="Explore Assets"
378
+ paginationVariant="dot"
332
379
  styles={{
333
- root: { paddingInline: horizontalPadding },
380
+ root: { paddingHorizontal: horizontalPadding },
334
381
  carousel: { gap: horizontalGap },
335
382
  }}
336
383
  snapMode="item"
337
384
  >
338
- {Object.values(assets).map((asset, index) => (
339
- <CarouselItem key={asset.symbol} id={asset.symbol}>
340
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
385
+ {Object.values(assets).map((asset) => (
386
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
387
+ <SquareAssetCard
388
+ imageUrl={asset.imageUrl}
389
+ name={asset.symbol}
390
+ onPress={() => console.log(`${asset.symbol} clicked`)}
391
+ />
341
392
  </CarouselItem>
342
393
  ))}
343
394
  </Carousel>
@@ -362,18 +413,27 @@ If you want to have the next item be shown at the edge of the screen, make sure
362
413
 
363
414
  ```jsx
364
415
  function OverflowCarousel() {
416
+ const theme = useTheme();
417
+ const horizontalPadding = theme.space[2];
418
+ const horizontalGap = theme.space[1];
419
+
365
420
  return (
366
421
  <Carousel
367
422
  title="Explore Assets"
423
+ paginationVariant="dot"
368
424
  snapMode="item"
369
425
  styles={{
370
- root: { paddingInline: horizontalPadding },
426
+ root: { paddingHorizontal: horizontalPadding },
371
427
  carousel: { gap: horizontalGap },
372
428
  }}
373
429
  >
374
- {Object.values(assets).map((asset, index) => (
375
- <CarouselItem key={asset.symbol} id={asset.symbol}>
376
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
430
+ {Object.values(assets).map((asset) => (
431
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
432
+ <SquareAssetCard
433
+ imageUrl={asset.imageUrl}
434
+ name={asset.symbol}
435
+ onPress={() => console.log(`${asset.symbol} clicked`)}
436
+ />
377
437
  </CarouselItem>
378
438
  ))}
379
439
  </Carousel>
@@ -381,29 +441,56 @@ function OverflowCarousel() {
381
441
  }
382
442
  ```
383
443
 
384
- ### Looping
444
+ ### Autoplay
385
445
 
386
- Enable infinite looping with the `loop` prop. You must have at least two pages of content to enable looping.
446
+ Use `autoplay` to allow for automatic page advancement. The default interval is 3 seconds but can be changed with `autoplayInterval`.
387
447
 
388
- Looping works with both `snap` and `free` drag modes, and with both `page` and `item` snap modes.
448
+ It is recommended to use pagination with autoplay so users know how many pages there are, and you should also set `paginationVariant="dot"` to best indicate the active page and progress.
389
449
 
390
- #### Looping with Snap
450
+ ```jsx
451
+ function AutoplayCarousel() {
452
+ const theme = useTheme();
453
+ const horizontalPadding = theme.space[2];
454
+ const horizontalGap = theme.space[1];
391
455
 
392
- When looping is enabled with snap drag mode, the carousel will snap to the nearest item or page (depending on `snapMode`) after releasing, allowing infinite scrolling in either direction.
456
+ return (
457
+ <Carousel
458
+ autoplay
459
+ loop
460
+ paginationVariant="dot"
461
+ title="Trending Assets"
462
+ styles={{
463
+ root: { paddingHorizontal: horizontalPadding },
464
+ carousel: { gap: horizontalGap },
465
+ }}
466
+ >
467
+ {Object.values(assets).map((asset) => (
468
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
469
+ <SquareAssetCard
470
+ imageUrl={asset.imageUrl}
471
+ name={asset.symbol}
472
+ onPress={() => console.log(`${asset.symbol} clicked`)}
473
+ />
474
+ </CarouselItem>
475
+ ))}
476
+ </Carousel>
477
+ );
478
+ }
479
+ ```
393
480
 
394
481
  ```jsx
395
- function LoopingSnapCarousel() {
482
+ function CustomIntervalCarousel() {
396
483
  const theme = useTheme();
397
- const windowWidth = Dimensions.get('window').width;
398
-
399
484
  const horizontalPadding = theme.space[2];
400
- const carouselWidth = windowWidth - horizontalPadding * 2;
401
485
  const horizontalGap = theme.space[1];
402
486
 
403
487
  return (
404
488
  <Carousel
489
+ autoplay
490
+ autoplayInterval={5000}
405
491
  loop
406
- title="Infinite Scroll"
492
+ paginationVariant="dot"
493
+ title="5 Second Interval"
407
494
  styles={{
408
495
  root: { paddingHorizontal: horizontalPadding },
409
496
  carousel: { gap: horizontalGap },
@@ -411,7 +498,11 @@ function LoopingSnapCarousel() {
411
498
  >
412
499
  {Object.values(assets).map((asset) => (
413
500
  <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
414
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
501
+ <SquareAssetCard
502
+ imageUrl={asset.imageUrl}
503
+ name={asset.symbol}
504
+ onPress={() => console.log(`${asset.symbol} clicked`)}
505
+ />
415
506
  </CarouselItem>
416
507
  ))}
417
508
  </Carousel>
@@ -419,25 +510,23 @@ function LoopingSnapCarousel() {
419
510
  }
420
511
  ```
421
512
 
422
- #### Looping with Free Drag
513
+ ### Looping
423
514
 
424
- When looping with free drag, the carousel scrolls continuously without snapping, creating a smooth endless scrolling experience.
515
+ Use `loop` to allow for infinite scrolling.
425
516
 
426
517
  ```jsx
427
- function LoopingFreeDragCarousel() {
518
+ function LoopingCarousel() {
428
519
  const theme = useTheme();
429
- const windowWidth = Dimensions.get('window').width;
430
-
431
520
  const horizontalPadding = theme.space[2];
432
- const carouselWidth = windowWidth - horizontalPadding * 2;
433
521
  const horizontalGap = theme.space[1];
434
522
 
435
523
  return (
436
524
  <Carousel
525
+ autoplay
437
526
  loop
438
- drag="free"
527
+ paginationVariant="dot"
439
528
  snapMode="item"
440
- title="Free Scroll Loop"
529
+ title="Infinite Scroll"
441
530
  styles={{
442
531
  root: { paddingHorizontal: horizontalPadding },
443
532
  carousel: { gap: horizontalGap },
@@ -445,7 +534,11 @@ function LoopingFreeDragCarousel() {
445
534
  >
446
535
  {Object.values(assets).map((asset) => (
447
536
  <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
448
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
537
+ <SquareAssetCard
538
+ imageUrl={asset.imageUrl}
539
+ name={asset.symbol}
540
+ onPress={() => console.log(`${asset.symbol} clicked`)}
541
+ />
449
542
  </CarouselItem>
450
543
  ))}
451
544
  </Carousel>
@@ -462,7 +555,7 @@ You need to use `accessibilityLabel` or `accessibilityLabelledBy` props to provi
462
555
  Similar to web, you are provided the `isVisible` render prop, however it is not necessary to use since mobile users do not have a keyboard.
463
556
 
464
557
  ```jsx
465
- <Carousel>
558
+ <Carousel paginationVariant="dot">
466
559
  <CarouselItem id="btc" accessibilityLabel="Bitcoin">
467
560
  <SquareAssetCard imageUrl={assets.btc.imageUrl} name={assets.btc.symbol} />
468
561
  </CarouselItem>
@@ -698,6 +791,7 @@ You can use the `styles` props to customize different parts of the carousel.
698
791
  function CustomStylesCarousel() {
699
792
  return (
700
793
  <Carousel
794
+ paginationVariant="dot"
701
795
  styles={{
702
796
  root: { paddingInline: horizontalPadding },
703
797
  carousel: { gap: horizontalGap },
@@ -778,12 +872,200 @@ function CustomStylesCarousel() {
778
872
  }
779
873
  ```
780
874
 
875
+ #### Composed Example
876
+
877
+ You can use `useCarouselAutoplayContext` inside a custom `PaginationComponent` to build your own controls. This example shows a composed layout with pagination and play/pause on the left, and navigation arrows on the right.
878
+
879
+ ```jsx
880
+ function ComposedAutoplayCarousel() {
881
+ const carouselRef = useRef(null);
882
+ const theme = useTheme();
883
+
884
+ function CustomPaginationDots({ totalPages, activePageIndex, onPressPage }) {
885
+ const autoplay = useCarouselAutoplayContext();
886
+
887
+ return (
888
+ <HStack
889
+ alignItems="center"
890
+ background="bgSecondary"
891
+ borderRadius={1000}
892
+ gap={0.5}
893
+ paddingX={1.5}
894
+ style={{ height: theme.space[5] }}
895
+ >
896
+ {Array.from({ length: totalPages }, (_, index) => {
897
+ const isActive = index === activePageIndex;
898
+ const showProgress = isActive && autoplay.isEnabled;
899
+
900
+ const springProps = useSpring({
901
+ width: isActive ? theme.space[3] : theme.space[1],
902
+ backgroundColor:
903
+ isActive && !showProgress ? theme.color.fgPrimary : theme.color.fgMuted,
904
+ config: { tension: 300, friction: 25 },
905
+ });
906
+
907
+ // Calculate progress from timing info
908
+ const remainingTime = autoplay.getRemainingTime();
909
+ const progress = 1 - remainingTime / autoplay.totalTime;
910
+ const progressSpring = useSpring({
911
+ width: autoplay.isPlaying ? theme.space[3] : progress * theme.space[3],
912
+ config: autoplay.isPlaying ? { duration: remainingTime } : { duration: 0 },
913
+ });
914
+
915
+ return (
916
+ <Pressable
917
+ key={index}
918
+ accessibilityLabel={`Go to page ${index + 1}`}
919
+ borderRadius={1000}
920
+ borderWidth={0}
921
+ onPress={() => onPressPage?.(index)}
922
+ overflow="hidden"
923
+ >
924
+ <animated.View
925
+ style={{
926
+ width: springProps.width,
927
+ height: theme.space[1],
928
+ backgroundColor: springProps.backgroundColor,
929
+ borderRadius: theme.borderRadius[1000],
930
+ overflow: 'hidden',
931
+ }}
932
+ >
933
+ {showProgress && (
934
+ <animated.View
935
+ style={{
936
+ width: progressSpring.width,
937
+ height: '100%',
938
+ backgroundColor: theme.color.fgPrimary,
939
+ borderRadius: theme.borderRadius[1000],
940
+ }}
941
+ />
942
+ )}
943
+ </animated.View>
944
+ </Pressable>
945
+ );
946
+ })}
947
+ </HStack>
948
+ );
949
+ }
950
+
951
+ function CustomControls({ totalPages, activePageIndex, onPressPage }) {
952
+ const autoplay = useCarouselAutoplayContext();
953
+
954
+ return (
955
+ <HStack justifyContent="space-between" paddingY={1}>
956
+ <HStack gap={1} alignItems="center">
957
+ <CustomPaginationDots
958
+ totalPages={totalPages}
959
+ activePageIndex={activePageIndex}
960
+ onPressPage={onPressPage}
961
+ />
962
+ <IconButton
963
+ accessibilityLabel={autoplay.isStopped ? 'Play' : 'Pause'}
964
+ name={autoplay.isStopped ? 'play' : 'pause'}
965
+ onPress={autoplay.toggle}
966
+ variant="secondary"
967
+ />
968
+ </HStack>
969
+ <HStack gap={1}>
970
+ <IconButton
971
+ accessibilityLabel="Previous"
972
+ disabled={activePageIndex <= 0}
973
+ name="caretLeft"
974
+ onPress={() => carouselRef.current?.goToPage(activePageIndex - 1)}
975
+ variant="secondary"
976
+ />
977
+ <IconButton
978
+ accessibilityLabel="Next"
979
+ disabled={activePageIndex >= totalPages - 1}
980
+ name="caretRight"
981
+ onPress={() => carouselRef.current?.goToPage(activePageIndex + 1)}
982
+ variant="secondary"
983
+ />
984
+ </HStack>
985
+ </HStack>
986
+ );
987
+ }
988
+
989
+ return (
990
+ <Carousel
991
+ ref={carouselRef}
992
+ autoplay
993
+ loop
994
+ hideNavigation
995
+ PaginationComponent={CustomControls}
996
+ styles={{
997
+ root: { paddingHorizontal: theme.space[3] },
998
+ carousel: { gap: theme.space[2] },
999
+ }}
1000
+ >
1001
+ <CarouselItem id="innovation-1" width={`${(100 - 2 * 2) / 3}%`}>
1002
+ <NudgeCard
1003
+ title="Innovation"
1004
+ description="Cards are a great way to showcase content."
1005
+ pictogram="shield"
1006
+ width="100%"
1007
+ minWidth={0}
1008
+ />
1009
+ </CarouselItem>
1010
+ <CarouselItem id="innovation-2" width={`${(100 - 2 * 2) / 3}%`}>
1011
+ <NudgeCard
1012
+ title="Innovation"
1013
+ description="Cards are a great way to showcase content."
1014
+ pictogram="security"
1015
+ width="100%"
1016
+ minWidth={0}
1017
+ />
1018
+ </CarouselItem>
1019
+ <CarouselItem id="innovation-3" width={`${(100 - 2 * 2) / 3}%`}>
1020
+ <NudgeCard
1021
+ title="Innovation"
1022
+ description="Cards are a great way to showcase content."
1023
+ pictogram="institutions"
1024
+ width="100%"
1025
+ minWidth={0}
1026
+ />
1027
+ </CarouselItem>
1028
+ <CarouselItem id="innovation-4" width={`${(100 - 2 * 2) / 3}%`}>
1029
+ <NudgeCard
1030
+ title="Innovation"
1031
+ description="Cards are a great way to showcase content."
1032
+ pictogram="key"
1033
+ width="100%"
1034
+ minWidth={0}
1035
+ />
1036
+ </CarouselItem>
1037
+ <CarouselItem id="innovation-5" width={`${(100 - 2 * 2) / 3}%`}>
1038
+ <NudgeCard
1039
+ title="Innovation"
1040
+ description="Cards are a great way to showcase content."
1041
+ pictogram="receipt"
1042
+ width="100%"
1043
+ minWidth={0}
1044
+ />
1045
+ </CarouselItem>
1046
+ <CarouselItem id="innovation-6" width={`${(100 - 2 * 2) / 3}%`}>
1047
+ <NudgeCard
1048
+ title="Innovation"
1049
+ description="Cards are a great way to showcase content."
1050
+ pictogram="worldwide"
1051
+ width="100%"
1052
+ minWidth={0}
1053
+ />
1054
+ </CarouselItem>
1055
+ </Carousel>
1056
+ );
1057
+ }
1058
+ ```
1059
+
781
1060
  #### Dynamic Content
782
1061
 
783
1062
  You can dynamically add and remove items from the carousel.
784
1063
 
785
1064
  ```jsx
786
1065
  function DynamicContentCarousel() {
1066
+ const theme = useTheme();
1067
+ const horizontalPadding = theme.space[2];
1068
+ const horizontalGap = theme.space[1];
787
1069
  const [items, setItems] = useState(Object.values(assets).slice(0, 3));
788
1070
 
789
1071
  function addAsset() {
@@ -804,14 +1086,19 @@ function DynamicContentCarousel() {
804
1086
  </HStack>
805
1087
  <Carousel
806
1088
  title="Explore Assets"
1089
+ paginationVariant="dot"
807
1090
  styles={{
808
- root: { paddingInline: horizontalPadding },
1091
+ root: { paddingHorizontal: horizontalPadding },
809
1092
  carousel: { gap: horizontalGap, height: 156 },
810
1093
  }}
811
1094
  >
812
- {items.map((asset, index) => (
813
- <CarouselItem key={asset.symbol} id={asset.symbol}>
814
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
1095
+ {items.map((asset) => (
1096
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
1097
+ <SquareAssetCard
1098
+ imageUrl={asset.imageUrl}
1099
+ name={asset.symbol}
1100
+ onPress={() => console.log(`${asset.symbol} clicked`)}
1101
+ />
815
1102
  </CarouselItem>
816
1103
  ))}
817
1104
  </Carousel>
@@ -822,10 +1109,16 @@ function DynamicContentCarousel() {
822
1109
 
823
1110
  #### Hide Navigation and Pagination
824
1111
 
825
- You can hide the navigation and pagination components of the carousel if desired.
1112
+ You can hide the navigation and pagination components of the carousel if desired (using `hideNavigation` and `hidePagination` props).
1113
+
1114
+ Note that this can prevent proper accessibility for the carousel, if carousel items are not focusable. If hiding pagination, it's recommended to ensure that the carousel is navigable by other means.
826
1115
 
827
1116
  ```jsx
828
1117
  function HideNavigationAndPaginationCarousel() {
1118
+ const theme = useTheme();
1119
+ const horizontalPadding = theme.space[2];
1120
+ const horizontalGap = theme.space[1];
1121
+
829
1122
  return (
830
1123
  <Carousel
831
1124
  title="Explore Assets"
@@ -834,13 +1127,17 @@ function HideNavigationAndPaginationCarousel() {
834
1127
  drag="free"
835
1128
  snapMode="item"
836
1129
  styles={{
837
- root: { paddingInline: horizontalPadding },
1130
+ root: { paddingHorizontal: horizontalPadding },
838
1131
  carousel: { gap: horizontalGap },
839
1132
  }}
840
1133
  >
841
- {Object.values(assets).map((asset, index) => (
842
- <CarouselItem key={asset.symbol} id={asset.symbol}>
843
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
1134
+ {Object.values(assets).map((asset) => (
1135
+ <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
1136
+ <SquareAssetCard
1137
+ imageUrl={asset.imageUrl}
1138
+ name={asset.symbol}
1139
+ onPress={() => console.log(`${asset.symbol} clicked`)}
1140
+ />
844
1141
  </CarouselItem>
845
1142
  ))}
846
1143
  </Carousel>
@@ -950,7 +1247,11 @@ function ImperativeApiCarousel() {
950
1247
  >
951
1248
  {Object.values(assets).map((asset) => (
952
1249
  <CarouselItem key={asset.symbol} accessibilityLabel={asset.name} id={asset.symbol}>
953
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
1250
+ <SquareAssetCard
1251
+ imageUrl={asset.imageUrl}
1252
+ name={asset.symbol}
1253
+ onPress={() => console.log(`${asset.symbol} clicked`)}
1254
+ />
954
1255
  </CarouselItem>
955
1256
  ))}
956
1257
  </Carousel>
@@ -1026,9 +1327,13 @@ function AnimatedPaginationCarousel() {
1026
1327
  }}
1027
1328
  title="Explore Assets"
1028
1329
  >
1029
- {Object.values(assets).map((asset, index) => (
1330
+ {Object.values(assets).map((asset) => (
1030
1331
  <CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
1031
- <SquareAssetCard imageUrl={asset.imageUrl} name={asset.symbol} />
1332
+ <SquareAssetCard
1333
+ imageUrl={asset.imageUrl}
1334
+ name={asset.symbol}
1335
+ onPress={() => console.log(`${asset.symbol} clicked`)}
1336
+ />
1032
1337
  </CarouselItem>
1033
1338
  ))}
1034
1339
  </Carousel>
@@ -1061,6 +1366,8 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
1061
1366
  | `alignSelf` | `auto \| FlexAlignType` | No | `-` | - |
1062
1367
  | `animated` | `boolean` | No | `-` | - |
1063
1368
  | `aspectRatio` | `string \| number` | No | `-` | - |
1369
+ | `autoplay` | `boolean` | No | `-` | Whether autoplay is enabled for the carousel. |
1370
+ | `autoplayInterval` | `number` | No | `3000 (3 seconds)` | The interval in milliseconds for autoplay. |
1064
1371
  | `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 | `-` | - |
1065
1372
  | `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
1066
1373
  | `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
@@ -1098,7 +1405,6 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
1098
1405
  | `fontSize` | `inherit \| FontSize` | No | `-` | - |
1099
1406
  | `fontWeight` | `inherit \| FontWeight` | No | `-` | - |
1100
1407
  | `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1101
- | `goToPageAccessibilityLabel` | `string` | No | `-` | Accessibility label for the go to page button. |
1102
1408
  | `height` | `string \| number` | No | `-` | - |
1103
1409
  | `hideNavigation` | `boolean` | No | `-` | Hides the navigation arrows (previous/next buttons). |
1104
1410
  | `hidePagination` | `boolean` | No | `-` | Hides the pagination indicators (dots/bars showing current page). |
@@ -1131,7 +1437,8 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
1131
1437
  | `paddingTop` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1132
1438
  | `paddingX` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1133
1439
  | `paddingY` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1134
- | `paginationAccessibilityLabel` | `string \| ((pageIndex: number) => string)` | No | `-` | Accessibility label for the go to page button. |
1440
+ | `paginationAccessibilityLabel` | `string \| ((pageIndex: number) => string)` | No | `(pageIndex) => `Go to page ${pageIndex + 1}`` | Accessibility label for the go to page button. When a string is provided, it is used as-is for all indicators. When a function is provided, it receives the page index and returns a label. |
1441
+ | `paginationVariant` | `dot \| pill` | No | `'pill'` | Visual variant for the pagination indicators. - pill: All indicators are pill-shaped (default) - dot: Inactive indicators are small dots, active indicator expands to a pill |
1135
1442
  | `pin` | `top \| bottom \| left \| right \| all` | No | `-` | Direction in which to absolutely pin the box. |
1136
1443
  | `position` | `static \| relative \| fixed \| absolute \| sticky` | No | `-` | - |
1137
1444
  | `previousPageAccessibilityLabel` | `string` | No | `-` | Accessibility label for the previous page button. |
@@ -1139,6 +1446,8 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
1139
1446
  | `right` | `string \| number` | No | `-` | - |
1140
1447
  | `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1141
1448
  | `snapMode` | `item \| page` | No | `'page'` | Specifies the pagination and navigation strategy for the carousel. item treats each item as a separate page for navigation, pagination, and snapping. page groups items into pages based on visible area for navigation, pagination, and snapping. This affects page calculation, navigation button behavior, and snap targets when dragging. |
1449
+ | `startAutoplayAccessibilityLabel` | `string` | No | `'Play Carousel'` | Accessibility label for starting autoplay. |
1450
+ | `stopAutoplayAccessibilityLabel` | `string` | No | `'Pause Carousel'` | Accessibility label for stopping autoplay. |
1142
1451
  | `style` | `((false \| RegisteredStyle<ViewStyle> \| Value \| AnimatedInterpolation<string \| number> \| WithAnimatedObject<ViewStyle> \| WithAnimatedArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle> \| RecursiveArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>> \| readonly (Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>)[]>) & (false \| ViewStyle \| RegisteredStyle<ViewStyle> \| RecursiveArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>>)) \| null` | No | `-` | Custom styles for the root element. |
1143
1452
  | `styles` | `{ root?: StyleProp<ViewStyle>; title?: StyleProp<TextStyle>; navigation?: StyleProp<ViewStyle>; pagination?: StyleProp<ViewStyle>; carousel?: StyleProp<ViewStyle>; carouselContainer?: StyleProp<ViewStyle>; }` | No | `-` | Custom styles for the component. |
1144
1453
  | `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 Used to locate this element in unit and end-to-end tests. |