diff --git a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
index 3ebc8557552..a6907c7cf71 100644
--- a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
+++ b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
@@ -49,6 +49,7 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
'socialButtonsRoot',
'socialButtons',
+ 'socialButtonsColumn',
'socialButtonsIconButton',
'socialButtonsBlockButton',
'socialButtonsBlockButtonText',
diff --git a/packages/clerk-js/src/ui/elements/SocialButtons.tsx b/packages/clerk-js/src/ui/elements/SocialButtons.tsx
index 584d2ba8738..99fb4215128 100644
--- a/packages/clerk-js/src/ui/elements/SocialButtons.tsx
+++ b/packages/clerk-js/src/ui/elements/SocialButtons.tsx
@@ -5,6 +5,7 @@ import React, { forwardRef, isValidElement } from 'react';
import { ProviderInitialIcon } from '../common';
import type { LocalizationKey } from '../customizables';
import {
+ Box,
Button,
descriptors,
Flex,
@@ -18,10 +19,10 @@ import {
useAppearance,
} from '../customizables';
import { useEnabledThirdPartyProviders, useResizeObserver } from '../hooks';
-import { mqu, type PropsOfComponent } from '../styledSystem';
+import { mq, mqu, type PropsOfComponent } from '../styledSystem';
import { sleep } from '../utils';
import { useCardState } from './contexts';
-import { distributeStrategiesIntoRows } from './utils';
+import { distributeStrategiesIntoRows, getColumnCount } from './utils';
const SOCIAL_BUTTON_BLOCK_THRESHOLD = 2;
const SOCIAL_BUTTON_PRE_TEXT_THRESHOLD = 1;
@@ -57,6 +58,7 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
return null;
}
+ const strategyColumns = getColumnCount(strategies.length, MAX_STRATEGIES_PER_ROW);
const strategyRows = distributeStrategiesIntoRows([...strategies], MAX_STRATEGIES_PER_ROW);
const preferBlockButtons =
@@ -90,6 +92,80 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
gap={2}
elementDescriptor={descriptors.socialButtonsRoot}
>
+ ({
+ rowGap: t.sizes.$2,
+ [mq.sm]: {
+ marginLeft: `calc(${t.sizes.$2}*-1)`,
+ },
+ })}
+ >
+ {strategies.map(strategy => {
+ 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,
+ })
+ : localizationKeys('socialButtonsBlockButtonManyInView', {
+ provider: strategyToDisplayData[strategy].name,
+ });
+ const ref = null;
+
+ const imageOrInitial = strategyToDisplayData[strategy].iconUrl ? (
+ ({ width: theme.sizes.$4, height: theme.sizes.$4, maxWidth: '100%' })}
+ />
+ ) : (
+
+ );
+
+ return (
+ ({
+ [mq.sm]: {
+ paddingLeft: t.sizes.$2,
+ width: `calc(100%/${strategyColumns})`,
+ },
+ width: '100%',
+ })}
+ >
+
+
+ );
+ })}
+
+ ({ height: t.sizes.$4 })} />
{strategyRows.map((row, rowIndex) => (
(strategies: T[], maxStrategiesPe
return rows;
}
+
+/**
+ * Calculates the number of columns given the total number of items and the maximum columns allowed per row.
+ *
+ * @param {Object} options
+ * @param {number} options.length - The total number of items.
+ * @param {number} options.max - The maximum number of columns allowed per row.
+ * @returns The calculated number of columns.
+ *
+ * Example output for item counts from 1 to 24 with `columns: 6`:
+ *
+ * 1: [ 1 ]
+ * 2: [ 1, 2 ]
+ * 3: [ 1, 2, 3 ]
+ * 4: [ 1, 2, 3, 4 ]
+ * 5: [ 1, 2, 3, 4, 5 ]
+ * 6: [ 1, 2, 3, 4, 5, 6 ]
+ * 7: [ [1, 2, 3, 4], [5, 6, 7] ]
+ * 8: [ [1, 2, 3, 4], [5, 6, 7, 8] ]
+ * 9: [ [1, 2, 3, 4, 5], [6, 7, 8, 9] ]
+ * 10: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10] ]
+ * 11: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11] ]
+ * 12: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12] ]
+ * 13: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13] ]
+ * 14: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14] ]
+ * 15: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14, 15] ]
+ * 16: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16] ]
+ * 17: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17] ]
+ * 18: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18] ]
+ * 19: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19] ]
+ * 20: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20] ]
+ * 21: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14, 15, 16], [17, 18, 19, 20, 21] ]
+ * 22: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14, 15, 16], [17, 18, 19, 20, 21, 22] ]
+ * 23: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17], [18, 19, 20, 21, 22, 23] ]
+ * 24: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18], [19, 20, 21, 22, 23, 24] ]
+ *
+ * Examples:
+ * ```
+ * getColumnCount(1); // 1
+ * getColumnCount(7); // 4
+ * getColumnCount(15); // 6
+ * ```
+ */
+export function getColumnCount(length: number, max: number): number {
+ const numRows = Math.ceil(length / max);
+ return Math.ceil(length / numRows);
+}
diff --git a/packages/clerk-js/src/ui/styledSystem/breakpoints.tsx b/packages/clerk-js/src/ui/styledSystem/breakpoints.tsx
index 23b536d202c..b70892b727e 100644
--- a/packages/clerk-js/src/ui/styledSystem/breakpoints.tsx
+++ b/packages/clerk-js/src/ui/styledSystem/breakpoints.tsx
@@ -13,9 +13,9 @@ const deviceQueries = {
ios: '@supports (-webkit-touch-callout: none)',
} as const;
-// export const mq = Object.fromEntries(
-// Object.entries(breakpoints).map(([k, v]) => [k, `@media (min-width: ${v})`]),
-// ) as Record;
+export const mq = Object.fromEntries(
+ Object.entries(breakpoints).map(([k, v]) => [k, `@media (min-width: ${v})`]),
+) as Record;
export const mqu = {
...deviceQueries,
diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts
index 73d2e73b487..32ae911396f 100644
--- a/packages/types/src/appearance.ts
+++ b/packages/types/src/appearance.ts
@@ -168,6 +168,7 @@ export type ElementsConfig = {
socialButtonsRoot: WithOptions;
socialButtons: WithOptions;
+ socialButtonsColumn: WithOptions;
socialButtonsIconButton: WithOptions;
socialButtonsBlockButton: WithOptions;
socialButtonsBlockButtonText: WithOptions;