Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/sad-mangos-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Fix SVG masking cross-browser compatibility in checkout complete component
2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
{ "path": "./dist/waitlist*.js", "maxSize": "1.5KB" },
{ "path": "./dist/keylessPrompt*.js", "maxSize": "6.5KB" },
{ "path": "./dist/pricingTable*.js", "maxSize": "4.02KB" },
{ "path": "./dist/checkout*.js", "maxSize": "7.25KB" },
{ "path": "./dist/checkout*.js", "maxSize": "7.3KB" },
{ "path": "./dist/paymentSources*.js", "maxSize": "9.17KB" },
{ "path": "./dist/up-billing-page*.js", "maxSize": "3.0KB" },
{ "path": "./dist/op-billing-page*.js", "maxSize": "3.0KB" },
Expand Down
59 changes: 48 additions & 11 deletions packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useEffect, useId, useRef, useState } from 'react';

import { Drawer, useDrawerContext } from '@/ui/elements/Drawer';
import { LineItems } from '@/ui/elements/LineItems';
Expand All @@ -22,6 +22,11 @@ export const CheckoutComplete = () => {
const [mousePosition, setMousePosition] = useState({ x: 256, y: 256 });
const [currentPosition, setCurrentPosition] = useState({ x: 256, y: 256 });

// Generate unique IDs for SVG elements to avoid conflicts with multiple component instances
const maskId1 = useId();
const maskId2 = useId();
const maskId3 = useId();

const prefersReducedMotion = usePrefersReducedMotion();
const { animations: layoutAnimations } = useAppearance().parsedLayout;
const isMotionSafe = !prefersReducedMotion && layoutAnimations === true;
Expand Down Expand Up @@ -94,7 +99,6 @@ export const CheckoutComplete = () => {
aspectRatio: '1/1',
display: 'grid',
width: '100%',
padding: t.space.$4,
flexShrink: 0,
transformOrigin: 'bottom center',
animationName: 'scaleIn',
Expand Down Expand Up @@ -155,22 +159,55 @@ export const CheckoutComplete = () => {
<filter id='clerk-checkout-success-blur-effect'>
<feGaussianBlur stdDeviation='10' />
</filter>
{[
{ r: 225, maskStart: 10, maskEnd: 90, id: maskId1 },
{ r: 162.5, maskStart: 15, maskEnd: 85, id: maskId2 },
{ r: 100, maskStart: 20, maskEnd: 80, id: maskId3 },
].map(({ maskStart, maskEnd, id }) => (
<linearGradient
key={id}
id={`gradient-${id}`}
x1='0%'
y1='0%'
x2='0%'
y2='100%'
>
<stop
offset={`${maskStart + 5}%`}
stopColor='white'
stopOpacity='0'
/>
<stop
offset={`${maskStart + 35}%`}
stopColor='white'
stopOpacity='1'
/>
<stop
offset={`${maskEnd - 35}%`}
stopColor='white'
stopOpacity='1'
/>
<stop
offset={`${maskEnd - 5}%`}
stopColor='white'
stopOpacity='0'
/>
</linearGradient>
))}
<mask id='clerk-checkout-success-mask'>
{[
{ r: 225, maskStart: 10, maskEnd: 90 },
{ r: 162.5, maskStart: 15, maskEnd: 85 },
{ r: 100, maskStart: 20, maskEnd: 80 },
].map(({ r, maskStart, maskEnd }) => (
{ r: 225, id: maskId1 },
{ r: 162.5, id: maskId2 },
{ r: 100, id: maskId3 },
].map(({ r, id }) => (
<circle
key={r}
key={id}
cx='256'
cy='256'
r={r}
stroke='white'
stroke={`url(#gradient-${id})`}
fill='none'
style={{
maskImage: `linear-gradient(to bottom, transparent ${maskStart}%, black, transparent ${maskEnd}%)`,
}}
strokeWidth='1'
/>
))}
</mask>
Expand Down