From eeda21cfd94bf1440486d4ce08670a8babd1f237 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 11:18:46 -0500 Subject: [PATCH 1/9] feat: update carousel code connect --- .../src/carousel/__figma__/Carousel.figma.tsx | 29 +++++++++++++++++-- .../src/carousel/__figma__/Carousel.figma.tsx | 29 +++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx b/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx index e538ae0e9..f849b0e79 100644 --- a/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx +++ b/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx @@ -5,9 +5,34 @@ figma.connect( Carousel, 'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/%E2%9C%A8-CDS-Components?node-id=48671-10433', { + variant: { platform: 'mobile' }, imports: ["import { Carousel, CarouselItem } from '@coinbase/cds-mobile/carousel'"], - example: () => ( - + props: { + title: figma.boolean('show header', { + true: figma.string('title'), + false: undefined, + }), + hideNavigation: figma.boolean('show navigation', { + true: undefined, + false: true, + }), + hidePagination: figma.boolean('show pagination', { + true: undefined, + false: true, + }), + autoplay: figma.boolean('show autoplay', { + true: true, + false: undefined, + }), + }, + example: ({ title, hideNavigation, hidePagination, autoplay }) => ( + {/* Item content */} {/* Item content */} {/* Item content */} diff --git a/packages/web/src/carousel/__figma__/Carousel.figma.tsx b/packages/web/src/carousel/__figma__/Carousel.figma.tsx index b54847416..fe3aa73ec 100644 --- a/packages/web/src/carousel/__figma__/Carousel.figma.tsx +++ b/packages/web/src/carousel/__figma__/Carousel.figma.tsx @@ -6,9 +6,34 @@ figma.connect( Carousel, 'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/%E2%9C%A8-CDS-Components?node-id=48671-10433', { + variant: { platform: 'desktop' }, imports: ["import { Carousel, CarouselItem } from '@coinbase/cds-web/carousel'"], - example: () => ( - + props: { + title: figma.boolean('show header', { + true: figma.string('title'), + false: undefined, + }), + hideNavigation: figma.boolean('show navigation', { + true: undefined, + false: true, + }), + hidePagination: figma.boolean('show pagination', { + true: undefined, + false: true, + }), + autoplay: figma.boolean('show autoplay', { + true: true, + false: undefined, + }), + }, + example: ({ title, hideNavigation, hidePagination, autoplay }) => ( + {/* Item content */} {/* Item content */} {/* Item content */} From bc33f422f0842d8b9666bec2a6f06f1923274916 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 11:19:01 -0500 Subject: [PATCH 2/9] Update carousel pagination dot color --- packages/mobile/src/carousel/DefaultCarouselPagination.tsx | 3 ++- packages/web/src/carousel/DefaultCarouselPagination.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/mobile/src/carousel/DefaultCarouselPagination.tsx b/packages/mobile/src/carousel/DefaultCarouselPagination.tsx index e45e2f1e4..1fc245227 100644 --- a/packages/mobile/src/carousel/DefaultCarouselPagination.tsx +++ b/packages/mobile/src/carousel/DefaultCarouselPagination.tsx @@ -80,7 +80,8 @@ const PaginationDot = memo(function PaginationDot({ const springProps = useSpring({ width: isActive ? INDICATOR_ACTIVE_WIDTH : INDICATOR_INACTIVE_WIDTH, - backgroundColor: isActive && !showProgress ? theme.color.bgPrimary : theme.color.bgLine, + backgroundColor: + isActive && !showProgress ? theme.color.bgPrimary : `rgb(${theme.spectrum.gray40})`, config: animationConfig, }); diff --git a/packages/web/src/carousel/DefaultCarouselPagination.tsx b/packages/web/src/carousel/DefaultCarouselPagination.tsx index 2d804f69f..abf616347 100644 --- a/packages/web/src/carousel/DefaultCarouselPagination.tsx +++ b/packages/web/src/carousel/DefaultCarouselPagination.tsx @@ -138,7 +138,7 @@ const PaginationDot = memo(function PaginationDot({ animate={{ width: isActive ? INDICATOR_ACTIVE_WIDTH : INDICATOR_INACTIVE_WIDTH, backgroundColor: - isActive && !showProgress ? 'var(--color-bgPrimary)' : 'var(--color-bgLine)', + isActive && !showProgress ? 'var(--color-bgPrimary)' : 'rgb(var(--gray40))', }} aria-current={isActive ? 'true' : undefined} borderColor="transparent" From 846b1289df5177f4d7908edbd14cb6b327f40f38 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 13:20:09 -0500 Subject: [PATCH 3/9] Revert "Update carousel pagination dot color" This reverts commit bc33f422f0842d8b9666bec2a6f06f1923274916. --- packages/mobile/src/carousel/DefaultCarouselPagination.tsx | 3 +-- packages/web/src/carousel/DefaultCarouselPagination.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/mobile/src/carousel/DefaultCarouselPagination.tsx b/packages/mobile/src/carousel/DefaultCarouselPagination.tsx index 1fc245227..e45e2f1e4 100644 --- a/packages/mobile/src/carousel/DefaultCarouselPagination.tsx +++ b/packages/mobile/src/carousel/DefaultCarouselPagination.tsx @@ -80,8 +80,7 @@ const PaginationDot = memo(function PaginationDot({ const springProps = useSpring({ width: isActive ? INDICATOR_ACTIVE_WIDTH : INDICATOR_INACTIVE_WIDTH, - backgroundColor: - isActive && !showProgress ? theme.color.bgPrimary : `rgb(${theme.spectrum.gray40})`, + backgroundColor: isActive && !showProgress ? theme.color.bgPrimary : theme.color.bgLine, config: animationConfig, }); diff --git a/packages/web/src/carousel/DefaultCarouselPagination.tsx b/packages/web/src/carousel/DefaultCarouselPagination.tsx index abf616347..2d804f69f 100644 --- a/packages/web/src/carousel/DefaultCarouselPagination.tsx +++ b/packages/web/src/carousel/DefaultCarouselPagination.tsx @@ -138,7 +138,7 @@ const PaginationDot = memo(function PaginationDot({ animate={{ width: isActive ? INDICATOR_ACTIVE_WIDTH : INDICATOR_INACTIVE_WIDTH, backgroundColor: - isActive && !showProgress ? 'var(--color-bgPrimary)' : 'rgb(var(--gray40))', + isActive && !showProgress ? 'var(--color-bgPrimary)' : 'var(--color-bgLine)', }} aria-current={isActive ? 'true' : undefined} borderColor="transparent" From d24959ae2f240281ef816de6e7dc78ac9fe5d312 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 13:25:43 -0500 Subject: [PATCH 4/9] Update tray figma --- .../overlays/tray/__figma__/Tray.figma.tsx | 167 +++++++++--------- .../overlays/tray/__figma__/Tray.figma.tsx | 163 +++++++++++++++++ 2 files changed, 243 insertions(+), 87 deletions(-) create mode 100644 packages/web/src/overlays/tray/__figma__/Tray.figma.tsx diff --git a/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx b/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx index dd92e1c8a..a5fc5ff27 100644 --- a/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx +++ b/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx @@ -1,46 +1,47 @@ -import { useRef, useState } from 'react'; +import React, { useState } from 'react'; import { figma } from '@figma/code-connect'; import { Button } from '../../../buttons/Button'; import { Box, VStack } from '../../../layout'; -import { TextBody, TextTitle1 } from '../../../typography'; -import { Tray, TrayStickyFooter } from '../Tray'; +import { StickyFooter } from '../../../sticky-footer/StickyFooter'; +import { Text } from '../../../typography/Text'; +import { Tray } from '../Tray'; figma.connect( Tray, 'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/✨-CDS-Components?node-id=14729-33327&m=dev', { - imports: ["import { Tray } from '@coinbase/cds-mobile/overlays/tray/Tray'"], + imports: [ + "import { Tray } from '@coinbase/cds-mobile/overlays/tray/Tray'", + "import { StickyFooter } from '@coinbase/cds-mobile/sticky-footer/StickyFooter'", + ], props: { title: figma.boolean('show section header', { true: figma.textContent('SectionHeader'), false: undefined, }), - stickyFooter: figma.children('StickyFooter'), content: figma.children('.Select Option*'), }, - example: function TrayExample({ stickyFooter, content, title }) { + example: function TrayExample({ content, title }) { const [isTrayVisible, setIsTrayVisible] = useState(false); - const trayRef = useRef(null); return ( <> - + {isTrayVisible && ( setIsTrayVisible(false)} - onVisibilityChange={() => {}} title={title} - > - {({ handleClose }) => ( - - {content} - {stickyFooter} - + footer={({ handleClose }) => ( + + + )} + > + {content} )} @@ -59,37 +60,31 @@ figma.connect( true: figma.children('Spot Square/blockchain'), false: undefined, }), - title: figma.textContent('SectionHeader'), - stickyFooter: figma.children('StickyFooter'), + sectionTitle: figma.textContent('SectionHeader'), }, - example: function TrayExample({ pictogram, title, stickyFooter }) { + example: function TrayExample({ pictogram, sectionTitle }) { const [isTrayVisible, setIsTrayVisible] = useState(false); - const trayRef = useRef(null); return ( <> - + {isTrayVisible && ( setIsTrayVisible(false)} - onVisibilityChange={() => {}} - title={title} - > - {({ handleClose }) => ( - + title={ + {pictogram} - - Lorem ipsum dolor sit amet consectetur. Lacus vitae vulputate maecenas sed ac - cursus enim elementum euismod. Ac vulputate gravida mauris id nulla imperdiet - eget. Dictum vitae enim eget ut. Maecenas hendrerit amet integer sagittis cras. - Fermentum ultricies malesuada interdum - - {stickyFooter} - - )} + {sectionTitle} + + } + > + + + Content goes here. + + )} @@ -102,44 +97,45 @@ figma.connect( Tray, 'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/✨-CDS-Components?node-id=14729-33505&m=dev', { - imports: ["import { Tray } from '@coinbase/cds-mobile/overlays/tray/Tray'"], + imports: [ + "import { Tray } from '@coinbase/cds-mobile/overlays/tray/Tray'", + "import { StickyFooter } from '@coinbase/cds-mobile/sticky-footer/StickyFooter'", + ], props: { spotRectangle: figma.instance('spot rectangle'), title: figma.string('title'), body: figma.string('body'), - stickyFooter: figma.children('StickyFooter'), }, - example: function TrayExample({ spotRectangle, title, body, stickyFooter }) { + example: function TrayExample({ spotRectangle, title, body }) { const [isTrayVisible, setIsTrayVisible] = useState(false); - const trayRef = useRef(null); return ( <> - + {isTrayVisible && ( setIsTrayVisible(false)} - onVisibilityChange={() => {}} - > - {({ handleClose }) => ( - - - - {spotRectangle} - - - {title} - - - {body} - - - {stickyFooter} - + footer={({ handleClose }) => ( + + + )} + > + + + {spotRectangle} + + + {title} + + + {body} + + )} @@ -158,20 +154,17 @@ figma.connect( }, example: function TrayExample({ children }) { const [isTrayVisible, setIsTrayVisible] = useState(false); - const trayRef = useRef(null); return ( <> - + {isTrayVisible && ( setIsTrayVisible(false)} - onVisibilityChange={() => {}} + title="Title" > - {({ handleClose }) => {children}} + {children} )} @@ -184,37 +177,37 @@ figma.connect( Tray, 'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/✨-CDS-Components?node-id=14729-77780&m=dev', { - imports: ["import { Tray } from '@coinbase/cds-mobile/overlays/tray/Tray'"], + imports: [ + "import { Tray } from '@coinbase/cds-mobile/overlays/tray/Tray'", + "import { StickyFooter } from '@coinbase/cds-mobile/sticky-footer/StickyFooter'", + ], props: { content: figma.instance('content'), - stickyFooter: figma.children('StickyFooter'), title: figma.boolean('show section header', { true: figma.textContent('SectionHeader'), false: undefined, }), }, - example: function TrayExample({ content, stickyFooter, title }) { + example: function TrayExample({ content, title }) { const [isTrayVisible, setIsTrayVisible] = useState(false); - const trayRef = useRef(null); return ( <> - + {isTrayVisible && ( setIsTrayVisible(false)} - onVisibilityChange={() => {}} title={title} - > - {({ handleClose }) => ( - - {content} - {stickyFooter} - + footer={({ handleClose }) => ( + + + )} + > + {content} )} diff --git a/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx b/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx new file mode 100644 index 000000000..6b221371b --- /dev/null +++ b/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx @@ -0,0 +1,163 @@ +import { useId, useState } from 'react'; +import { figma } from '@figma/code-connect'; + +import { Button } from '../../../buttons'; +import { Pictogram } from '../../../illustrations/Pictogram'; +import { Box } from '../../../layout'; +import { VStack } from '../../../layout/VStack'; +import { PageFooter } from '../../../page/PageFooter'; +import { Text } from '../../../typography/Text'; +import { Tray } from '../Tray'; + +const FIGMA_URL = + 'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/%E2%9C%A8-CDS-Components?node-id=74148-11495&m=dev'; + +figma.connect(Tray, FIGMA_URL, { + variant: { type: 'standard' }, + imports: [ + "import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'", + "import { PageFooter } from '@coinbase/cds-web/page/PageFooter'", + ], + props: { + pin: figma.enum('device', { + desktop: 'right', + }), + showHandleBar: figma.enum('device', { + mobile: true, + }), + title: figma.textContent('SectionHeader'), + }, + example: function StandardExample({ title, ...props }) { + const [visible, setVisible] = useState(false); + return ( + <> + + {visible && ( + ( + Close} /> + )} + onCloseComplete={() => setVisible(false)} + title={title} + {...props} + > + + Content goes here. + + + )} + + ); + }, +}); + +figma.connect(Tray, FIGMA_URL, { + variant: { type: 'illustration' }, + imports: [ + "import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'", + "import { Pictogram } from '@coinbase/cds-web/illustrations/Pictogram'", + ], + props: { + pin: figma.enum('device', { + desktop: 'right', + }), + showHandleBar: figma.enum('device', { + mobile: true, + }), + sectionTitle: figma.textContent('SectionHeader'), + }, + example: function IllustrationExample({ sectionTitle, ...props }) { + const [visible, setVisible] = useState(false); + const titleId = useId(); + return ( + <> + + {visible && ( + setVisible(false)} + title={ + + + + {sectionTitle} + + + } + {...props} + > + + Content goes here. + + + )} + + ); + }, +}); + +figma.connect(Tray, FIGMA_URL, { + variant: { type: 'full-bleed image' }, + imports: ["import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'"], + props: { + pin: figma.enum('device', { + desktop: 'right', + }), + showHandleBar: figma.enum('device', { + mobile: true, + }), + sectionTitle: figma.textContent('SectionHeader'), + }, + example: function FullBleedImageExample({ sectionTitle, ...props }) { + const [visible, setVisible] = useState(false); + const titleId = useId(); + return ( + <> + + {visible && ( + + {sectionTitle} + + } + onCloseComplete={() => setVisible(false)} + styles={{ + handleBar: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + zIndex: 1, + }, + closeButton: { + position: 'absolute', + top: 'var(--space-4)', + right: 'var(--space-4)', + zIndex: 1, + }, + header: { paddingTop: 0 }, + }} + title={ + + Full Bleed + + } + {...props} + > + + Content goes here. + + + )} + + ); + }, +}); From 0c60f215825f56003890acab4189bd880bc9ab78 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 13:28:06 -0500 Subject: [PATCH 5/9] Update slidebutton docs --- .../inputs/SlideButton/_mobileExamples.mdx | 163 +++++++++++++++--- .../inputs/SlideButton/mobileMetadata.json | 7 +- 2 files changed, 148 insertions(+), 22 deletions(-) diff --git a/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx b/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx index a6efccf54..1abc81fc2 100644 --- a/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx +++ b/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx @@ -1,9 +1,9 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import ThemedImage from '@theme/ThemedImage'; -### SlideButton +## Basics -Use the `onChange` prop to listen and make changes to the `checked` state. +Use the `onChange` callback to update the `checked` state. This is the primary callback that controls both the visual and accessible state of the component. console.log('Completed')} uncheckedLabel="Swipe to confirm" checkedLabel="Confirming..." /> @@ -33,9 +32,13 @@ function Example() { } ``` -### Negative SlideButton +## Variants + +Use the `variant` prop to change the visual style. The default variant is `primary`. + +### Negative -You can use the `variant` prop to change the color of the button. +Use the `negative` variant for destructive or high-stakes confirmations. ```jsx @@ -57,7 +60,6 @@ function Example() { console.log('Completed')} uncheckedLabel="Swipe to confirm" checkedLabel="Confirming..." variant="negative" @@ -66,7 +68,27 @@ function Example() { } ``` -### Compact SlideButton +### Positive + +Use the `positive` variant for affirmative confirmations. + +```jsx +function Example() { + const [checked, setChecked] = useState(false); + + return ( + + ); +} +``` + +## Compact Use the `compact` prop to reduce the height, border-radius and padding of the button: @@ -78,7 +100,6 @@ function Example() { console.log('Completed')} uncheckedLabel="Swipe to confirm" checkedLabel="Confirming..." compact @@ -87,9 +108,31 @@ function Example() { } ``` -### Auto Complete on Threshold +## Disabled + +Use the `disabled` prop to prevent interaction. This works for both unchecked and checked states. + +```jsx +function Example() { + return ( + + + + + ); +} +``` + +## Auto Complete on Threshold + +By default, the user must release the handle past the threshold to complete. Set `autoCompleteSlideOnThresholdMet` to automatically complete as soon as the threshold is reached, without requiring release. -You can set the button to automatically complete when the slide reaches the threshold: +You can also adjust the threshold via `checkThreshold` (a value from 0 to 1, defaulting to 0.7). ```jsx function Example() { @@ -99,7 +142,6 @@ function Example() { console.log('Completed')} uncheckedLabel="Swipe to confirm" checkedLabel="Confirming..." autoCompleteSlideOnThresholdMet @@ -108,9 +150,41 @@ function Example() { } ``` -### Custom Nodes on SlideButton +## Callback Lifecycle + +SlideButton fires callbacks in a specific order during the slide gesture: -You can also use SlideButton with custom nodes. +1. `onSlideStart` -- when the gesture begins +2. `onChange` -- when the slide completes past the threshold (sets `checked` to `true`) +3. `onSlideComplete` -- immediately after `onChange` +4. `onSlideEnd` -- always fires last + +If the user releases before the threshold, `onSlideCancel` fires instead, followed by `onSlideEnd`. + +**Important:** Always use `onChange` to manage the `checked` state. The `checked` prop drives the component's `accessibilityLabel` (switching between `uncheckedLabel` and `checkedLabel`), so failing to update it means screen readers won't announce the state change. Use `onSlideComplete` only for supplementary side effects (e.g. analytics, haptic feedback) that don't affect accessible state. + +```jsx +function Example() { + const [checked, setChecked] = useState(false); + + return ( + console.log('Started')} + onSlideComplete={() => console.log('Completed')} + onSlideCancel={() => console.log('Cancelled')} + onSlideEnd={() => console.log('Ended')} + uncheckedLabel="Swipe to confirm" + checkedLabel="Confirming..." + /> + ); +} +``` + +## Custom Nodes + +Use `startUncheckedNode` and `endCheckedNode` to replace the default arrow icon and loading indicator on the handle. ```jsx @@ -132,19 +206,41 @@ function Example() { console.log('Completed')} uncheckedLabel="Swipe to enable notifications" checkedLabel="Enabling..." - startUncheckedNode={} - endCheckedNode={} + startUncheckedNode={} + endCheckedNode={} /> ); } ``` -### Custom Background and Handle Components +## Labels as Nodes + +The `uncheckedLabel` and `checkedLabel` props accept `ReactNode`, so you can pass custom styled text or other components. When using non-string labels, the component uses `accessibilityLabelledBy` to associate the handle with the container element, so ensure your label nodes contain meaningful text content. -You can customize the background and handle components of the SlideButton. +```jsx +function Example() { + const [checked, setChecked] = useState(false); + + return ( + Swipe to confirm} + checkedLabel={ + + Confirming... + + } + /> + ); +} +``` + +## Custom Background and Handle Components + +You can fully customize the background and handle by providing your own components via `SlideButtonBackgroundComponent` and `SlideButtonHandleComponent`. Your components receive typed props (`SlideButtonBackgroundProps` and `SlideButtonHandleProps`) including a `progress` spring value and the current `checked` state. ```jsx @@ -208,3 +304,28 @@ function Example() { ); } ``` + +## Accessibility + +SlideButton has built-in accessibility support. The component automatically derives its `accessibilityLabel` from the `checked` state -- displaying `uncheckedLabel` when unchecked and `checkedLabel` when checked. It also registers an `activate` accessibility action so screen readers can trigger the slide without performing a gesture. + +**Use `onChange` as your primary callback.** The `onChange` callback updates the `checked` prop, which controls the accessible label. Placing critical logic in `onSlideComplete` without updating `checked` via `onChange` will leave the accessible state stale, meaning screen readers won't announce the confirmation. + +When providing a custom `SlideButtonHandleComponent`, always spread the incoming props to preserve the built-in `accessibilityActions` and `onAccessibilityAction` handlers, and set `accessibilityLabel` and `accessibilityRole="button"` on the handle element. + +When using `ReactNode` labels instead of strings, the component uses `accessibilityLabelledBy` to link to the container element, so ensure your custom label nodes contain meaningful text. + +```jsx +function Example() { + const [checked, setChecked] = useState(false); + + return ( + + ); +} +``` diff --git a/apps/docs/docs/components/inputs/SlideButton/mobileMetadata.json b/apps/docs/docs/components/inputs/SlideButton/mobileMetadata.json index 31631d3ce..2c8b9fc38 100644 --- a/apps/docs/docs/components/inputs/SlideButton/mobileMetadata.json +++ b/apps/docs/docs/components/inputs/SlideButton/mobileMetadata.json @@ -21,5 +21,10 @@ "url": "/components/inputs/Pressable/" } ], - "dependencies": [] + "dependencies": [ + { + "name": "react-native-gesture-handler", + "version": "^2.16.2" + } + ] } From e7beac325796b38fba8b62383946974d7e49f8bd Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 14:03:52 -0500 Subject: [PATCH 6/9] Fix lint --- .../src/overlays/tray/__figma__/Tray.figma.tsx | 16 ++++++++-------- .../src/overlays/tray/__figma__/Tray.figma.tsx | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx b/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx index a5fc5ff27..3406b6f83 100644 --- a/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx +++ b/packages/mobile/src/overlays/tray/__figma__/Tray.figma.tsx @@ -30,9 +30,6 @@ figma.connect( {isTrayVisible && ( setIsTrayVisible(false)} - title={title} footer={({ handleClose }) => ( )} + handleBarVariant="inside" + onCloseComplete={() => setIsTrayVisible(false)} + title={title} > {content} @@ -115,8 +115,6 @@ figma.connect( {isTrayVisible && ( setIsTrayVisible(false)} footer={({ handleClose }) => ( )} + handleBarVariant="inside" + onCloseComplete={() => setIsTrayVisible(false)} > @@ -196,9 +196,6 @@ figma.connect( {isTrayVisible && ( setIsTrayVisible(false)} - title={title} footer={({ handleClose }) => ( )} + handleBarVariant="inside" + onCloseComplete={() => setIsTrayVisible(false)} + title={title} > {content} diff --git a/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx b/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx index 6b221371b..8d5f4a903 100644 --- a/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx +++ b/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx @@ -79,7 +79,7 @@ figma.connect(Tray, FIGMA_URL, { title={ - + {sectionTitle} @@ -118,7 +118,7 @@ figma.connect(Tray, FIGMA_URL, { + {sectionTitle} } From a77c3a3435503e9fc8de85966be99415f2bed234 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 12 Feb 2026 14:11:00 -0500 Subject: [PATCH 7/9] Drop nested child properties --- .../src/carousel/__figma__/Carousel.figma.tsx | 18 ++---------------- .../src/carousel/__figma__/Carousel.figma.tsx | 18 ++---------------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx b/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx index f849b0e79..26597b66f 100644 --- a/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx +++ b/packages/mobile/src/carousel/__figma__/Carousel.figma.tsx @@ -12,27 +12,13 @@ figma.connect( true: figma.string('title'), false: undefined, }), - hideNavigation: figma.boolean('show navigation', { - true: undefined, - false: true, - }), hidePagination: figma.boolean('show pagination', { true: undefined, false: true, }), - autoplay: figma.boolean('show autoplay', { - true: true, - false: undefined, - }), }, - example: ({ title, hideNavigation, hidePagination, autoplay }) => ( - + example: ({ title, hidePagination }) => ( + {/* Item content */} {/* Item content */} {/* Item content */} diff --git a/packages/web/src/carousel/__figma__/Carousel.figma.tsx b/packages/web/src/carousel/__figma__/Carousel.figma.tsx index fe3aa73ec..cb248f543 100644 --- a/packages/web/src/carousel/__figma__/Carousel.figma.tsx +++ b/packages/web/src/carousel/__figma__/Carousel.figma.tsx @@ -13,27 +13,13 @@ figma.connect( true: figma.string('title'), false: undefined, }), - hideNavigation: figma.boolean('show navigation', { - true: undefined, - false: true, - }), hidePagination: figma.boolean('show pagination', { true: undefined, false: true, }), - autoplay: figma.boolean('show autoplay', { - true: true, - false: undefined, - }), }, - example: ({ title, hideNavigation, hidePagination, autoplay }) => ( - + example: ({ title, hidePagination }) => ( + {/* Item content */} {/* Item content */} {/* Item content */} From c823fd818fe745f4f8d07b3a3dd151b32148a65e Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Tue, 17 Feb 2026 08:28:07 -0500 Subject: [PATCH 8/9] Improve web tray code connect --- .../overlays/tray/__figma__/Tray.figma.tsx | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx b/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx index 8d5f4a903..59633c6c2 100644 --- a/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx +++ b/packages/web/src/overlays/tray/__figma__/Tray.figma.tsx @@ -2,6 +2,7 @@ import { useId, useState } from 'react'; import { figma } from '@figma/code-connect'; import { Button } from '../../../buttons'; +import { useBreakpoints } from '../../../hooks/useBreakpoints'; import { Pictogram } from '../../../illustrations/Pictogram'; import { Box } from '../../../layout'; import { VStack } from '../../../layout/VStack'; @@ -17,29 +18,34 @@ figma.connect(Tray, FIGMA_URL, { imports: [ "import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'", "import { PageFooter } from '@coinbase/cds-web/page/PageFooter'", + "import { useBreakpoints } from '@coinbase/cds-web/hooks/useBreakpoints'", ], props: { - pin: figma.enum('device', { - desktop: 'right', - }), - showHandleBar: figma.enum('device', { - mobile: true, - }), title: figma.textContent('SectionHeader'), }, - example: function StandardExample({ title, ...props }) { + example: function StandardExample({ title }) { const [visible, setVisible] = useState(false); + const { isPhone } = useBreakpoints(); return ( <> {visible && ( ( - Close} /> + + Close + + } + justifyContent={isPhone ? 'center' : 'flex-end'} + /> )} onCloseComplete={() => setVisible(false)} + pin={isPhone ? 'bottom' : 'right'} + showHandleBar={isPhone} title={title} - {...props} > Content goes here. @@ -56,18 +62,14 @@ figma.connect(Tray, FIGMA_URL, { imports: [ "import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'", "import { Pictogram } from '@coinbase/cds-web/illustrations/Pictogram'", + "import { useBreakpoints } from '@coinbase/cds-web/hooks/useBreakpoints'", ], props: { - pin: figma.enum('device', { - desktop: 'right', - }), - showHandleBar: figma.enum('device', { - mobile: true, - }), sectionTitle: figma.textContent('SectionHeader'), }, - example: function IllustrationExample({ sectionTitle, ...props }) { + example: function IllustrationExample({ sectionTitle }) { const [visible, setVisible] = useState(false); + const { isPhone } = useBreakpoints(); const titleId = useId(); return ( <> @@ -76,15 +78,16 @@ figma.connect(Tray, FIGMA_URL, { setVisible(false)} + pin={isPhone ? 'bottom' : 'right'} + showHandleBar={isPhone} title={ - + {sectionTitle} } - {...props} > Content goes here. @@ -98,18 +101,16 @@ figma.connect(Tray, FIGMA_URL, { figma.connect(Tray, FIGMA_URL, { variant: { type: 'full-bleed image' }, - imports: ["import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'"], + imports: [ + "import { Tray } from '@coinbase/cds-web/overlays/tray/Tray'", + "import { useBreakpoints } from '@coinbase/cds-web/hooks/useBreakpoints'", + ], props: { - pin: figma.enum('device', { - desktop: 'right', - }), - showHandleBar: figma.enum('device', { - mobile: true, - }), sectionTitle: figma.textContent('SectionHeader'), }, - example: function FullBleedImageExample({ sectionTitle, ...props }) { + example: function FullBleedImageExample({ sectionTitle }) { const [visible, setVisible] = useState(false); + const { isPhone } = useBreakpoints(); const titleId = useId(); return ( <> @@ -123,6 +124,8 @@ figma.connect(Tray, FIGMA_URL, { } onCloseComplete={() => setVisible(false)} + pin={isPhone ? 'bottom' : 'right'} + showHandleBar={isPhone} styles={{ handleBar: { position: 'absolute', @@ -150,7 +153,6 @@ figma.connect(Tray, FIGMA_URL, { /> } - {...props} > Content goes here. From ed9d6cae802112c004b8c60a3f0b7d620be5f7bb Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Tue, 17 Feb 2026 12:21:55 -0500 Subject: [PATCH 9/9] Update text --- .../inputs/SlideButton/_mobileExamples.mdx | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx b/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx index 1abc81fc2..64e3c8973 100644 --- a/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx +++ b/apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx @@ -34,11 +34,7 @@ function Example() { ## Variants -Use the `variant` prop to change the visual style. The default variant is `primary`. - -### Negative - -Use the `negative` variant for destructive or high-stakes confirmations. +Use the `variant` prop to change the color of the button. The default variant is `primary`. Available variants are `negative` and `positive`. ```jsx @@ -68,26 +64,6 @@ function Example() { } ``` -### Positive - -Use the `positive` variant for affirmative confirmations. - -```jsx -function Example() { - const [checked, setChecked] = useState(false); - - return ( - - ); -} -``` - ## Compact Use the `compact` prop to reduce the height, border-radius and padding of the button: