diff --git a/.changeset/shaggy-garlics-remain.md b/.changeset/shaggy-garlics-remain.md
new file mode 100644
index 00000000000..99d588424a0
--- /dev/null
+++ b/.changeset/shaggy-garlics-remain.md
@@ -0,0 +1,6 @@
+---
+'@clerk/clerk-js': patch
+'@clerk/types': patch
+---
+
+Remove usage of `` from ``.
diff --git a/packages/clerk-js/src/ui/components/PricingTable/PlanCard.tsx b/packages/clerk-js/src/ui/components/PricingTable/PlanCard.tsx
deleted file mode 100644
index de5b2fee4f9..00000000000
--- a/packages/clerk-js/src/ui/components/PricingTable/PlanCard.tsx
+++ /dev/null
@@ -1,474 +0,0 @@
-import type {
- __experimental_CommercePlanResource,
- __experimental_CommerceSubscriptionPlanPeriod,
- __experimental_PricingTableProps,
-} from '@clerk/types';
-import * as React from 'react';
-
-import {
- Badge,
- Box,
- Button,
- descriptors,
- Flex,
- Heading,
- Icon,
- localizationKeys,
- SimpleButton,
- Span,
- Text,
- useAppearance,
-} from '../../customizables';
-import { Avatar, SegmentedControl } from '../../elements';
-import { usePrefersReducedMotion } from '../../hooks';
-import { Check, InformationCircle, Minus, Plus } from '../../icons';
-import type { ThemableCssProp } from '../../styledSystem';
-import { common } from '../../styledSystem';
-import { colors } from '../../utils';
-
-/* -------------------------------------------------------------------------------------------------
- * PlanCard
- * -----------------------------------------------------------------------------------------------*/
-
-interface PlanCardProps {
- plan: __experimental_CommercePlanResource;
- planPeriod: __experimental_CommerceSubscriptionPlanPeriod;
- setPlanPeriod: (p: __experimental_CommerceSubscriptionPlanPeriod) => void;
- onSelect: (plan: __experimental_CommercePlanResource) => void;
- isCompact?: boolean;
- props: __experimental_PricingTableProps;
-}
-
-export function PlanCard(props: PlanCardProps) {
- const { plan, planPeriod, setPlanPeriod, onSelect, props: pricingTableProps, isCompact = false } = props;
- const isDefaultLayout = pricingTableProps.layout === 'default';
- const ctaPosition = (isDefaultLayout && pricingTableProps.ctaPosition) || 'top';
- const collapseFeatures = (isDefaultLayout && pricingTableProps.collapseFeatures) || false;
- const { id, slug, subscriptionIdForCurrentSubscriber, features } = plan;
- const totalFeatures = features.length;
- const hasFeatures = totalFeatures > 0;
-
- return (
- ({
- display: 'flex',
- flexDirection: 'column',
- background: common.mergedColorsBackground(
- colors.setAlpha(t.colors.$colorBackground, 1),
- t.colors.$neutralAlpha50,
- ),
- borderWidth: t.borderWidths.$normal,
- borderStyle: t.borderStyles.$solid,
- borderColor: t.colors.$neutralAlpha100,
- boxShadow: !isCompact ? t.shadows.$cardBoxShadow : undefined,
- borderRadius: t.radii.$xl,
- overflow: 'hidden',
- textAlign: 'left',
- })}
- >
-
-
- {!collapseFeatures && hasFeatures ? (
- ({
- display: 'flex',
- flexDirection: 'column',
- flex: '1',
- padding: isCompact ? t.space.$3 : t.space.$4,
- backgroundColor: t.colors.$colorBackground,
- borderTopWidth: t.borderWidths.$normal,
- borderTopStyle: t.borderStyles.$solid,
- borderTopColor: t.colors.$neutralAlpha100,
- })}
- >
-
-
- ) : null}
- ({
- marginTop: 'auto',
- padding: isCompact ? t.space.$3 : t.space.$4,
- borderTopWidth: t.borderWidths.$normal,
- borderTopStyle: t.borderStyles.$solid,
- borderTopColor: t.colors.$neutralAlpha100,
- background: collapseFeatures || !hasFeatures ? t.colors.$colorBackground : undefined,
- })}
- >
-
-
-
- );
-}
-
-/* -------------------------------------------------------------------------------------------------
- * PlanCardHeader
- * -----------------------------------------------------------------------------------------------*/
-
-interface PlanCardHeaderProps {
- plan: __experimental_CommercePlanResource;
- isCompact?: boolean;
- isActivePlan?: boolean;
- planPeriod: __experimental_CommerceSubscriptionPlanPeriod;
- setPlanPeriod: (val: __experimental_CommerceSubscriptionPlanPeriod) => void;
- closeSlot?: React.ReactNode;
-}
-
-export const PlanCardHeader = React.forwardRef((props, ref) => {
- const prefersReducedMotion = usePrefersReducedMotion();
- const { animations: layoutAnimations } = useAppearance().parsedLayout;
- const { plan, isCompact, planPeriod, setPlanPeriod, closeSlot } = props;
- const { name, avatarUrl, subscriptionIdForCurrentSubscriber, annualMonthlyAmount } = plan;
- const isMotionSafe = !prefersReducedMotion && layoutAnimations === true;
- const planCardFeePeriodNoticeAnimation: ThemableCssProp = t => ({
- transition: isMotionSafe
- ? `grid-template-rows ${t.transitionDuration.$slower} ${t.transitionTiming.$slowBezier}`
- : 'none',
- });
- const getPlanFee = React.useMemo(() => {
- if (annualMonthlyAmount <= 0) {
- return plan.amountFormatted;
- }
- return planPeriod === 'annual' ? plan.annualMonthlyAmountFormatted : plan.amountFormatted;
- }, [annualMonthlyAmount, planPeriod, plan.amountFormatted, plan.annualMonthlyAmountFormatted]);
- return (
- ({
- width: '100%',
- padding: isCompact ? t.space.$3 : t.space.$4,
- })}
- >
- {avatarUrl || !!subscriptionIdForCurrentSubscriber || closeSlot ? (
- ({
- marginBlockEnd: t.space.$3,
- display: 'flex',
- alignItems: 'flex-start',
- justifyContent: 'space-between',
- flexWrap: 'wrap',
- gap: t.space.$3,
- float: !avatarUrl && !subscriptionIdForCurrentSubscriber ? 'right' : undefined,
- })}
- >
- {avatarUrl ? (
- 40}
- title={name}
- initials={name[0]}
- rounded={false}
- imageUrl={avatarUrl}
- />
- ) : null}
-
- {closeSlot}
- {subscriptionIdForCurrentSubscriber ? (
-
-
-
- ) : null}
-
-
- ) : null}
-
- {plan.name}
-
- {!isCompact && plan.description ? (
-
- {plan.description}
-
- ) : null}
- ({
- marginTop: isCompact ? t.space.$2 : t.space.$3,
- columnGap: t.space.$1x5,
- })}
- >
- {plan.hasBaseFee ? (
- <>
-
- {plan.currencySymbol}
- {getPlanFee}
-
- ({
- textTransform: 'lowercase',
- ':before': {
- content: '"/"',
- marginInlineEnd: t.space.$1,
- },
- })}
- localizationKey={localizationKeys('__experimental_commerce.month')}
- />
- {annualMonthlyAmount > 0 ? (
- ({
- width: '100%',
- display: 'grid',
- gridTemplateRows: planPeriod === 'annual' ? '1fr' : '0fr',
- }),
- planCardFeePeriodNoticeAnimation,
- ]}
- // @ts-ignore - Needed until React 19 support
- inert={planPeriod !== 'annual' ? 'true' : undefined}
- >
-
- ({
- width: '100%',
- display: 'flex',
- alignItems: 'center',
- columnGap: t.space.$1,
- })}
- >
- {' '}
-
-
-
-
- ) : null}
- >
- ) : (
-
- )}
-
- {plan.hasBaseFee && annualMonthlyAmount > 0 && setPlanPeriod ? (
- ({
- display: 'flex',
- marginTop: t.space.$3,
- })}
- >
- setPlanPeriod(value as __experimental_CommerceSubscriptionPlanPeriod)}
- >
-
-
-
-
- ) : null}
-
- );
-});
-
-/* -------------------------------------------------------------------------------------------------
- * PlanCardFeaturesList
- * -----------------------------------------------------------------------------------------------*/
-
-interface PlanCardFeaturesListProps {
- plan: __experimental_CommercePlanResource;
- /**
- * @default false
- */
- isCompact?: boolean;
- /**
- * @default `checkmark`
- */
- variant?: 'checkmark' | 'avatar';
-}
-
-export const PlanCardFeaturesList = React.forwardRef((props, ref) => {
- const { plan, isCompact, variant = 'checkmark' } = props;
- const totalFeatures = plan.features.length;
- const [showAllFeatures, setShowAllFeatures] = React.useState(false);
- const canToggleFeatures = isCompact && totalFeatures > 3;
- const toggleFeatures = () => {
- setShowAllFeatures(prev => !prev);
- };
- return (
- ({
- display: 'grid',
- flex: '1',
- rowGap: isCompact ? t.space.$2 : t.space.$3,
- })}
- >
- ({
- display: 'grid',
- flex: '1',
- rowGap: variant === 'avatar' ? t.space.$4 : isCompact ? t.space.$2 : t.space.$3,
- })}
- >
- {plan.features.slice(0, showAllFeatures || !canToggleFeatures ? totalFeatures : 3).map(feature => (
- ({
- display: 'flex',
- alignItems: 'baseline',
- gap: variant === 'checkmark' ? t.space.$2 : t.space.$3,
- })}
- >
- {variant === 'checkmark' ? (
- ({
- transform: `translateY(${t.space.$0x25})`,
- })}
- />
- ) : feature.avatarUrl ? (
- 24}
- title={feature.name}
- initials={feature.name[0]}
- rounded={false}
- imageUrl={feature.avatarUrl}
- />
- ) : null}
-
- ({
- fontWeight: variant === 'checkmark' ? t.fontWeights.$normal : t.fontWeights.$medium,
- })}
- >
- {feature.name}
-
- {variant === 'avatar' && feature.description ? (
- ({
- marginBlockStart: t.space.$0x25,
- fontSize: t.fontSizes.$xs,
- })}
- >
- {feature.description}
-
- ) : null}
-
-
- ))}
-
- {canToggleFeatures && (
- ({
- marginBlockStart: t.space.$2,
- gap: t.space.$1,
- })}
- >
-
- {showAllFeatures ? 'Hide features' : 'See all features'}
-
- )}
-
- );
-});
-
-function ReversibleContainer(props: React.PropsWithChildren<{ reverse?: boolean }>) {
- const { children, reverse } = props;
- return <>{reverse ? React.Children.toArray(children).reverse() : children}>;
-}
diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx
index 4bad13fa7e4..e873873b6b8 100644
--- a/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx
+++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx
@@ -41,7 +41,7 @@ export function PricingTableMatrix({
const pricingTableMatrixId = React.useId();
const segmentedControlId = `${pricingTableMatrixId}-segmented-control`;
- const planCardFeePeriodNoticeAnimation: ThemableCssProp = t => ({
+ const feePeriodNoticeAnimation: ThemableCssProp = t => ({
transition: isMotionSafe
? `grid-template-rows ${t.transitionDuration.$slower} ${t.transitionTiming.$slowBezier}`
: 'none',
@@ -258,7 +258,7 @@ export function PricingTableMatrix({
display: 'grid',
gridTemplateRows: planPeriod === 'annual' ? '1fr' : '0fr',
}),
- planCardFeePeriodNoticeAnimation,
+ feePeriodNoticeAnimation,
]}
// @ts-ignore - Needed until React 19 support
inert={planPeriod !== 'annual' ? 'true' : undefined}
diff --git a/packages/clerk-js/src/ui/components/PricingTable/SubscriptionDetailDrawer.tsx b/packages/clerk-js/src/ui/components/PricingTable/SubscriptionDetailDrawer.tsx
index 154f68bb905..0f10817382e 100644
--- a/packages/clerk-js/src/ui/components/PricingTable/SubscriptionDetailDrawer.tsx
+++ b/packages/clerk-js/src/ui/components/PricingTable/SubscriptionDetailDrawer.tsx
@@ -1,15 +1,28 @@
import type {
+ __experimental_CommercePlanResource,
__experimental_CommerceSubscriptionPlanPeriod,
__experimental_CommerceSubscriptionResource,
ClerkAPIError,
ClerkRuntimeError,
} from '@clerk/types';
import { useState } from 'react';
+import * as React from 'react';
-import { Box, Button, descriptors, Heading, Text } from '../../customizables';
-import { Alert, Drawer } from '../../elements';
+import {
+ Badge,
+ Box,
+ Button,
+ descriptors,
+ Flex,
+ Heading,
+ Icon,
+ localizationKeys,
+ Span,
+ Text,
+} from '../../customizables';
+import { Alert, Avatar, Drawer, ReversibleContainer } from '../../elements';
+import { InformationCircle } from '../../icons';
import { handleError } from '../../utils';
-import { PlanCardFeaturesList, PlanCardHeader } from './PlanCard';
type DrawerRootProps = React.ComponentProps;
@@ -38,7 +51,8 @@ export function SubscriptionDetailDrawer({
if (!subscription) {
return null;
}
- const hasFeatures = subscription.plan.features.length > 0;
+ const features = subscription.plan.features;
+ const hasFeatures = features.length > 0;
const cancelSubscription = async () => {
setCancelError(undefined);
setIsSubmitting(true);
@@ -76,7 +90,7 @@ export function SubscriptionDetailDrawer({
: null
}
>
-
({
+ display: 'grid',
+ rowGap: t.space.$4,
padding: t.space.$4,
})}
>
-
+ {features.map(feature => (
+ ({
+ display: 'flex',
+ alignItems: 'baseline',
+ gap: t.space.$3,
+ })}
+ >
+ {feature.avatarUrl ? (
+ 24}
+ title={feature.name}
+ initials={feature.name[0]}
+ rounded={false}
+ imageUrl={feature.avatarUrl}
+ />
+ ) : null}
+
+ ({
+ fontWeight: t.fontWeights.$medium,
+ })}
+ >
+ {feature.name}
+
+ {feature.description ? (
+ ({
+ marginBlockStart: t.space.$0x25,
+ fontSize: t.fontSizes.$xs,
+ })}
+ >
+ {feature.description}
+
+ ) : null}
+
+
+ ))}
) : null}
@@ -171,3 +231,180 @@ export function SubscriptionDetailDrawer({
);
}
+
+/* -------------------------------------------------------------------------------------------------
+ * Header
+ * -----------------------------------------------------------------------------------------------*/
+
+interface HeaderProps {
+ plan: __experimental_CommercePlanResource;
+ isActivePlan?: boolean;
+ planPeriod: __experimental_CommerceSubscriptionPlanPeriod;
+ setPlanPeriod: (val: __experimental_CommerceSubscriptionPlanPeriod) => void;
+ closeSlot?: React.ReactNode;
+}
+
+const Header = React.forwardRef((props, ref) => {
+ const { plan, planPeriod, closeSlot } = props;
+ const { name, avatarUrl, subscriptionIdForCurrentSubscriber, annualMonthlyAmount } = plan;
+ const getPlanFee = React.useMemo(() => {
+ if (annualMonthlyAmount <= 0) {
+ return plan.amountFormatted;
+ }
+ return planPeriod === 'annual' ? plan.annualMonthlyAmountFormatted : plan.amountFormatted;
+ }, [annualMonthlyAmount, planPeriod, plan.amountFormatted, plan.annualMonthlyAmountFormatted]);
+
+ return (
+ ({
+ width: '100%',
+ padding: t.space.$4,
+ })}
+ >
+ {avatarUrl || !!subscriptionIdForCurrentSubscriber || closeSlot ? (
+ ({
+ marginBlockEnd: t.space.$3,
+ display: 'flex',
+ alignItems: 'flex-start',
+ justifyContent: 'space-between',
+ flexWrap: 'wrap',
+ gap: t.space.$3,
+ float: !avatarUrl && !subscriptionIdForCurrentSubscriber ? 'right' : undefined,
+ })}
+ >
+ {avatarUrl ? (
+ 40}
+ title={name}
+ initials={name[0]}
+ rounded={false}
+ imageUrl={avatarUrl}
+ />
+ ) : null}
+
+ {closeSlot}
+ {subscriptionIdForCurrentSubscriber ? (
+
+
+
+ ) : null}
+
+
+ ) : null}
+
+ {plan.name}
+
+ {plan.description ? (
+
+ {plan.description}
+
+ ) : null}
+ ({
+ marginTop: t.space.$3,
+ columnGap: t.space.$1x5,
+ })}
+ >
+ {plan.hasBaseFee ? (
+ <>
+
+ {plan.currencySymbol}
+ {getPlanFee}
+
+ ({
+ textTransform: 'lowercase',
+ ':before': {
+ content: '"/"',
+ marginInlineEnd: t.space.$1,
+ },
+ })}
+ localizationKey={localizationKeys('__experimental_commerce.month')}
+ />
+ {annualMonthlyAmount > 0 ? (
+ ({
+ width: '100%',
+ display: 'grid',
+ gridTemplateRows: planPeriod === 'annual' ? '1fr' : '0fr',
+ }),
+ ]}
+ // @ts-ignore - Needed until React 19 support
+ inert={planPeriod !== 'annual' ? 'true' : undefined}
+ >
+
+ ({
+ width: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ columnGap: t.space.$1,
+ })}
+ >
+ {' '}
+
+
+
+
+ ) : null}
+ >
+ ) : (
+
+ )}
+
+
+ );
+});
diff --git a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
index 55d1227815d..9658dc0ad4d 100644
--- a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
+++ b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
@@ -235,31 +235,6 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
'accountSwitcherActionButtonIconBox',
'accountSwitcherActionButtonIcon',
- 'planCard',
- 'planCardDefault',
- 'planCardCompact',
- 'planCardHeader',
- 'planCardTitle',
- 'planCardDescription',
- 'planCardAvatarBadgeContainer',
- 'planCardAvatar',
- 'planCardBadgeContainer',
- 'planCardBadge',
- 'planCardFeatures',
- 'planCardFeaturesList',
- 'planCardFeaturesListItem',
- 'planCardFeaturesListItemContent',
- 'planCardFeaturesListItemTitle',
- 'planCardFeaturesListItemDescription',
- 'planCardAction',
- 'planCardPeriodToggle',
- 'planCardFeeContainer',
- 'planCardFee',
- 'planCardFeePeriod',
- 'planCardFeePeriodNotice',
- 'planCardFeePeriodNoticeInner',
- 'planCardFeePeriodNoticeLabel',
-
'pricingTable',
'pricingTableCard',
'pricingTableCardHeader',
@@ -305,6 +280,27 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
'pricingTableMatrixFeePeriodNoticeLabel',
'pricingTableMatrixFooter',
+ 'subscriptionDetailHeader',
+ 'subscriptionDetailAvatarBadgeContainer',
+ 'subscriptionDetailAvatar',
+ 'subscriptionDetailBadgeContainer',
+ 'subscriptionDetailBadge',
+ 'subscriptionDetailTitle',
+ 'subscriptionDetailDescription',
+ 'subscriptionDetailAction',
+ 'subscriptionDetailFeeContainer',
+ 'subscriptionDetailFee',
+ 'subscriptionDetailFeePeriod',
+ 'subscriptionDetailFeePeriodNotice',
+ 'subscriptionDetailFeePeriodNoticeInner',
+ 'subscriptionDetailFeePeriodNoticeLabel',
+ 'subscriptionDetailFeatures',
+ 'subscriptionDetailFeaturesList',
+ 'subscriptionDetailFeaturesListItem',
+ 'subscriptionDetailFeaturesListItemContent',
+ 'subscriptionDetailFeaturesListItemTitle',
+ 'subscriptionDetailFeaturesListItemDescription',
+
'segmentedControlRoot',
'segmentedControlButton',
diff --git a/packages/clerk-js/src/ui/polishedAppearance.ts b/packages/clerk-js/src/ui/polishedAppearance.ts
index ea4f43792e1..5315d127be4 100644
--- a/packages/clerk-js/src/ui/polishedAppearance.ts
+++ b/packages/clerk-js/src/ui/polishedAppearance.ts
@@ -241,13 +241,6 @@ export const polishedAppearance: Appearance = {
boxShadow: 'none',
},
},
- planCardDefault: {
- borderWidth: 0,
- boxShadow: `${theme.shadows.$cardBoxShadow}, ${BORDER_SHADOW_LENGTH} ${theme.colors.$neutralAlpha100}`,
- },
- planCardCompact: {
- boxShadow: 'none',
- },
scrollBox: {
...cardContentStyles(theme),
},
diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts
index e407169cd87..a992b7dfbef 100644
--- a/packages/types/src/appearance.ts
+++ b/packages/types/src/appearance.ts
@@ -407,30 +407,26 @@ export type ElementsConfig = {
pricingTableMatrixFeePeriodNoticeLabel: WithOptions;
pricingTableMatrixFooter: WithOptions;
- planCard: WithOptions;
- planCardDefault: WithOptions;
- planCardCompact: WithOptions;
- planCardHeader: WithOptions;
- planCardAvatarBadgeContainer: WithOptions;
- planCardAvatar: WithOptions;
- planCardBadgeContainer: WithOptions;
- planCardBadge: WithOptions;
- planCardTitle: WithOptions;
- planCardDescription: WithOptions;
- planCardFeatures: WithOptions;
- planCardFeaturesList: WithOptions;
- planCardFeaturesListItem: WithOptions;
- planCardFeaturesListItemContent: WithOptions;
- planCardFeaturesListItemTitle: WithOptions;
- planCardFeaturesListItemDescription: WithOptions;
- planCardAction: WithOptions;
- planCardPeriodToggle: WithOptions;
- planCardFeeContainer: WithOptions;
- planCardFee: WithOptions;
- planCardFeePeriod: WithOptions;
- planCardFeePeriodNotice: WithOptions;
- planCardFeePeriodNoticeInner: WithOptions;
- planCardFeePeriodNoticeLabel: WithOptions;
+ subscriptionDetailHeader: WithOptions;
+ subscriptionDetailAvatarBadgeContainer: WithOptions;
+ subscriptionDetailAvatar: WithOptions;
+ subscriptionDetailBadgeContainer: WithOptions;
+ subscriptionDetailBadge: WithOptions;
+ subscriptionDetailTitle: WithOptions;
+ subscriptionDetailDescription: WithOptions;
+ subscriptionDetailAction: WithOptions;
+ subscriptionDetailFeeContainer: WithOptions;
+ subscriptionDetailFee: WithOptions;
+ subscriptionDetailFeePeriod: WithOptions;
+ subscriptionDetailFeePeriodNotice: WithOptions;
+ subscriptionDetailFeePeriodNoticeInner: WithOptions;
+ subscriptionDetailFeePeriodNoticeLabel: WithOptions;
+ subscriptionDetailFeatures: WithOptions;
+ subscriptionDetailFeaturesList: WithOptions;
+ subscriptionDetailFeaturesListItem: WithOptions;
+ subscriptionDetailFeaturesListItemContent: WithOptions;
+ subscriptionDetailFeaturesListItemTitle: WithOptions;
+ subscriptionDetailFeaturesListItemDescription: WithOptions;
alert: WithOptions;
alertIcon: WithOptions;