Skip to content
6 changes: 6 additions & 0 deletions .changeset/twenty-ducks-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/types': patch
---

Refactor `<Checkout />` components to apply descriptors and ensure styling is properly connected to theming layer.
4 changes: 1 addition & 3 deletions packages/clerk-js/src/ui/components/Checkout/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ const AuthenticatedRoutes = withCoreUserGuard((props: __experimental_CheckoutPro
<Drawer.Overlay />
<Drawer.Content>
<Drawer.Header title='Checkout' />
<Drawer.Body>
<CheckoutPage {...props} />
</Drawer.Body>
<CheckoutPage {...props} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that tying the checkout page to be always in a drawer? Do we want users to be able to embed this onto a separate page?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<Checkout /> would be what the user implements on the page which always renders in the <Drawer />.

</Drawer.Content>
</Drawer.Root>
);
Expand Down
257 changes: 102 additions & 155 deletions packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { __experimental_CommerceCheckoutResource } from '@clerk/types';

import { useCheckoutContext } from '../../contexts';
import { Box, Button, Col, Flex, Icon, Text } from '../../customizables';
import { LineItems } from '../../elements';
import { Box, Button, descriptors, Heading, Icon, Span, Text } from '../../customizables';
import { Drawer, LineItems } from '../../elements';
import { Check } from '../../icons';

export const CheckoutComplete = ({ checkout }: { checkout: __experimental_CommerceCheckoutResource }) => {
Expand All @@ -15,44 +15,85 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
};

return (
<Col
sx={{
flex: 1,
}}
>
<Col
align='center'
justify='center'
gap={8}
sx={t => ({
flex: 1,
paddingBlock: t.space.$4,
})}
>
<SuccessCircle />

<Col
align='center'
gap={2}
sx={{ position: 'relative' }}
<>
<Drawer.Body>
<Span
elementDescriptor={descriptors.checkoutSuccessRoot}
sx={t => ({
margin: 'auto',
position: 'relative',
aspectRatio: '1/1',
display: 'grid',
width: '100%',
padding: t.space.$4,
flexShrink: 0,
})}
>
{/* TODO(@COMMERCE): needs localization */}
<Text variant='h2'>Payment was successful!</Text>
<Text sx={t => ({ textAlign: 'center', paddingInline: t.space.$8 })}>
{/* TODO(@COMMERCE): needs localization */}
Minim adipisicing enim fugiat enim est ad nisi exercitation nisi exercitation quis culpa.
</Text>
</Col>
</Col>
<Ring scale={1} />
<Ring scale={0.75} />
<Ring scale={0.5} />
<Box
elementDescriptor={descriptors.checkoutSuccessBadge}
sx={t => ({
margin: 'auto',
gridArea: '1/1',
display: 'flex',
position: 'relative',
width: t.sizes.$16,
height: t.sizes.$16,
borderRadius: t.radii.$circle,
backgroundImage: `linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(0, 0, 0, 0.12) 50%, rgba(0, 0, 0, 0.30) 95.31%)`,
boxShadow: '0px 4px 12px 0px rgba(0, 0, 0, 0.35), 0px 1px 0px 0px rgba(255, 255, 255, 0.05) inset',
':before': {
content: '""',
position: 'absolute',
inset: t.space.$1,
borderRadius: t.radii.$circle,
backgroundColor: t.colors.$colorBackground,
},
})}
>
<Icon
icon={Check}
colorScheme='neutral'
sx={{
position: 'relative',
margin: 'auto',
}}
aria-hidden
/>
</Box>
<Span
sx={t => ({
margin: 'auto',
gridArea: '1/1',
position: 'relative',
textAlign: 'center',
transform: `translateY(${t.space.$20})`,
})}
>
<Heading
elementDescriptor={descriptors.checkoutSuccessTitle}
as='h2'
textVariant='h2'
>
Payment was successful!
</Heading>
<Text
elementDescriptor={descriptors.checkoutSuccessDescription}
colorScheme='secondary'
sx={t => ({ textAlign: 'center', paddingInline: t.space.$8, marginBlockStart: t.space.$2 })}
>
{/* TODO(@COMMERCE): needs localization */}
Minim adipisicing enim fugiat enim est ad nisi exercitation nisi exercitation quis culpa.
</Text>
</Span>
</Span>
</Drawer.Body>

<Col
gap={2}
<Drawer.Footer
sx={t => ({
padding: t.space.$4,
borderTopWidth: t.borderWidths.$normal,
borderTopStyle: t.borderStyles.$solid,
borderTopColor: t.colors.$neutralAlpha100,
position: 'relative',
rowGap: t.space.$4,
})}
>
<LineItems.Root>
Expand All @@ -77,131 +118,37 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
<LineItems.Description>{checkout.invoice ? checkout.invoice.id : '–'}</LineItems.Description>
</LineItems.Group>
</LineItems.Root>
<Button
colorScheme='secondary'
variant='bordered'
size='sm'
hasArrow
textVariant={'buttonSmall'}
sx={t => ({
width: '100%',
marginTop: t.space.$2,
})}
onClick={handleClose}
>
<Button onClick={handleClose}>
{/* TODO(@COMMERCE): needs localization */}
Continue
</Button>
</Col>
</Col>
</Drawer.Footer>
</>
);
};

