diff --git a/.changeset/purple-cities-study.md b/.changeset/purple-cities-study.md
new file mode 100644
index 00000000000..c7a40b91e51
--- /dev/null
+++ b/.changeset/purple-cities-study.md
@@ -0,0 +1,5 @@
+---
+'@clerk/clerk-js': patch
+---
+
+Add entry animations to CheckoutComplete component to smooth our the transition between checking out to successful state.
diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json
index 47b4a4fdd85..d24606d3c2d 100644
--- a/packages/clerk-js/bundlewatch.config.json
+++ b/packages/clerk-js/bundlewatch.config.json
@@ -21,7 +21,7 @@
{ "path": "./dist/waitlist*.js", "maxSize": "1.3KB" },
{ "path": "./dist/keylessPrompt*.js", "maxSize": "6.5KB" },
{ "path": "./dist/pricingTable*.js", "maxSize": "4.02KB" },
- { "path": "./dist/checkout*.js", "maxSize": "5.3KB" },
+ { "path": "./dist/checkout*.js", "maxSize": "5.75KB" },
{ "path": "./dist/paymentSources*.js", "maxSize": "8.9KB" },
{ "path": "./dist/up-billing-page*.js", "maxSize": "2.4KB" },
{ "path": "./dist/op-billing-page*.js", "maxSize": "2.4KB" },
diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx
index 664d8116d31..b45caa4b186 100644
--- a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx
+++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx
@@ -1,13 +1,19 @@
import type { __experimental_CommerceCheckoutResource } from '@clerk/types';
-import { Box, Button, descriptors, Heading, Icon, localizationKeys, Span, Text } from '../../customizables';
+import { Box, Button, descriptors, Heading, localizationKeys, Span, Text } from '../../customizables';
import { Drawer, LineItems, useDrawerContext } from '../../elements';
-import { Check } from '../../icons';
+import { transitionDurationValues, transitionTiming } from '../../foundations/transitions';
+import { animations } from '../../styledSystem';
import { formatDate } from '../../utils';
-
const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1);
-export const CheckoutComplete = ({ checkout }: { checkout: __experimental_CommerceCheckoutResource }) => {
+export const CheckoutComplete = ({
+ checkout,
+ isMotionSafe,
+}: {
+ checkout: __experimental_CommerceCheckoutResource;
+ isMotionSafe: boolean;
+}) => {
const { setIsOpen } = useDrawerContext();
const handleClose = () => {
@@ -29,11 +35,40 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
width: '100%',
padding: t.space.$4,
flexShrink: 0,
+ transformOrigin: 'bottom center',
+ animationName: 'scaleIn',
+ animationDuration: `${transitionDurationValues.slowest}ms`,
+ animationTimingFunction: transitionTiming.bezier,
+ animationFillMode: 'forwards',
+ opacity: 0,
+ '@keyframes scaleIn': {
+ '0%': {
+ filter: 'blur(10px)',
+ transform: 'scale(0.85)',
+ opacity: 0,
+ },
+ '100%': {
+ filter: 'blur(0px)',
+ transform: 'scale(1)',
+ opacity: 1,
+ },
+ },
+ ...(!isMotionSafe && {
+ animation: 'none',
+ opacity: 1,
+ }),
})}
>
-
-
-
+ {[1, 0.75, 0.5].map((scale, index, array) => {
+ return (
+
+ );
+ })}
({
@@ -55,15 +90,48 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
},
})}
>
-
+ >
+
+
+
({
@@ -83,11 +151,55 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
? localizationKeys('__experimental_commerce.checkout.title__paymentSuccessful')
: localizationKeys('__experimental_commerce.checkout.title__subscriptionSuccessful')
}
+ sx={{
+ opacity: 0,
+ animationName: 'slideUp',
+ animationDuration: `${transitionDurationValues.slowest}ms`,
+ animationTimingFunction: transitionTiming.bezier,
+ animationFillMode: 'forwards',
+ '@keyframes slideUp': {
+ '0%': {
+ transform: 'translateY(30px)',
+ opacity: 0,
+ },
+ '100%': {
+ transform: 'translateY(0)',
+ opacity: 1,
+ },
+ },
+ ...(!isMotionSafe && {
+ opacity: 1,
+ animation: 'none',
+ }),
+ }}
/>
({ textAlign: 'center', paddingInline: t.space.$8, marginBlockStart: t.space.$2 })}
+ sx={t => ({
+ textAlign: 'center',
+ paddingInline: t.space.$8,
+ marginBlockStart: t.space.$2,
+ opacity: 0,
+ animationName: 'slideUp',
+ animationDuration: `${transitionDurationValues.slowest * 1.5}ms`,
+ animationTimingFunction: transitionTiming.bezier,
+ animationFillMode: 'forwards',
+ '@keyframes slideUp': {
+ '0%': {
+ transform: 'translateY(30px)',
+ opacity: 0,
+ },
+ '100%': {
+ transform: 'translateY(0)',
+ opacity: 1,
+ },
+ },
+ ...(!isMotionSafe && {
+ opacity: 1,
+ animation: 'none',
+ }),
+ })}
localizationKey={
checkout.subscription?.status === 'active'
? localizationKeys('__experimental_commerce.checkout.description__paymentSuccessful')
@@ -97,10 +209,25 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
-
({
rowGap: t.space.$4,
+ animationName: 'footerSlideInUp',
+ animationDuration: `${transitionDurationValues.drawer}ms`,
+ animationTimingFunction: transitionTiming.bezier,
+ '@keyframes footerSlideInUp': {
+ '0%': {
+ transform: 'translateY(100%)',
+ opacity: 0,
+ },
+ '100%': {
+ transform: 'translateY(0)',
+ opacity: 1,
+ },
+ },
+ ...(!isMotionSafe && {
+ animation: 'none',
+ }),
})}
>
@@ -142,11 +269,18 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
function Ring({
scale,
+ index,
+ isMotionSafe,
}: {
/**
* Number between 0-1
*/
scale: number;
+ /**
+ * Index of the ring (0-2)
+ */
+ index: number;
+ isMotionSafe: boolean;
}) {
return (
);
diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx
index 428b6faff75..50681015b23 100644
--- a/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx
+++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx
@@ -5,9 +5,9 @@ import type {
} from '@clerk/types';
import { useEffect } from 'react';
-import { Alert, Box, Flex, localizationKeys, Spinner, useLocalizations } from '../../customizables';
+import { Alert, Box, Flex, localizationKeys, Spinner, useAppearance, useLocalizations } from '../../customizables';
import { Drawer, useDrawerContext } from '../../elements';
-import { useCheckout } from '../../hooks';
+import { useCheckout, usePrefersReducedMotion } from '../../hooks';
import { EmailForm } from '../UserProfile/EmailForm';
import { CheckoutComplete } from './CheckoutComplete';
import { CheckoutForm } from './CheckoutForm';
@@ -16,6 +16,9 @@ export const CheckoutPage = (props: __experimental_CheckoutProps) => {
const { translateError } = useLocalizations();
const { planId, planPeriod, subscriberType, onSubscriptionComplete } = props;
const { setIsOpen, isOpen } = useDrawerContext();
+ const prefersReducedMotion = usePrefersReducedMotion();
+ const { animations: layoutAnimations } = useAppearance().parsedLayout;
+ const isMotionSafe = !prefersReducedMotion && layoutAnimations === true;
const { checkout, isLoading, invalidate, revalidate, updateCheckout, errors } = useCheckout({
planId,
@@ -49,7 +52,12 @@ export const CheckoutPage = (props: __experimental_CheckoutProps) => {
if (checkout) {
if (checkout?.status === 'completed') {
- return ;
+ return (
+
+ );
}
return (
diff --git a/packages/clerk-js/src/ui/elements/Drawer.tsx b/packages/clerk-js/src/ui/elements/Drawer.tsx
index b0800e62795..0542c6c66b6 100644
--- a/packages/clerk-js/src/ui/elements/Drawer.tsx
+++ b/packages/clerk-js/src/ui/elements/Drawer.tsx
@@ -313,11 +313,11 @@ const Header = React.forwardRef(({ title, children,
* Drawer.Body
* -----------------------------------------------------------------------------------------------*/
-interface BodyProps {
+interface BodyProps extends React.HTMLAttributes {
children: React.ReactNode;
}
-const Body = React.forwardRef(({ children }, ref) => {
+const Body = React.forwardRef(({ children, ...props }, ref) => {
return (
(({ children }, ref) =>
overflowY: 'auto',
overflowX: 'hidden',
}}
+ {...props}
>
{children}
@@ -339,12 +340,12 @@ const Body = React.forwardRef(({ children }, ref) =>
* Drawer.Footer
* -----------------------------------------------------------------------------------------------*/
-interface FooterProps {
+interface FooterProps extends React.HTMLAttributes {
children?: React.ReactNode;
sx?: ThemableCssProp;
}
-const Footer = React.forwardRef(({ children, sx }, ref) => {
+const Footer = React.forwardRef(({ children, sx, ...props }, ref) => {
return (
(({ children, sx },
}),
sx,
]}
+ {...props}
>
{children}