From eca1ec97838a6cf7d05f84655b11bc817c60dd61 Mon Sep 17 00:00:00 2001
From: Marcel Cruz <12413903+marcelscruz@users.noreply.github.com>
Date: Mon, 27 Nov 2023 16:57:20 +0100
Subject: [PATCH] feat(clerk-js): Retheme OAuth buttons (#2139)
* feat(clerk-js): Retheme OAuth buttons
* fix(clerk-js): Add missing descriptors on OAuth block button
* chore(clerk-js): Move ResizeObserver to its own hook
* chore(clerk-js): Add descriptor to all
* chore(clerk-js): Move `distributeStrategiesIntoRows` to separate utils file
---------
Co-authored-by: Marcel <{ID}+{username}@users.noreply.github.com>
---
.changeset/friendly-months-film.md | 2 +
.changeset/nice-dingos-argue.md | 2 +
.changeset/rotten-ways-do.md | 2 +
.changeset/silly-emus-boil.md | 2 +
.../ui.retheme/common/EmailLinkStatusCard.tsx | 1 +
.../ui.retheme/common/InfiniteListSpinner.tsx | 3 +-
.../OrganizationList/OrganizationListPage.tsx | 1 +
.../components/OrganizationList/shared.tsx | 1 +
.../OrganizationProfile/DomainList.tsx | 3 +-
.../OrganizationProfile/MemberListTable.tsx | 1 +
.../OrganizationProfile/RemoveDomainPage.tsx | 3 +-
.../VerifiedDomainPage.tsx | 3 +-
.../OrganizationProfile/VerifyDomainPage.tsx | 1 +
.../SignIn/ResetPasswordSuccess.tsx | 5 +-
.../customizables/elementDescriptors.ts | 1 +
.../src/ui.retheme/elements/Actions.tsx | 7 +-
.../src/ui.retheme/elements/CodeControl.tsx | 1 +
.../ui.retheme/elements/FullHeightLoader.tsx | 3 +-
.../src/ui.retheme/elements/LoadingCard.tsx | 1 +
.../src/ui.retheme/elements/SocialButtons.tsx | 233 ++++++++++++------
.../clerk-js/src/ui.retheme/elements/utils.ts | 22 ++
.../src/ui.retheme/foundations/sizes.ts | 1 +
.../clerk-js/src/ui.retheme/hooks/index.ts | 1 +
.../src/ui.retheme/hooks/useResizeObserver.ts | 57 +++++
.../src/ui.retheme/primitives/Button.tsx | 27 +-
25 files changed, 285 insertions(+), 99 deletions(-)
create mode 100644 .changeset/friendly-months-film.md
create mode 100644 .changeset/nice-dingos-argue.md
create mode 100644 .changeset/rotten-ways-do.md
create mode 100644 .changeset/silly-emus-boil.md
create mode 100644 packages/clerk-js/src/ui.retheme/elements/utils.ts
create mode 100644 packages/clerk-js/src/ui.retheme/hooks/useResizeObserver.ts
diff --git a/.changeset/friendly-months-film.md b/.changeset/friendly-months-film.md
new file mode 100644
index 0000000000..a845151cc8
--- /dev/null
+++ b/.changeset/friendly-months-film.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/nice-dingos-argue.md b/.changeset/nice-dingos-argue.md
new file mode 100644
index 0000000000..a845151cc8
--- /dev/null
+++ b/.changeset/nice-dingos-argue.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/rotten-ways-do.md b/.changeset/rotten-ways-do.md
new file mode 100644
index 0000000000..a845151cc8
--- /dev/null
+++ b/.changeset/rotten-ways-do.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/silly-emus-boil.md b/.changeset/silly-emus-boil.md
new file mode 100644
index 0000000000..a845151cc8
--- /dev/null
+++ b/.changeset/silly-emus-boil.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/packages/clerk-js/src/ui.retheme/common/EmailLinkStatusCard.tsx b/packages/clerk-js/src/ui.retheme/common/EmailLinkStatusCard.tsx
index 953a707026..06e2752ae6 100644
--- a/packages/clerk-js/src/ui.retheme/common/EmailLinkStatusCard.tsx
+++ b/packages/clerk-js/src/ui.retheme/common/EmailLinkStatusCard.tsx
@@ -61,6 +61,7 @@ const StatusRow = (props: { status: VerificationStatus }) => {
size='xl'
colorScheme='primary'
sx={theme => ({ margin: `${theme.space.$12} 0` })}
+ elementDescriptor={descriptors.spinner}
/>
) : (
<>
diff --git a/packages/clerk-js/src/ui.retheme/common/InfiniteListSpinner.tsx b/packages/clerk-js/src/ui.retheme/common/InfiniteListSpinner.tsx
index 1a359c4ebe..97ae637e73 100644
--- a/packages/clerk-js/src/ui.retheme/common/InfiniteListSpinner.tsx
+++ b/packages/clerk-js/src/ui.retheme/common/InfiniteListSpinner.tsx
@@ -1,6 +1,6 @@
import { forwardRef } from 'react';
-import { Box, Spinner } from '../customizables';
+import { Box, descriptors, Spinner } from '../customizables';
export const InfiniteListSpinner = forwardRef((_, ref) => {
return (
@@ -24,6 +24,7 @@ export const InfiniteListSpinner = forwardRef((_, ref) => {
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationList/OrganizationListPage.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationList/OrganizationListPage.tsx
index 509eb62abd..87dcd8a807 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationList/OrganizationListPage.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationList/OrganizationListPage.tsx
@@ -67,6 +67,7 @@ export const OrganizationListPage = withCardStateProvider(() => {
)}
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationList/shared.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationList/shared.tsx
index e3e0d7df8e..7467687a2c 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationList/shared.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationList/shared.tsx
@@ -85,6 +85,7 @@ export const PreviewListSpinner = forwardRef((_, ref) => {
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/DomainList.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/DomainList.tsx
index b782829b78..b1178894ba 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/DomainList.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/DomainList.tsx
@@ -10,7 +10,7 @@ import { stripOrigin, toURL, trimLeadingSlash } from '../../../utils';
import { useGate, withGate } from '../../common';
import { useCoreOrganization } from '../../contexts';
import type { LocalizationKey } from '../../customizables';
-import { Box, Col, localizationKeys, Spinner } from '../../customizables';
+import { Box, Col, descriptors, localizationKeys, Spinner } from '../../customizables';
import { ArrowBlockButton, BlockWithTrailingComponent, ThreeDotsMenu } from '../../elements';
import { useInView } from '../../hooks';
import { useRouter } from '../../router';
@@ -193,6 +193,7 @@ export const DomainList = withGate(
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/MemberListTable.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/MemberListTable.tsx
index efdd2d5207..98f33d6faa 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/MemberListTable.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/MemberListTable.tsx
@@ -60,6 +60,7 @@ export const DataTable = (props: MembersListTableProps) => {
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/RemoveDomainPage.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/RemoveDomainPage.tsx
index 4da8c1a2c0..50f0aaec49 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/RemoveDomainPage.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/RemoveDomainPage.tsx
@@ -3,7 +3,7 @@ import React from 'react';
import { RemoveResourcePage } from '../../common';
import { useCoreOrganization, useEnvironment } from '../../contexts';
-import { Flex, Spinner } from '../../customizables';
+import { descriptors, Flex, Spinner } from '../../customizables';
import { useFetch } from '../../hooks';
import { localizationKeys } from '../../localization';
import { useRouter } from '../../router';
@@ -51,6 +51,7 @@ export const RemoveDomainPage = () => {
);
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx
index c6283731d6..84e7119544 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx
@@ -7,7 +7,7 @@ import type {
import { CalloutWithAction, useGate } from '../../common';
import { useCoreOrganization, useEnvironment } from '../../contexts';
import type { LocalizationKey } from '../../customizables';
-import { Col, Flex, localizationKeys, Spinner, Text } from '../../customizables';
+import { Col, descriptors, Flex, localizationKeys, Spinner, Text } from '../../customizables';
import {
ContentPage,
Form,
@@ -189,6 +189,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => {
);
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx
index f94b205255..c68fba57a4 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx
@@ -114,6 +114,7 @@ export const VerifyDomainPage = withCardStateProvider(() => {
);
diff --git a/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPasswordSuccess.tsx b/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPasswordSuccess.tsx
index d34d2345b3..210b49bb7f 100644
--- a/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPasswordSuccess.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPasswordSuccess.tsx
@@ -1,8 +1,8 @@
import { withRedirectToHomeSingleSessionGuard } from '../../common';
-import { Col, descriptors, localizationKeys, Text } from '../../customizables';
+import { Col, descriptors, localizationKeys, Spinner, Text } from '../../customizables';
import { Card, CardAlert, Header, useCardState, withCardStateProvider } from '../../elements';
import { useSetSessionWithTimeout } from '../../hooks/useSetSessionWithTimeout';
-import { Flex, Spinner } from '../../primitives';
+import { Flex } from '../../primitives';
export const _ResetPasswordSuccess = () => {
const card = useCardState();
@@ -29,6 +29,7 @@ export const _ResetPasswordSuccess = () => {
diff --git a/packages/clerk-js/src/ui.retheme/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui.retheme/customizables/elementDescriptors.ts
index 1434238073..0c35a94837 100644
--- a/packages/clerk-js/src/ui.retheme/customizables/elementDescriptors.ts
+++ b/packages/clerk-js/src/ui.retheme/customizables/elementDescriptors.ts
@@ -279,6 +279,7 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
'notificationBadge',
'button',
'providerIcon',
+ 'spinner',
// Decide if we want to keep the keys as camel cased in HTML as well,
// if yes, refactor and remove the .map(camelize) method
] as const).map(camelize) as (keyof ElementsConfig)[];
diff --git a/packages/clerk-js/src/ui.retheme/elements/Actions.tsx b/packages/clerk-js/src/ui.retheme/elements/Actions.tsx
index 434b61f4fa..60f5aa7b2e 100644
--- a/packages/clerk-js/src/ui.retheme/elements/Actions.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/Actions.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import type { LocalizationKey } from '../customizables';
-import { Button, Col, Flex, Icon, Spinner, Text } from '../customizables';
+import { Button, Col, descriptors, Flex, Icon, Spinner, Text } from '../customizables';
import type { ElementDescriptor, ElementId } from '../customizables/elementDescriptors';
import { useCardState } from '../elements/contexts';
import { useLoadingStatus } from '../hooks';
@@ -96,7 +96,10 @@ export const Action = (props: ActionProps) => {
sx={theme => ({ flex: `0 0 ${theme.sizes.$11}` })}
>
{status.isLoading ? (
-
+
) : (
((_, ref) => {
({ marginLeft: theme.space.$2 })}
+ elementDescriptor={descriptors.spinner}
/>
)}
diff --git a/packages/clerk-js/src/ui.retheme/elements/FullHeightLoader.tsx b/packages/clerk-js/src/ui.retheme/elements/FullHeightLoader.tsx
index 0422a7f1cc..a40bda53ed 100644
--- a/packages/clerk-js/src/ui.retheme/elements/FullHeightLoader.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/FullHeightLoader.tsx
@@ -1,4 +1,4 @@
-import { Flex, Spinner } from '../customizables';
+import { descriptors, Flex, Spinner } from '../customizables';
export const FullHeightLoader = (): JSX.Element => {
return (
@@ -9,6 +9,7 @@ export const FullHeightLoader = (): JSX.Element => {
);
diff --git a/packages/clerk-js/src/ui.retheme/elements/LoadingCard.tsx b/packages/clerk-js/src/ui.retheme/elements/LoadingCard.tsx
index b3f9af49f7..6282001487 100644
--- a/packages/clerk-js/src/ui.retheme/elements/LoadingCard.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/LoadingCard.tsx
@@ -21,6 +21,7 @@ export const LoadingCardContainer = ({ children }: PropsWithChildren) => {
{children}
diff --git a/packages/clerk-js/src/ui.retheme/elements/SocialButtons.tsx b/packages/clerk-js/src/ui.retheme/elements/SocialButtons.tsx
index a267a383a1..a7e05e1a4e 100644
--- a/packages/clerk-js/src/ui.retheme/elements/SocialButtons.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/SocialButtons.tsx
@@ -1,14 +1,30 @@
import type { OAuthProvider, OAuthStrategy, Web3Provider, Web3Strategy } from '@clerk/types';
-import React from 'react';
-
-import { Button, descriptors, Grid, Image, localizationKeys, useAppearance } from '../customizables';
-import { useEnabledThirdPartyProviders } from '../hooks';
+import type { Ref } from 'react';
+import React, { forwardRef, isValidElement } from 'react';
+
+import type { LocalizationKey } from '../customizables';
+import {
+ Button,
+ descriptors,
+ Flex,
+ Grid,
+ Icon,
+ Image,
+ localizationKeys,
+ SimpleButton,
+ Spinner,
+ Text,
+ useAppearance,
+} from '../customizables';
+import { useEnabledThirdPartyProviders, useResizeObserver } from '../hooks';
import type { PropsOfComponent } from '../styledSystem';
import { sleep } from '../utils';
-import { ArrowBlockButton } from './ArrowBlockButton';
import { useCardState } from './contexts';
+import { distributeStrategiesIntoRows } from './utils';
const SOCIAL_BUTTON_BLOCK_THRESHOLD = 2;
+const SOCIAL_BUTTON_PRE_TEXT_THRESHOLD = 1;
+const MAX_STRATEGIES_PER_ROW = 6;
export type SocialButtonsProps = React.PropsWithChildren<{
enableOAuthProviders: boolean;
@@ -29,6 +45,7 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
const { web3Strategies, authenticatableOauthStrategies, strategyToDisplayData } = useEnabledThirdPartyProviders();
const card = useCardState();
const { socialButtonsVariant } = useAppearance().parsedLayout;
+ const [firstStrategyRef, firstElementRect] = useResizeObserver();
const strategies = [
...(enableOAuthProviders ? authenticatableOauthStrategies : []),
@@ -39,6 +56,8 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
return null;
}
+ const strategyRows = distributeStrategiesIntoRows([...strategies], MAX_STRATEGIES_PER_ROW);
+
const preferBlockButtons =
socialButtonsVariant === 'blockButton'
? true
@@ -63,112 +82,172 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
};
const ButtonElement = preferBlockButtons ? SocialButtonBlock : SocialButtonIcon;
- const WrapperElement = preferBlockButtons ? ButtonRows : ButtonGrid;
-
- return (
-
- {strategies.map(strategy => (
- ({ width: theme.sizes.$5, height: 'auto', maxWidth: '100%' })}
- />
- }
- />
- ))}
-
- );
-});
-
-const ButtonGrid = (props: React.PropsWithChildren) => {
- return (
- ({
- gridTemplateColumns: `repeat(auto-fit, minmax(${t.sizes.$12}, 1fr))`,
- gridAutoRows: t.sizes.$12,
- })}
- >
- {props.children}
-
- );
-};
-const ButtonRows = (props: React.PropsWithChildren) => {
return (
-
- {props.children}
-
+ {strategyRows.map((row, rowIndex) => (
+
+ {row.map((strategy, strategyIndex) => {
+ const label =
+ strategies.length === SOCIAL_BUTTON_PRE_TEXT_THRESHOLD
+ ? `Continue with ${strategyToDisplayData[strategy].name}`
+ : strategyToDisplayData[strategy].name;
+
+ const localizedText =
+ strategies.length === SOCIAL_BUTTON_PRE_TEXT_THRESHOLD
+ ? localizationKeys('socialButtonsBlockButton', {
+ provider: strategyToDisplayData[strategy].name,
+ })
+ : undefined;
+
+ // When strategies break into 2 rows or more, use the first item of the first
+ // row as reference for the width of the buttons in the second row and beyond
+ const ref =
+ strategies.length > MAX_STRATEGIES_PER_ROW && rowIndex === 0 && strategyIndex === 0
+ ? firstStrategyRef
+ : null;
+
+ return (
+ ({ width: theme.sizes.$4, height: 'auto', maxWidth: '100%' })}
+ />
+ }
+ />
+ );
+ })}
+
+ ))}
+
);
-};
+});
type SocialButtonProps = PropsOfComponent & {
icon: React.ReactElement;
id: OAuthProvider | Web3Provider;
- providerName: string;
+ textLocalizationKey: LocalizationKey | undefined;
label?: string;
};
-const SocialButtonIcon = (props: SocialButtonProps): JSX.Element => {
- const { icon, label, id, providerName, ...rest } = props;
+const SocialButtonIcon = forwardRef((props: SocialButtonProps, ref: Ref | null): JSX.Element => {
+ const { icon, label, id, textLocalizationKey, ...rest } = props;
+
return (
);
-};
+});
const SocialButtonBlock = (props: SocialButtonProps): JSX.Element => {
- const { label, id, providerName, sx, icon, ...rest } = props;
+ const { id, icon, isLoading, label, textLocalizationKey, ...rest } = props;
+ const isIconElement = isValidElement(icon);
return (
- [
{
- textOverflow: 'ellipsis',
- overflow: 'hidden',
+ gap: theme.space.$4,
+ position: 'relative',
+ justifyContent: 'flex-start',
+ borderColor: theme.colors.$blackAlpha200,
},
- sx,
+ props.sx,
]}
- {...rest}
>
- {label}
-
+
+ {(isLoading || icon) && (
+ ({ flex: `0 0 ${theme.space.$4}` })}
+ >
+ {isLoading ? (
+
+ ) : !isIconElement && icon ? (
+ ({
+ color: theme.colors.$blackAlpha600,
+ width: theme.sizes.$4,
+ position: 'absolute',
+ }),
+ ]}
+ />
+ ) : (
+ icon
+ )}
+
+ )}
+
+ {label}
+
+
+
);
};
diff --git a/packages/clerk-js/src/ui.retheme/elements/utils.ts b/packages/clerk-js/src/ui.retheme/elements/utils.ts
new file mode 100644
index 0000000000..83a094095d
--- /dev/null
+++ b/packages/clerk-js/src/ui.retheme/elements/utils.ts
@@ -0,0 +1,22 @@
+// This function evenly distributes the strategies into rows (the inner arrays).
+// This is done by calculating the number of necessary rows, then the amount of strategies in each row, and then distributing the strategies into the rows.
+// Example of 5 strategies with max 3 per row: [ [1, 2, 3], [4, 5] ]
+export function distributeStrategiesIntoRows(strategies: T[], maxStrategiesPerRow: number): T[][] {
+ if (strategies.length <= maxStrategiesPerRow) return [strategies];
+
+ const numRows = Math.ceil(strategies.length / maxStrategiesPerRow);
+ const strategiesPerRow = Math.ceil(strategies.length / numRows);
+ const rows: T[][] = Array.from({ length: numRows }, () => []);
+
+ let currentArrayIndex = 0;
+
+ for (const strategy of strategies) {
+ rows[currentArrayIndex].push(strategy);
+
+ if (rows[currentArrayIndex].length === strategiesPerRow) {
+ currentArrayIndex++;
+ }
+ }
+
+ return rows;
+}
diff --git a/packages/clerk-js/src/ui.retheme/foundations/sizes.ts b/packages/clerk-js/src/ui.retheme/foundations/sizes.ts
index 0bdaa9c2e8..27c4f7ae85 100644
--- a/packages/clerk-js/src/ui.retheme/foundations/sizes.ts
+++ b/packages/clerk-js/src/ui.retheme/foundations/sizes.ts
@@ -16,6 +16,7 @@ const dynamicSpaceUnits = Object.freeze({
'5': '1.25rem',
'6': '1.5rem',
'7': '1.75rem',
+ '7x5': '1.875rem',
'8': '2rem',
'9': '2.25rem',
'9x5': '2.375rem',
diff --git a/packages/clerk-js/src/ui.retheme/hooks/index.ts b/packages/clerk-js/src/ui.retheme/hooks/index.ts
index d0527e8808..5679be23e3 100644
--- a/packages/clerk-js/src/ui.retheme/hooks/index.ts
+++ b/packages/clerk-js/src/ui.retheme/hooks/index.ts
@@ -12,6 +12,7 @@ export * from './usePasswordComplexity';
export * from './usePopover';
export * from './usePrefersReducedMotion';
export * from './useLocalStorage';
+export * from './useResizeObserver';
export * from './useSafeState';
export * from './useSearchInput';
export * from './useDebounce';
diff --git a/packages/clerk-js/src/ui.retheme/hooks/useResizeObserver.ts b/packages/clerk-js/src/ui.retheme/hooks/useResizeObserver.ts
new file mode 100644
index 0000000000..f861ea78e5
--- /dev/null
+++ b/packages/clerk-js/src/ui.retheme/hooks/useResizeObserver.ts
@@ -0,0 +1,57 @@
+import { useEffect, useMemo, useRef, useState } from 'react';
+
+type ObserverRect = Omit;
+
+export function useResizeObserver() {
+ const frameID = useRef(0);
+ const ref = useRef(null);
+
+ const [rect, setRect] = useState({
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ });
+
+ const observer = useMemo(
+ () =>
+ typeof window !== 'undefined'
+ ? new ResizeObserver((entries: any) => {
+ const entry = entries[0];
+
+ if (entry) {
+ cancelAnimationFrame(frameID.current);
+
+ frameID.current = requestAnimationFrame(() => {
+ if (ref.current) {
+ setRect(ref.current.getBoundingClientRect());
+ }
+ });
+ }
+ })
+ : null,
+ [],
+ );
+
+ useEffect(() => {
+ if (ref.current) {
+ observer?.observe(ref.current);
+ }
+
+ return () => {
+ observer?.disconnect();
+
+ if (frameID.current) {
+ cancelAnimationFrame(frameID.current);
+ }
+ };
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [ref.current]);
+
+ return [ref, rect] as const;
+}
diff --git a/packages/clerk-js/src/ui.retheme/primitives/Button.tsx b/packages/clerk-js/src/ui.retheme/primitives/Button.tsx
index fefb5e3b7b..feafa10a8a 100644
--- a/packages/clerk-js/src/ui.retheme/primitives/Button.tsx
+++ b/packages/clerk-js/src/ui.retheme/primitives/Button.tsx
@@ -1,13 +1,12 @@
import React from 'react';
-import { Icon } from '../customizables';
+import { descriptors, Icon, Spinner } from '../customizables';
import { ArrowRightButtonIcon } from '../icons';
import type { PrimitiveProps, StyleVariants } from '../styledSystem';
import { common, createCssVariables, createVariants } from '../styledSystem';
import { colors } from '../utils';
import { applyDataStateProps } from './applyDataStateProps';
import { Flex } from './Flex';
-import { Spinner } from './Spinner';
const vars = createCssVariables('accent', 'accentDark', 'accentDarker', 'accentLighter', 'accentLightest', 'border');
@@ -62,11 +61,11 @@ const { applyVariants, filterProps } = createVariants((theme, props: OwnProps) =
},
neutral: {
[vars.border]: theme.colors.$blackAlpha200,
- [vars.accentLightest]: theme.colors.$blackAlpha50,
- [vars.accentLighter]: theme.colors.$blackAlpha300,
- [vars.accent]: theme.colors.$colorText,
- [vars.accentDark]: theme.colors.$blackAlpha600,
- [vars.accentDarker]: theme.colors.$blackAlpha700,
+ [vars.accentLightest]: theme.colors.$blackAlpha50, // TODO: once we have the new color palette and style for pseudo classes
+ [vars.accentLighter]: theme.colors.$blackAlpha300, // TODO: once we have the new color palette and style for pseudo classes
+ [vars.accent]: theme.colors.$primary800, // WIP reference: Updated to new color palette; previously `$colorText`
+ [vars.accentDark]: theme.colors.$blackAlpha600, // TODO: once we have the new color palette and style for pseudo classes
+ [vars.accentDarker]: theme.colors.$blackAlpha700, // TODO: once we have the new color palette and style for pseudo classes
},
},
variant: {
@@ -87,9 +86,9 @@ const { applyVariants, filterProps } = createVariants((theme, props: OwnProps) =
},
},
outline: {
- border: theme.borders.$normal,
- borderColor: vars.accentLighter,
color: vars.accent,
+ boxShadow:
+ '0px 2px 3px -1px rgba(0, 0, 0, 0.08), 0px 1px 0px 0px rgba(25, 28, 33, 0.02), 0px 0px 0px 1px rgba(25, 28, 33, 0.08)', // TODO: Move to theme once we have the shadows defined
'&:hover': { backgroundColor: vars.accentLightest },
'&:focus': props.hoverAsFocus ? { backgroundColor: vars.accentLightest } : undefined,
'&:active': { backgroundColor: vars.accentLighter },
@@ -102,9 +101,8 @@ const { applyVariants, filterProps } = createVariants((theme, props: OwnProps) =
},
icon: {
color: vars.accent,
- border: theme.borders.$normal,
- borderRadius: theme.radii.$lg,
- borderColor: vars.border,
+ boxShadow:
+ '0px 2px 3px -1px rgba(0, 0, 0, 0.08), 0px 1px 0px 0px rgba(25, 28, 33, 0.02), 0px 0px 0px 1px rgba(25, 28, 33, 0.08)', // TODO: Move to theme once we have the shadows defined
'&:hover': { backgroundColor: vars.accentLightest },
'&:focus': props.hoverAsFocus ? { backgroundColor: vars.accentLightest } : undefined,
'&:active': { backgroundColor: vars.accentLighter },
@@ -201,8 +199,11 @@ const Button = React.forwardRef((props, ref) =>
}}
>
{loadingText || {children}}