const SuccessCircle = () => {
function Ring({
scale,
}: {
/**
* Number between 0-1
*/
scale: number;
}) {
return (
<Flex
align='center'
justify='center'
<Span
elementDescriptor={descriptors.checkoutSuccessRing}
sx={t => ({
position: 'relative',
width: '100%',
height: t.sizes.$16,
margin: 'auto',
gridArea: '1/1',
width: `${scale * 100}%`,
height: `${scale * 100}%`,
borderWidth: 1,
borderStyle: 'solid',
borderColor: t.colors.$neutralAlpha200,
borderRadius: t.radii.$circle,
maskImage: `linear-gradient(to bottom, transparent 15%, black, transparent 85%)`,
})}
>
{/* rings */}
<Box>
<Box
sx={t => ({
position: 'absolute',
top: `-${t.sizes.$8}`,
bottom: `-${t.sizes.$8}`,
left: '50%',
translate: '-50% 0',
aspectRatio: '1/1',
borderWidth: 1,
borderStyle: 'solid',
borderColor: t.colors.$neutralAlpha150,
borderRadius: t.radii.$circle,
})}
/>
<Box
sx={t => ({
position: 'absolute',
top: `-${t.sizes.$24}`,
bottom: `-${t.sizes.$24}`,
left: '50%',
translate: '-50% 0',
aspectRatio: '1/1',
borderWidth: 1,
borderStyle: 'solid',
borderColor: t.colors.$neutralAlpha200,
borderRadius: t.radii.$circle,
})}
/>
<Box
sx={t => ({
position: 'absolute',
top: `-${t.sizes.$40}`,
bottom: `-${t.sizes.$40}`,
left: '50%',
translate: '-50% 0',
aspectRatio: '1/1',
borderWidth: 1,
borderStyle: 'solid',
borderColor: t.colors.$neutralAlpha200,
borderRadius: t.radii.$circle,
})}
/>
</Box>

{/* fade overlays */}
<Box
sx={t => ({
position: 'absolute',
width: '120%',
aspectRatio: '1/1',
top: '50%',
translate: '0 -50%',
backgroundImage: `linear-gradient(to bottom, ${t.colors.$colorBackground} 35%, transparent 48%, transparent 52%, ${t.colors.$colorBackground} 65%)`,
})}
/>

{/* coin */}
<Box
sx={t => ({
position: 'relative',
width: t.sizes.$16,
height: t.sizes.$16,
borderRadius: t.radii.$circle,
backgroundImage:
'linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(0, 0, 0, 0.12) 50%, rgba(0, 0, 0, 0.30) 95.31%)',
})}
>
<Box
sx={t => ({
position: 'relative',
width: t.sizes.$16,
height: t.sizes.$16,
borderRadius: t.radii.$circle,
backgroundImage: 'linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.00) 60.94%)',
backgroundBlendMode: 'plus-lighter, normal',
boxShadow: '0px 4px 12px 0px rgba(0, 0, 0, 0.35), 0px 1px 0px 0px rgba(255, 255, 255, 0.05) inset',
})}
>
<Flex
align='center'
justify='center'
sx={t => ({
position: 'absolute',
inset: t.space.$1,
borderRadius: t.radii.$circle,
backgroundColor: t.colors.$colorBackground,
})}
>
<Icon icon={Check} />
</Flex>
</Box>
</Box>
</Flex>
/>
);
};
}
Loading