diff --git a/.changeset/tall-donuts-repeat.md b/.changeset/tall-donuts-repeat.md
new file mode 100644
index 00000000000..1de2f8cb06e
--- /dev/null
+++ b/.changeset/tall-donuts-repeat.md
@@ -0,0 +1,5 @@
+---
+'@clerk/ui': patch
+---
+
+Use logical CSS properties for the organization members footer and the MFA setup task header, so both lay out correctly in right-to-left locales.
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 5598e5426cf..67c4ccab1d3 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -323,6 +323,7 @@ export default tseslint.config([
'custom-rules': {
rules: {
'no-global-object': noGlobalObject,
+ 'no-navigate-useClerk': noNavigateUseClerk,
'no-unstable-methods': noUnstableMethods,
'no-physical-css-properties': noPhysicalCssProperties,
},
@@ -518,14 +519,6 @@ export default tseslint.config([
{
name: 'packages/clerk-js',
files: ['packages/clerk-js/src/ui/**/*'],
- plugins: {
- 'custom-rules': {
- rules: {
- 'no-navigate-useClerk': noNavigateUseClerk,
- 'no-unstable-methods': noUnstableMethods,
- },
- },
- },
rules: {
'custom-rules/no-navigate-useClerk': 'error',
'custom-rules/no-unstable-methods': 'error',
@@ -534,8 +527,12 @@ export default tseslint.config([
{
name: 'packages/ui',
files: ['packages/ui/src/**/*'],
+ // Tests assert on style values they receive; they are not authoring styles.
+ ignores: ['packages/ui/src/**/__tests__/**', 'packages/ui/src/**/*.test.{ts,tsx}'],
rules: {
'custom-rules/no-physical-css-properties': 'error',
+ 'custom-rules/no-navigate-useClerk': 'error',
+ 'custom-rules/no-unstable-methods': 'error',
},
},
{
@@ -543,6 +540,10 @@ export default tseslint.config([
files: ['packages/ui/src/mosaic/**/*'],
ignores: ['packages/ui/src/mosaic/utils.ts', 'packages/ui/src/mosaic/__tests__/**'],
rules: {
+ // Mosaic renders elements through `render={p => }`, so children and controls sit on
+ // the outer component. Both rules only see the empty inner element and always report.
+ 'jsx-a11y/heading-has-content': 'off',
+ 'jsx-a11y/label-has-associated-control': 'off',
'no-restricted-syntax': [
'error',
{
@@ -566,6 +567,19 @@ export default tseslint.config([
files: ['packages/*/src/**/*.test.{ts,tsx}'],
rules: {
'@typescript-eslint/unbound-method': 'off',
+ // vitest's `importOriginal()` has no type-import equivalent.
+ '@typescript-eslint/consistent-type-imports': [
+ 'error',
+ { prefer: 'type-imports', disallowTypeAnnotations: false, fixStyle: 'separate-type-imports' },
+ ],
+ },
+ },
+ {
+ name: 'packages - vitest type tests',
+ files: ['packages/*/src/**/*.test-d.ts'],
+ rules: {
+ // Type tests bind values only to read their type back off with `typeof`.
+ '@typescript-eslint/no-unused-vars': 'off',
},
},
{
diff --git a/packages/testing/src/playwright/__tests__/setupClerkTestingToken.test.ts b/packages/testing/src/playwright/__tests__/setupClerkTestingToken.test.ts
index 4f9a5bff6b9..b6b86d8d4fc 100644
--- a/packages/testing/src/playwright/__tests__/setupClerkTestingToken.test.ts
+++ b/packages/testing/src/playwright/__tests__/setupClerkTestingToken.test.ts
@@ -4,7 +4,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { ERROR_MISSING_FRONTEND_API_URL } from '../../common/errors';
// We need to reset the module-level WeakSet between tests
-// eslint-disable-next-line @typescript-eslint/consistent-type-imports
let setupClerkTestingToken: (typeof import('../setupClerkTestingToken'))['setupClerkTestingToken'];
function createMockRoute(
diff --git a/packages/ui/package.json b/packages/ui/package.json
index ebbc7f6e00d..85eee2d3ac1 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -84,8 +84,8 @@
"dev:sandbox:serve": "rspack serve --config rspack.config.js --env devOrigin=http://localhost:${UI_PORT:-4011}",
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
+ "lint": "eslint src",
"lint:attw": "attw --pack . --exclude-entrypoints themes/shadcn.css --profile esm-only",
- "lint:disabled": "eslint src",
"lint:publint": "publint",
"showerrors": "tsc",
"test": "vitest run",
diff --git a/packages/ui/src/Components.tsx b/packages/ui/src/Components.tsx
index 9b290c5ca9a..d28d93d83ea 100644
--- a/packages/ui/src/Components.tsx
+++ b/packages/ui/src/Components.tsx
@@ -12,14 +12,14 @@ import type {
EnvironmentResource,
GoogleOneTapProps,
OrganizationProfileModalProps,
- SignInProps,
SignInModalProps,
- SignUpProps,
+ SignInProps,
SignUpModalProps,
+ SignUpProps,
UserProfileModalProps,
UserProfileProps,
- WaitlistProps,
WaitlistModalProps,
+ WaitlistProps,
} from '@clerk/shared/types';
import { createDeferredPromise } from '@clerk/shared/utils';
import React, { Suspense, useCallback, useRef, useSyncExternalStore } from 'react';
diff --git a/packages/ui/src/common/__tests__/ProviderIcon.test.tsx b/packages/ui/src/common/__tests__/ProviderIcon.test.tsx
index c4e85fa618e..370016475e2 100644
--- a/packages/ui/src/common/__tests__/ProviderIcon.test.tsx
+++ b/packages/ui/src/common/__tests__/ProviderIcon.test.tsx
@@ -56,7 +56,6 @@ describe('ProviderIcon', () => {
);
const icon = screen.getByLabelText('Apple icon');
- const styles = window.getComputedStyle(icon);
// Check that mask-image is applied (via inline styles)
expect(icon).toHaveStyle({
@@ -225,7 +224,7 @@ describe('ProviderIcon', () => {
id='google'
iconUrl={null}
name='Google'
- isLoading={true}
+ isLoading
/>,
{ wrapper },
);
@@ -242,7 +241,7 @@ describe('ProviderIcon', () => {
id='google'
iconUrl={null}
name='Google'
- isDisabled={true}
+ isDisabled
/>,
{ wrapper },
);
@@ -261,7 +260,7 @@ describe('ProviderIcon', () => {
id='google'
iconUrl='https://example.com/google-icon.svg'
name='Google'
- isLoading={true}
+ isLoading
/>,
{ wrapper },
);
@@ -279,7 +278,7 @@ describe('ProviderIcon', () => {
id='google'
iconUrl='https://example.com/google-icon.svg'
name='Google'
- isDisabled={true}
+ isDisabled
/>,
{ wrapper },
);
diff --git a/packages/ui/src/components/APIKeys/__tests__/APIKeyModal.test.tsx b/packages/ui/src/components/APIKeys/__tests__/APIKeyModal.test.tsx
index 00904507c08..c2789bdf8d6 100644
--- a/packages/ui/src/components/APIKeys/__tests__/APIKeyModal.test.tsx
+++ b/packages/ui/src/components/APIKeys/__tests__/APIKeyModal.test.tsx
@@ -15,7 +15,7 @@ describe('APIKeyModal modalRoot behavior', () => {
modalRoot.current = container;
document.body.appendChild(container);
- const { container: testContainer } = render(
+ render(
{
modalRoot.current = container;
document.body.appendChild(container);
- const { container: testContainer } = render(
+ render(
{
const getContainer = () => container2;
- const { container: testContainer } = render(
+ render(
diff --git a/packages/ui/src/components/AccountCredits/AccountCredits.tsx b/packages/ui/src/components/AccountCredits/AccountCredits.tsx
index 329d5571b06..ca238e801ca 100644
--- a/packages/ui/src/components/AccountCredits/AccountCredits.tsx
+++ b/packages/ui/src/components/AccountCredits/AccountCredits.tsx
@@ -1,4 +1,5 @@
import { ProfileSection } from '@/ui/elements/Section';
+
import { useCreditBalance, useSubscriberTypeLocalizationRoot } from '../../contexts';
import { localizationKeys, Text, useLocalizations } from '../../customizables';
import { useRouter } from '../../router';
diff --git a/packages/ui/src/components/Checkout/CheckoutForm.tsx b/packages/ui/src/components/Checkout/CheckoutForm.tsx
index 05c238ac8b7..00e23e79ff1 100644
--- a/packages/ui/src/components/Checkout/CheckoutForm.tsx
+++ b/packages/ui/src/components/Checkout/CheckoutForm.tsx
@@ -4,11 +4,11 @@ import { useMemo, useState } from 'react';
import { Card } from '@/ui/elements/Card';
import { useCardState, withCardStateProvider } from '@/ui/elements/contexts';
+import { DevModeOverlay } from '@/ui/elements/DevModeNotice';
import { Drawer } from '@/ui/elements/Drawer';
import { LineItems } from '@/ui/elements/LineItems';
import { SegmentedControl } from '@/ui/elements/SegmentedControl';
import { Select, SelectButton, SelectOptionList } from '@/ui/elements/Select';
-import { DevModeOverlay } from '@/ui/elements/DevModeNotice';
import { Tooltip } from '@/ui/elements/Tooltip';
import { toNegativeAmount } from '@/ui/utils/billing';
import {
diff --git a/packages/ui/src/components/ConfigureSSO/elements/Wizard/__tests__/reducer.test.ts b/packages/ui/src/components/ConfigureSSO/elements/Wizard/__tests__/reducer.test.ts
index 00095f16437..88267d22e57 100644
--- a/packages/ui/src/components/ConfigureSSO/elements/Wizard/__tests__/reducer.test.ts
+++ b/packages/ui/src/components/ConfigureSSO/elements/Wizard/__tests__/reducer.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
-import { isStepReachable, initialState, reduce, type WizardConfig, type WizardState } from '../reducer';
+import { initialState, isStepReachable, reduce, type WizardConfig, type WizardState } from '../reducer';
import type { WizardStepDescriptor } from '../types';
const cfg = (descriptors: WizardStepDescriptor[]): WizardConfig => ({ descriptors });
diff --git a/packages/ui/src/components/ConfigureSSO/elements/Wizard/useWizardMachine.ts b/packages/ui/src/components/ConfigureSSO/elements/Wizard/useWizardMachine.ts
index c60f1f1cb7a..bd8699e70ef 100644
--- a/packages/ui/src/components/ConfigureSSO/elements/Wizard/useWizardMachine.ts
+++ b/packages/ui/src/components/ConfigureSSO/elements/Wizard/useWizardMachine.ts
@@ -1,8 +1,8 @@
import React from 'react';
import {
- isStepReachable,
initialState,
+ isStepReachable,
reduce,
type WizardConfig,
type WizardEvent,
diff --git a/packages/ui/src/components/OAuthConsent/utils.ts b/packages/ui/src/components/OAuthConsent/utils.ts
index ea97f3bf115..f511c600df8 100644
--- a/packages/ui/src/components/OAuthConsent/utils.ts
+++ b/packages/ui/src/components/OAuthConsent/utils.ts
@@ -9,14 +9,20 @@ export function getRedirectDisplay(url: string): string {
} catch {
return '';
}
- if (!hostname) return '';
+ if (!hostname) {
+ return '';
+ }
// WHATWG URL.hostname includes surrounding brackets for IPv6 literals on some
// platforms; strip them so detection and output formatting are uniform.
const host = hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;
- if (IPV4_REGEX.test(host)) return host;
- if (host.includes(':')) return `[${host}]`;
+ if (IPV4_REGEX.test(host)) {
+ return host;
+ }
+ if (host.includes(':')) {
+ return `[${host}]`;
+ }
return host.split('.').slice(-2).join('.');
}
diff --git a/packages/ui/src/components/OrganizationList/__tests__/OrganizationList.test.tsx b/packages/ui/src/components/OrganizationList/__tests__/OrganizationList.test.tsx
index a355535300b..60a48c3e5ad 100644
--- a/packages/ui/src/components/OrganizationList/__tests__/OrganizationList.test.tsx
+++ b/packages/ui/src/components/OrganizationList/__tests__/OrganizationList.test.tsx
@@ -3,12 +3,12 @@ import { describe, expect, it, vi } from 'vitest';
import { bindCreateFixtures } from '@/test/create-fixtures';
import { render, waitFor } from '@/test/utils';
-import { OrganizationList } from '../';
import { createFakeOrganization } from '../../CreateOrganization/__tests__/CreateOrganization.test';
import {
createFakeUserOrganizationInvitation,
createFakeUserOrganizationMembership,
} from '../../OrganizationSwitcher/__tests__/test-utils';
+import { OrganizationList } from '../';
const { createFixtures } = bindCreateFixtures('OrganizationList');
diff --git a/packages/ui/src/components/OrganizationProfile/MembersActions.tsx b/packages/ui/src/components/OrganizationProfile/MembersActions.tsx
index c608eb031c7..6ec6daeea5f 100644
--- a/packages/ui/src/components/OrganizationProfile/MembersActions.tsx
+++ b/packages/ui/src/components/OrganizationProfile/MembersActions.tsx
@@ -1,5 +1,6 @@
-import { useMemo, type ReactNode } from 'react';
import { useOrganization } from '@clerk/shared/react';
+import { type ReactNode, useMemo } from 'react';
+
import { Animated } from '@/ui/elements/Animated';
import { Tooltip } from '@/ui/elements/Tooltip';
import { isPlanWithPerSeatCosts } from '@/ui/utils/billingPlanSeats';
diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationBillingPage.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationBillingPage.tsx
index f47d28b8aad..18e5aaa0568 100644
--- a/packages/ui/src/components/OrganizationProfile/OrganizationBillingPage.tsx
+++ b/packages/ui/src/components/OrganizationProfile/OrganizationBillingPage.tsx
@@ -8,11 +8,11 @@ import { Protect } from '../../common';
import { SubscriberTypeContext } from '../../contexts';
import { Col, descriptors, localizationKeys } from '../../customizables';
import { useTabState } from '../../hooks/useTabState';
+import { AccountCredits } from '../AccountCredits';
import { PaymentAttemptsList } from '../PaymentAttempts';
import { PaymentMethods } from '../PaymentMethods';
import { StatementsList } from '../Statements';
import { SubscriptionsList } from '../Subscriptions';
-import { AccountCredits } from '../AccountCredits';
const orgTabMap = {
0: 'subscriptions',
diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationMembers.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationMembers.tsx
index 0b103b559e0..d1bf7ec69ad 100644
--- a/packages/ui/src/components/OrganizationProfile/OrganizationMembers.tsx
+++ b/packages/ui/src/components/OrganizationProfile/OrganizationMembers.tsx
@@ -185,8 +185,7 @@ export const OrganizationMembers = withCardStateProvider(() => {
sx={theme => ({
position: 'absolute',
bottom: 0,
- left: 0,
- right: 0,
+ insetInline: 0,
backgroundColor: theme.colors.$colorBackground,
borderTop: `1px solid ${theme.colors.$borderAlpha100}`,
paddingInline: theme.space.$4,
diff --git a/packages/ui/src/components/OrganizationSwitcher/OtherOrganizationActions.tsx b/packages/ui/src/components/OrganizationSwitcher/OtherOrganizationActions.tsx
index 5d3fbd798d9..4c952b9fae8 100644
--- a/packages/ui/src/components/OrganizationSwitcher/OtherOrganizationActions.tsx
+++ b/packages/ui/src/components/OrganizationSwitcher/OtherOrganizationActions.tsx
@@ -1,11 +1,12 @@
import React from 'react';
+import { CreateOrganizationAction } from '@/common/CreateOrganizationAction';
+import { Add } from '@/icons';
+
import { descriptors, localizationKeys } from '../../customizables';
import { UserInvitationSuggestionList } from './UserInvitationSuggestionList';
import type { UserMembershipListProps } from './UserMembershipList';
import { UserMembershipList } from './UserMembershipList';
-import { CreateOrganizationAction } from '@/common/CreateOrganizationAction';
-import { Add } from '@/icons';
export interface OrganizationActionListProps extends UserMembershipListProps {
onCreateOrganizationClick: React.MouseEventHandler;
diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx
index 8af236fcf18..c910527f0be 100644
--- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx
+++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx
@@ -11,8 +11,8 @@ import {
} from '@/ui/components/OrganizationSwitcher/__tests__/test-utils';
import { clearFetchCache } from '@/ui/hooks/useFetch';
-import { TaskChooseOrganization } from '..';
import type { FakeOrganizationParams } from '../../../../CreateOrganization/__tests__/CreateOrganization.test';
+import { TaskChooseOrganization } from '..';
type FakeOrganizationParams = {
id: string;
diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx
index 779819b5665..d12190b0329 100644
--- a/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx
+++ b/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx
@@ -38,8 +38,7 @@ export const SetupMfaStartScreen = withCardStateProvider((props: SetupMfaStartSc
showLogo
sx={t => ({
paddingTop: t.space.$8,
- paddingLeft: t.space.$8,
- paddingRight: t.space.$8,
+ paddingInline: t.space.$8,
})}
>
diff --git a/packages/ui/src/components/SignIn/__tests__/SignInFactorOne.test.tsx b/packages/ui/src/components/SignIn/__tests__/SignInFactorOne.test.tsx
index 614f98fee40..57ba4fb7448 100644
--- a/packages/ui/src/components/SignIn/__tests__/SignInFactorOne.test.tsx
+++ b/packages/ui/src/components/SignIn/__tests__/SignInFactorOne.test.tsx
@@ -6,8 +6,8 @@ import { describe, expect, it, vi } from 'vitest';
import { bindCreateFixtures } from '@/test/create-fixtures';
import { act, mockWebAuthn, render, screen } from '@/test/utils';
-import { SignInFactorOne } from '../SignInFactorOne';
import { SIGN_IN_RESET_PASSWORD_INTENT_PARAM } from '../shared';
+import { SignInFactorOne } from '../SignInFactorOne';
const { createFixtures } = bindCreateFixtures('SignIn');
diff --git a/packages/ui/src/components/SignUp/__tests__/SignUpStart.test.tsx b/packages/ui/src/components/SignUp/__tests__/SignUpStart.test.tsx
index a4984f49263..73807b7968d 100644
--- a/packages/ui/src/components/SignUp/__tests__/SignUpStart.test.tsx
+++ b/packages/ui/src/components/SignUp/__tests__/SignUpStart.test.tsx
@@ -1,4 +1,3 @@
-import { ClerkAPIResponseError } from '@clerk/shared/error';
import { CAPTCHA_ELEMENT_ID } from '@clerk/shared/internal/clerk-js/constants';
import { OAUTH_PROVIDERS } from '@clerk/shared/oauth';
import type { SignUpResource } from '@clerk/shared/types';
diff --git a/packages/ui/src/components/SubscriptionDetails/index.tsx b/packages/ui/src/components/SubscriptionDetails/index.tsx
index efeb7c85774..3fb2a352bcf 100644
--- a/packages/ui/src/components/SubscriptionDetails/index.tsx
+++ b/packages/ui/src/components/SubscriptionDetails/index.tsx
@@ -19,9 +19,9 @@ import { CardAlert } from '@/ui/elements/Card/CardAlert';
import { useCardState, withCardStateProvider } from '@/ui/elements/contexts';
import { Drawer, useDrawerContext } from '@/ui/elements/Drawer';
import { LineItems } from '@/ui/elements/LineItems';
+import { getSeatLimitAndIncludedSeatsLocalizationKey } from '@/ui/utils/billingPlanSeats';
import { isManageableSubscriptionItem } from '@/ui/utils/billingSubscription';
import { handleError } from '@/ui/utils/errorHandler';
-import { getSeatLimitAndIncludedSeatsLocalizationKey } from '@/ui/utils/billingPlanSeats';
import { formatDate } from '@/ui/utils/formatDate';
import { SubscriberTypeContext, usePlansContext, useSubscriberTypeContext, useSubscription } from '../../contexts';
diff --git a/packages/ui/src/components/UserButton/__tests__/UserButton.test.tsx b/packages/ui/src/components/UserButton/__tests__/UserButton.test.tsx
index 8af062ee274..06d19655215 100644
--- a/packages/ui/src/components/UserButton/__tests__/UserButton.test.tsx
+++ b/packages/ui/src/components/UserButton/__tests__/UserButton.test.tsx
@@ -103,7 +103,7 @@ describe('UserButton', () => {
});
});
- const { getByText, getByRole, userEvent } = render(
+ const { getByRole, userEvent } = render(
,
diff --git a/packages/ui/src/components/UserProfile/BillingPage.tsx b/packages/ui/src/components/UserProfile/BillingPage.tsx
index 3d555676142..fa3d059dad9 100644
--- a/packages/ui/src/components/UserProfile/BillingPage.tsx
+++ b/packages/ui/src/components/UserProfile/BillingPage.tsx
@@ -7,11 +7,11 @@ import { Tab, TabPanel, TabPanels, Tabs, TabsList } from '@/ui/elements/Tabs';
import { SubscriberTypeContext } from '../../contexts';
import { Col, descriptors, localizationKeys } from '../../customizables';
import { useTabState } from '../../hooks/useTabState';
+import { AccountCredits } from '../AccountCredits';
import { PaymentAttemptsList } from '../PaymentAttempts';
import { PaymentMethods } from '../PaymentMethods';
import { StatementsList } from '../Statements';
import { SubscriptionsList } from '../Subscriptions';
-import { AccountCredits } from '../AccountCredits';
const tabMap = {
0: 'subscriptions',
diff --git a/packages/ui/src/components/UserProfile/MfaBackupCodeList.tsx b/packages/ui/src/components/UserProfile/MfaBackupCodeList.tsx
index 557897eef9a..5cc29ebe135 100644
--- a/packages/ui/src/components/UserProfile/MfaBackupCodeList.tsx
+++ b/packages/ui/src/components/UserProfile/MfaBackupCodeList.tsx
@@ -61,16 +61,12 @@ export const MfaBackupCodeList = (props: MfaBackupCodeListProps) => {
({
- textAlign: 'start',
- })}
+ sx={{ textAlign: 'start' }}
/>
({
- textAlign: 'start',
- })}
+ sx={{ textAlign: 'start' }}
colorScheme='secondary'
/>
diff --git a/packages/ui/src/components/UserProfile/MfaSection.tsx b/packages/ui/src/components/UserProfile/MfaSection.tsx
index cbb1bfbd17b..9a2b22a3dee 100644
--- a/packages/ui/src/components/UserProfile/MfaSection.tsx
+++ b/packages/ui/src/components/UserProfile/MfaSection.tsx
@@ -14,7 +14,7 @@ import { useEnvironment } from '../../contexts';
import { Badge, Flex, Icon, localizationKeys, Text } from '../../customizables';
import { Action } from '../../elements/Action';
import { useActionContext } from '../../elements/Action/ActionRoot';
-import { AuthApp, ThreeDotsCircle, Mobile } from '../../icons';
+import { AuthApp, Mobile, ThreeDotsCircle } from '../../icons';
import type { PropsOfComponent } from '../../styledSystem';
import { MfaBackupCodeCreateScreen, MfaScreen, RemoveMfaPhoneCodeScreen, RemoveMfaTOTPScreen } from './MfaScreens';
diff --git a/packages/ui/src/components/UserProfile/__tests__/utils.test.ts b/packages/ui/src/components/UserProfile/__tests__/utils.test.ts
index aeb1605ad5b..95d67d29ce1 100644
--- a/packages/ui/src/components/UserProfile/__tests__/utils.test.ts
+++ b/packages/ui/src/components/UserProfile/__tests__/utils.test.ts
@@ -2,6 +2,7 @@ import type { VerificationJSON } from '@clerk/shared/types';
import { describe, expect, it } from 'vitest';
import { EmailAddress, PhoneNumber } from '@/core/resources';
+
import { sortIdentificationBasedOnVerification } from '../utils';
describe('UserProfile utils', () => {
diff --git a/packages/ui/src/contexts/components/Plans.tsx b/packages/ui/src/contexts/components/Plans.tsx
index 79a43c29d12..4d17c38455f 100644
--- a/packages/ui/src/contexts/components/Plans.tsx
+++ b/packages/ui/src/contexts/components/Plans.tsx
@@ -1,10 +1,10 @@
import {
- __internal_useCreditBalanceQuery,
__experimental_usePaymentAttempts,
__experimental_usePaymentMethods,
__experimental_usePlans,
__experimental_useStatements,
__experimental_useSubscription,
+ __internal_useCreditBalanceQuery,
__internal_useCreditHistoryQuery,
__internal_useOrganizationBase,
useClerk,
diff --git a/packages/ui/src/elements/Action/ActionTrigger.tsx b/packages/ui/src/elements/Action/ActionTrigger.tsx
index 641951fc17a..132e40d7d3d 100644
--- a/packages/ui/src/elements/Action/ActionTrigger.tsx
+++ b/packages/ui/src/elements/Action/ActionTrigger.tsx
@@ -22,7 +22,6 @@ export const ActionTrigger = (props: ActionTriggerProps) => {
return cloneElement(validChildren, {
//@ts-ignore
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick: async () => {
await validChildren.props.onClick?.();
open(value);
diff --git a/packages/ui/src/elements/Header.tsx b/packages/ui/src/elements/Header.tsx
index efb92811df8..93b7346a4cb 100644
--- a/packages/ui/src/elements/Header.tsx
+++ b/packages/ui/src/elements/Header.tsx
@@ -8,9 +8,9 @@ import {
Heading,
Icon,
Link,
+ type LocalizationKey,
Text,
useAppearance,
- type LocalizationKey,
} from '../customizables';
import { ArrowLeft } from '../icons';
import type { PropsOfComponent, ThemableCssProp } from '../styledSystem';
diff --git a/packages/ui/src/hooks/__tests__/useCoreOrganizationList.test.tsx b/packages/ui/src/hooks/__tests__/useCoreOrganizationList.test.tsx
index dbe8147240e..6d2155bcabd 100644
--- a/packages/ui/src/hooks/__tests__/useCoreOrganizationList.test.tsx
+++ b/packages/ui/src/hooks/__tests__/useCoreOrganizationList.test.tsx
@@ -3,8 +3,8 @@ import { createDeferredPromise } from '@clerk/shared/utils/index';
import { describe, expect, it } from 'vitest';
import { bindCreateFixtures } from '@/test/create-fixtures';
-
import { act, renderHook, waitFor } from '@/test/utils';
+
import {
createFakeUserOrganizationInvitation,
createFakeUserOrganizationMembership,
diff --git a/packages/ui/src/hooks/__tests__/useEnabledThirdPartyProviders.test.tsx b/packages/ui/src/hooks/__tests__/useEnabledThirdPartyProviders.test.tsx
index b9a013d16d9..84d94bd6e12 100644
--- a/packages/ui/src/hooks/__tests__/useEnabledThirdPartyProviders.test.tsx
+++ b/packages/ui/src/hooks/__tests__/useEnabledThirdPartyProviders.test.tsx
@@ -3,6 +3,7 @@ import { renderHook } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { bindCreateFixtures } from '@/test/utils';
+
import { useEnabledThirdPartyProviders } from '../useEnabledThirdPartyProviders';
const { createFixtures } = bindCreateFixtures('SignUp');
diff --git a/packages/ui/src/hooks/usePopover.ts b/packages/ui/src/hooks/usePopover.ts
index 805559111ac..fb89dc5eb87 100644
--- a/packages/ui/src/hooks/usePopover.ts
+++ b/packages/ui/src/hooks/usePopover.ts
@@ -114,6 +114,7 @@ export const usePopover = (props: UsePopoverProps = {}): UsePopoverReturn => {
nodeId,
close,
isOpen,
+ // eslint-disable-next-line custom-rules/no-physical-css-properties -- floating-ui computes physical x/y for the chosen strategy
styles: { position: strategy, top: y ?? 0, left: x ?? 0 },
context,
getReferenceProps,
diff --git a/packages/ui/src/hooks/useScrollLock.ts b/packages/ui/src/hooks/useScrollLock.ts
index 0c637d32c7f..99156b804cd 100644
--- a/packages/ui/src/hooks/useScrollLock.ts
+++ b/packages/ui/src/hooks/useScrollLock.ts
@@ -41,7 +41,9 @@ function enableScrollLock() {
Object.assign(bodyStyle, {
position: 'fixed',
top: `${-(scrollY - Math.floor(offsetTop))}px`,
+ // eslint-disable-next-line custom-rules/no-physical-css-properties -- mirrors physical viewport scroll offsets, which are not direction-aware
left: `${-(scrollX - Math.floor(offsetLeft))}px`,
+ // eslint-disable-next-line custom-rules/no-physical-css-properties -- pairs with the physical `left` above
right: '0',
});
}
@@ -56,7 +58,9 @@ function enableScrollLock() {
Object.assign(bodyStyle, {
position: '',
top: '',
+ // eslint-disable-next-line custom-rules/no-physical-css-properties -- resets the physical properties set above
left: '',
+ // eslint-disable-next-line custom-rules/no-physical-css-properties -- resets the physical properties set above
right: '',
});
window.scrollTo(scrollX, scrollY);
diff --git a/packages/ui/src/index.legacy.browser.ts b/packages/ui/src/index.legacy.browser.ts
index a073f7d89d2..fe698c2d965 100644
--- a/packages/ui/src/index.legacy.browser.ts
+++ b/packages/ui/src/index.legacy.browser.ts
@@ -1,6 +1,6 @@
// It's crucial this is the first import,
// otherwise chunk loading will not work
-// eslint-disable-next-line
+
import './utils/setWebpackChunkPublicPath';
import { ClerkUI } from './ClerkUI';
diff --git a/packages/ui/src/localization/__tests__/applyMarkupToNodes.test.tsx b/packages/ui/src/localization/__tests__/applyMarkupToNodes.test.tsx
index 04b1c13238b..1ff28954946 100644
--- a/packages/ui/src/localization/__tests__/applyMarkupToNodes.test.tsx
+++ b/packages/ui/src/localization/__tests__/applyMarkupToNodes.test.tsx
@@ -2,8 +2,8 @@ import type { ReactNode } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it } from 'vitest';
-import type { Tokens } from '../applyTokensToString';
import { applyMarkupAndTokens, stripMarkup } from '../applyMarkupToNodes';
+import type { Tokens } from '../applyTokensToString';
const html = (node: ReactNode) => renderToStaticMarkup(node as any);
diff --git a/packages/ui/src/localization/applyMarkupToNodes.tsx b/packages/ui/src/localization/applyMarkupToNodes.tsx
index b17ad89179d..70eb047ff72 100644
--- a/packages/ui/src/localization/applyMarkupToNodes.tsx
+++ b/packages/ui/src/localization/applyMarkupToNodes.tsx
@@ -14,7 +14,9 @@ const TAG_RE = /<(\/?)(bold)>/g;
export const stripMarkup = (s: string): string => s.replace(/<\/?(bold)>/g, '');
export const applyMarkupAndTokens = (template: string | undefined, tokens: Tokens): ReactNode => {
- if (!template) return '';
+ if (!template) {
+ return '';
+ }
const substitute = (s: string) => (s.includes('{{') ? applyTokensToString(s, tokens) : s);
if (!template.includes('<')) {
return substitute(template);
@@ -29,7 +31,9 @@ export const applyMarkupAndTokens = (template: string | undefined, tokens: Token
while ((match = TAG_RE.exec(template)) !== null) {
const [full, slash, tag] = match;
const text = template.slice(cursor, match.index);
- if (text) stack[stack.length - 1].children.push(substitute(text));
+ if (text) {
+ stack[stack.length - 1].children.push(substitute(text));
+ }
cursor = match.index + full.length;
if (!slash) {
@@ -51,10 +55,16 @@ export const applyMarkupAndTokens = (template: string | undefined, tokens: Token
}
const tail = template.slice(cursor);
- if (tail) stack[0].children.push(substitute(tail));
+ if (tail) {
+ stack[0].children.push(substitute(tail));
+ }
const out = stack[0].children;
- if (out.length === 0) return '';
- if (out.length === 1) return out[0];
+ if (out.length === 0) {
+ return '';
+ }
+ if (out.length === 1) {
+ return out[0];
+ }
return createElement(Fragment, null, ...out);
};
diff --git a/packages/ui/src/mosaic/__tests__/slot-recipe.test.ts b/packages/ui/src/mosaic/__tests__/slot-recipe.test.ts
index 24758f8cd94..438780d75d6 100644
--- a/packages/ui/src/mosaic/__tests__/slot-recipe.test.ts
+++ b/packages/ui/src/mosaic/__tests__/slot-recipe.test.ts
@@ -236,18 +236,18 @@ describe('useSlot / slot sugar', () => {
describe('type inference', () => {
it('infers variant prop names and values', () => {
// Type-only assertions — never executed, validated by `tsc` during type-check.
- () => {
+ function _typeAssertions() {
useRecipe(buttonRecipe, { variants: { color: 'danger', size: 'sm', loading: true } });
// @ts-expect-error 'nope' is not a valid `color`
useRecipe(buttonRecipe, { variants: { color: 'nope' } });
// @ts-expect-error `loading` is a boolean variant, not a string
useRecipe(buttonRecipe, { variants: { loading: 'true' } });
- };
+ }
expect(true).toBe(true);
});
it('defaultVariants only accepts valid variant keys and values', () => {
- () => {
+ function _typeAssertions() {
defineSlotRecipe({
slot: 'button',
variants: { color: { primary: {}, danger: {} } },
@@ -260,12 +260,12 @@ describe('type inference', () => {
// @ts-expect-error 'unknown' is not a declared variant axis
defaultVariants: { unknown: 'primary' },
});
- };
+ }
expect(true).toBe(true);
});
it('compoundVariants entries only accept valid variant keys and values', () => {
- () => {
+ function _typeAssertions() {
defineSlotRecipe({
slot: 'button',
variants: { color: { primary: {}, danger: {} }, size: { sm: {}, md: {} } },
@@ -282,7 +282,7 @@ describe('type inference', () => {
{ unknown: 'primary', css: {} },
],
});
- };
+ }
expect(true).toBe(true);
});
});
diff --git a/packages/ui/src/mosaic/__tests__/utils.test.ts b/packages/ui/src/mosaic/__tests__/utils.test.ts
index a5c45258128..fd655819edf 100644
--- a/packages/ui/src/mosaic/__tests__/utils.test.ts
+++ b/packages/ui/src/mosaic/__tests__/utils.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
-import { defaultMosaicVariables, resolveVariables } from '../variables';
import { alpha } from '../utils';
+import { defaultMosaicVariables, resolveVariables } from '../variables';
describe('alpha', () => {
it('produces a color-mix expression', () => {
diff --git a/packages/ui/src/mosaic/components/heading.tsx b/packages/ui/src/mosaic/components/heading.tsx
index 8c473b08e52..d09c777b064 100644
--- a/packages/ui/src/mosaic/components/heading.tsx
+++ b/packages/ui/src/mosaic/components/heading.tsx
@@ -1,7 +1,6 @@
-import React from 'react';
-
import type { ComponentProps } from '@clerk/headless/utils';
import { renderElement } from '@clerk/headless/utils';
+import React from 'react';
import type { RecipeVariantProps } from '../slot-recipe';
import { defineSlotRecipe, useRecipe } from '../slot-recipe';
diff --git a/packages/ui/src/mosaic/components/input.tsx b/packages/ui/src/mosaic/components/input.tsx
index d3d1686bd97..146deb77ad9 100644
--- a/packages/ui/src/mosaic/components/input.tsx
+++ b/packages/ui/src/mosaic/components/input.tsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { defineSlotRecipe, useRecipe } from '../slot-recipe';
import type { RecipeVariantProps } from '../slot-recipe';
+import { defineSlotRecipe, useRecipe } from '../slot-recipe';
export const inputRecipe = defineSlotRecipe(theme => ({
slot: 'input',
diff --git a/packages/ui/src/mosaic/components/skeleton.tsx b/packages/ui/src/mosaic/components/skeleton.tsx
index 274c999cd6e..0e3029e31db 100644
--- a/packages/ui/src/mosaic/components/skeleton.tsx
+++ b/packages/ui/src/mosaic/components/skeleton.tsx
@@ -1,3 +1,4 @@
+// eslint-disable-next-line no-restricted-imports
import { keyframes } from '@emotion/react';
import type { ReactNode } from 'react';
diff --git a/packages/ui/src/mosaic/components/text.tsx b/packages/ui/src/mosaic/components/text.tsx
index e34421ec358..45d5a6c80d4 100644
--- a/packages/ui/src/mosaic/components/text.tsx
+++ b/packages/ui/src/mosaic/components/text.tsx
@@ -1,7 +1,6 @@
-import React from 'react';
-
import type { ComponentProps } from '@clerk/headless/utils';
import { renderElement } from '@clerk/headless/utils';
+import React from 'react';
import type { RecipeVariantProps } from '../slot-recipe';
import { defineSlotRecipe, useRecipe } from '../slot-recipe';
diff --git a/packages/ui/src/mosaic/machine/__tests__/wizard-migration.test.tsx b/packages/ui/src/mosaic/machine/__tests__/wizard-migration.test.tsx
index 06286b1081e..516d20c3263 100644
--- a/packages/ui/src/mosaic/machine/__tests__/wizard-migration.test.tsx
+++ b/packages/ui/src/mosaic/machine/__tests__/wizard-migration.test.tsx
@@ -94,9 +94,13 @@ function createWizardMachine(descriptors: StepDescriptor[]): StateMachine {
- if (descriptors.length === 0) return '';
+ if (descriptors.length === 0) {
+ return '';
+ }
let i = 0;
- while (i + 1 < descriptors.length && guardHolds(descriptors[i + 1])) i++;
+ while (i + 1 < descriptors.length && guardHolds(descriptors[i + 1])) {
+ i++;
+ }
return descriptors[i].id;
};
diff --git a/packages/ui/src/mosaic/machine/assign.ts b/packages/ui/src/mosaic/machine/assign.ts
index 9822f37d416..a59754212ba 100644
--- a/packages/ui/src/mosaic/machine/assign.ts
+++ b/packages/ui/src/mosaic/machine/assign.ts
@@ -1,5 +1,5 @@
-import { ASSIGN } from './types';
import type { AssignAction, EventObject } from './types';
+import { ASSIGN } from './types';
/**
* Context-update action creator. The returned object is recognised by the
diff --git a/packages/ui/src/mosaic/machine/useMachine.ts b/packages/ui/src/mosaic/machine/useMachine.ts
index 065677726df..df2b11e7fb9 100644
--- a/packages/ui/src/mosaic/machine/useMachine.ts
+++ b/packages/ui/src/mosaic/machine/useMachine.ts
@@ -63,7 +63,9 @@ export function useMachine(
const onDoneRef = useRef(options?.onDone);
onDoneRef.current = options?.onDone;
useEffect(() => {
- snapshot.status === 'done' && onDoneRef.current?.();
+ if (snapshot.status === 'done') {
+ onDoneRef.current?.();
+ }
}, [snapshot.status]);
return [snapshot, actor.send, actor];
@@ -126,9 +128,10 @@ export function useMachineLogger(label: string, snapsho
const previous = prevValue.current;
prevValue.current = current;
- previous === undefined && console.log(`[${label}] ${current}`, snapshot.context);
- previous !== undefined &&
- previous !== current &&
+ if (previous === undefined) {
+ console.log(`[${label}] ${current}`, snapshot.context);
+ } else if (previous !== current) {
console.log(`[${label}] ${previous} → ${current}`, snapshot.context);
+ }
});
}
diff --git a/packages/ui/src/mosaic/organization/organization-profile-delete-section.view.tsx b/packages/ui/src/mosaic/organization/organization-profile-delete-section.view.tsx
index 035f97cd8de..d37a595a835 100644
--- a/packages/ui/src/mosaic/organization/organization-profile-delete-section.view.tsx
+++ b/packages/ui/src/mosaic/organization/organization-profile-delete-section.view.tsx
@@ -21,12 +21,7 @@ export function OrganizationProfileDeleteSectionView({
const isDeleting = snapshot.value === 'deleting';
return (
- ({
- width: '100%',
- containerType: 'inline-size',
- })}
- >
+
({
display: 'flex',
diff --git a/packages/ui/src/mosaic/organization/organization-profile-leave-section.machine.ts b/packages/ui/src/mosaic/organization/organization-profile-leave-section.machine.ts
index 9a2014dc5dc..f7b9040b8a1 100644
--- a/packages/ui/src/mosaic/organization/organization-profile-leave-section.machine.ts
+++ b/packages/ui/src/mosaic/organization/organization-profile-leave-section.machine.ts
@@ -55,6 +55,7 @@ export const organizationProfileLeaveSectionMachine = createMachine({
},
}),
},
+ // eslint-disable-next-line custom-rules/no-physical-css-properties -- machine state name ("has left the org"), not CSS
left: { type: 'final' },
},
});
diff --git a/packages/ui/src/mosaic/organization/organization-profile-leave-section.view.tsx b/packages/ui/src/mosaic/organization/organization-profile-leave-section.view.tsx
index 807c2f2b4eb..1fb132b241c 100644
--- a/packages/ui/src/mosaic/organization/organization-profile-leave-section.view.tsx
+++ b/packages/ui/src/mosaic/organization/organization-profile-leave-section.view.tsx
@@ -21,12 +21,7 @@ export function OrganizationProfileLeaveSectionView({
const isLeaving = snapshot.value === 'leaving';
return (
- ({
- width: '100%',
- containerType: 'inline-size',
- })}
- >
+
({
display: 'flex',
diff --git a/packages/ui/src/mosaic/primitives/box.tsx b/packages/ui/src/mosaic/primitives/box.tsx
index 5f07dc5d0d8..fda9910749e 100644
--- a/packages/ui/src/mosaic/primitives/box.tsx
+++ b/packages/ui/src/mosaic/primitives/box.tsx
@@ -1,4 +1,4 @@
-import { type ComponentProps, type RenderProp, renderElement } from '@clerk/headless/utils';
+import { type ComponentProps, renderElement, type RenderProp } from '@clerk/headless/utils';
import React from 'react';
/**
diff --git a/packages/ui/src/mosaic/primitives/dialog.tsx b/packages/ui/src/mosaic/primitives/dialog.tsx
index 9c4ed985c8e..bb7d0f9cec4 100644
--- a/packages/ui/src/mosaic/primitives/dialog.tsx
+++ b/packages/ui/src/mosaic/primitives/dialog.tsx
@@ -1,5 +1,5 @@
-import { Dialog as HeadlessDialog } from '@clerk/headless/dialog';
import type { DialogPortalProps, DialogProps } from '@clerk/headless/dialog';
+import { Dialog as HeadlessDialog } from '@clerk/headless/dialog';
import type { FunctionComponent } from 'react';
import { withMosaicSlot } from './withMosaicSlot';
diff --git a/packages/ui/src/mosaic/primitives/tabs.tsx b/packages/ui/src/mosaic/primitives/tabs.tsx
index 69cb2ff21b5..ee19d168a9d 100644
--- a/packages/ui/src/mosaic/primitives/tabs.tsx
+++ b/packages/ui/src/mosaic/primitives/tabs.tsx
@@ -1,5 +1,5 @@
-import { Tabs as HeadlessTabs } from '@clerk/headless/tabs';
import type { TabsProps } from '@clerk/headless/tabs';
+import { Tabs as HeadlessTabs } from '@clerk/headless/tabs';
import type { FunctionComponent } from 'react';
import { withMosaicSlot } from './withMosaicSlot';
diff --git a/packages/ui/src/mosaic/slot-recipe.ts b/packages/ui/src/mosaic/slot-recipe.ts
index 74880262085..4d89f02e519 100644
--- a/packages/ui/src/mosaic/slot-recipe.ts
+++ b/packages/ui/src/mosaic/slot-recipe.ts
@@ -140,18 +140,21 @@ export function defineSlotRecipe SlotRecipeConfig),
): SlotRecipe {
- const cache = typeof configOrFn === 'function' ? new WeakMap() : null;
- const resolveConfig = (theme: MosaicTheme): SlotRecipeConfig => {
- if (typeof configOrFn !== 'function') {
- return configOrFn;
- }
- let cached = cache!.get(theme);
- if (!cached) {
- cached = configOrFn(theme);
- cache!.set(theme, cached);
- }
- return cached;
- };
+ let resolveConfig: (theme: MosaicTheme) => SlotRecipeConfig;
+ if (typeof configOrFn === 'function') {
+ const buildConfig = configOrFn;
+ const cache = new WeakMap();
+ resolveConfig = theme => {
+ let cached = cache.get(theme);
+ if (!cached) {
+ cached = buildConfig(theme);
+ cache.set(theme, cached);
+ }
+ return cached;
+ };
+ } else {
+ resolveConfig = () => configOrFn;
+ }
// Slot identity is theme-independent, so probe once against the default theme.
const probe = resolveConfig(resolveVariables(defaultMosaicVariables));
diff --git a/packages/ui/src/mosaic/utils/context.ts b/packages/ui/src/mosaic/utils/context.ts
index 94e17b9c693..0859ac2d272 100644
--- a/packages/ui/src/mosaic/utils/context.ts
+++ b/packages/ui/src/mosaic/utils/context.ts
@@ -2,10 +2,14 @@ import React from 'react';
export function useContextProps(props: T, context: React.Context | null>): T {
const ctx = React.useContext(context);
- if (!ctx) return props;
+ if (!ctx) {
+ return props;
+ }
const result = { ...ctx } as T;
for (const key of Object.keys(props) as (keyof T)[]) {
- if (props[key] !== undefined) result[key] = props[key];
+ if (props[key] !== undefined) {
+ result[key] = props[key];
+ }
}
return result;
}
diff --git a/packages/ui/src/router/BaseRouter.tsx b/packages/ui/src/router/BaseRouter.tsx
index 38ad113b559..8d401d38a4e 100644
--- a/packages/ui/src/router/BaseRouter.tsx
+++ b/packages/ui/src/router/BaseRouter.tsx
@@ -1,8 +1,8 @@
import { getQueryParams, stringifyQueryParams } from '@clerk/shared/internal/clerk-js/querystring';
import { trimTrailingSlash } from '@clerk/shared/internal/clerk-js/url';
import { useClerk } from '@clerk/shared/react';
-import { withLeadingSlash } from '@clerk/shared/url';
import type { NavigateOptions } from '@clerk/shared/types';
+import { withLeadingSlash } from '@clerk/shared/url';
import React from 'react';
import { flushSync } from 'react-dom';
@@ -40,9 +40,10 @@ function ensurePushStatePatched(): void {
if (originalPushState) {
return;
}
- originalPushState = history.pushState.bind(history);
+ const original = history.pushState.bind(history);
+ originalPushState = original;
history.pushState = (...args: Parameters) => {
- originalPushState!(...args);
+ original(...args);
pushStateSubscribers.forEach(fn => fn());
};
}
@@ -51,9 +52,10 @@ function ensureReplaceStatePatched(): void {
if (originalReplaceState) {
return;
}
- originalReplaceState = history.replaceState.bind(history);
+ const original = history.replaceState.bind(history);
+ originalReplaceState = original;
history.replaceState = (...args: Parameters) => {
- originalReplaceState!(...args);
+ original(...args);
replaceStateSubscribers.forEach(fn => fn());
};
}
diff --git a/packages/ui/src/server.ts b/packages/ui/src/server.ts
index f7281c15251..59db2171dda 100644
--- a/packages/ui/src/server.ts
+++ b/packages/ui/src/server.ts
@@ -1,9 +1,8 @@
+import { ClerkUI } from './entry';
import type { Ui } from './internal';
import { UI_BRAND } from './internal';
import type { Appearance } from './internal/appearance';
-import { ClerkUI } from './entry';
-
declare const PACKAGE_VERSION: string;
/**
diff --git a/packages/ui/src/utils/__tests__/detectClerkStylesheetUsage.test.ts b/packages/ui/src/utils/__tests__/detectClerkStylesheetUsage.test.ts
index 1b194fc4ec2..9b8a6158e23 100644
--- a/packages/ui/src/utils/__tests__/detectClerkStylesheetUsage.test.ts
+++ b/packages/ui/src/utils/__tests__/detectClerkStylesheetUsage.test.ts
@@ -1,4 +1,4 @@
-import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
+import { afterEach, beforeEach, describe, expect, test } from 'vitest';
import { detectStructuralClerkCss } from '../detectClerkStylesheetUsage';
diff --git a/packages/ui/src/utils/billingPlanSeats.ts b/packages/ui/src/utils/billingPlanSeats.ts
index 876999c9797..57df0e7aba6 100644
--- a/packages/ui/src/utils/billingPlanSeats.ts
+++ b/packages/ui/src/utils/billingPlanSeats.ts
@@ -61,13 +61,19 @@ export type SeatChargeSummary = {
* Returns `{ totalSeats, included, paidTier }` otherwise.
*/
export const summarizeSeatCharges = (seatsTotal: BillingPerUnitTotal | null | undefined): SeatChargeSummary | null => {
- if (!seatsTotal) return null;
+ if (!seatsTotal) {
+ return null;
+ }
const paidTier = seatsTotal.tiers.find(tier => tier.feePerBlock.amount > 0);
- if (!paidTier) return null;
+ if (!paidTier) {
+ return null;
+ }
let totalSeats = 0;
let included = 0;
for (const tier of seatsTotal.tiers) {
- if (tier.quantity === null) continue;
+ if (tier.quantity === null) {
+ continue;
+ }
totalSeats += tier.quantity;
if (tier.feePerBlock.amount === 0) {
included += tier.quantity;
diff --git a/packages/ui/src/utils/detectClerkStylesheetUsage.ts b/packages/ui/src/utils/detectClerkStylesheetUsage.ts
index 04a4839c2ce..7d5cc1e0ccd 100644
--- a/packages/ui/src/utils/detectClerkStylesheetUsage.ts
+++ b/packages/ui/src/utils/detectClerkStylesheetUsage.ts
@@ -36,7 +36,7 @@ function splitSelectorList(selectorText: string): string[] {
function hasClerkWithAdjacency(selector: string): boolean {
// Pattern: .cl-class followed by combinator/space and another selector
// Or: another selector followed by combinator/space and .cl-class
- const clerkWithDescendant = /\.cl-[A-Za-z0-9_-]+[\s>+~]+[.#\w\[:]/;
+ const clerkWithDescendant = /\.cl-[A-Za-z0-9_-]+[\s>+~]+[.#\w[:]/;
const descendantWithClerk = /[.#\w\]:][\s>+~]+\.cl-[A-Za-z0-9_-]+/;
return clerkWithDescendant.test(selector) || descendantWithClerk.test(selector);
diff --git a/packages/ui/src/utils/warnAboutCustomizationWithoutPinning.ts b/packages/ui/src/utils/warnAboutCustomizationWithoutPinning.ts
index 7e7cde13278..674efe4a7b4 100644
--- a/packages/ui/src/utils/warnAboutCustomizationWithoutPinning.ts
+++ b/packages/ui/src/utils/warnAboutCustomizationWithoutPinning.ts
@@ -6,7 +6,7 @@ import { CLERK_CLASS_RE, HAS_RE, POSITIONAL_PSEUDO_RE } from './cssPatterns';
import { detectStructuralClerkCss } from './detectClerkStylesheetUsage';
// Regex patterns for hasAdjacencyWithOtherSelector
-const OTHER_SELECTOR_RE = /[.#\w\[:]/;
+const OTHER_SELECTOR_RE = /[.#\w[:]/;
const CLERK_CLASS_GLOBAL_RE = /\.cl-[A-Za-z0-9_-]+/g;
const COMBINATOR_RE = /[>+~\s]/;
diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json
index caa880cc04e..f6bc61c6e53 100644
--- a/packages/ui/tsconfig.json
+++ b/packages/ui/tsconfig.json
@@ -22,6 +22,8 @@
"declarationMap": true,
"allowJs": true,
"paths": {
+ // Test-only imports; vitest.config.mts aliases these to clerk-js at runtime.
+ "@/core/*": ["../clerk-js/src/core/*"],
"@/*": ["./src/*"],
// Adding this to avoid changes in the ui files
// in order to make git merges easier