diff --git a/.changeset/cuddly-ends-wait.md b/.changeset/cuddly-ends-wait.md new file mode 100644 index 00000000000..61a21cc03cd --- /dev/null +++ b/.changeset/cuddly-ends-wait.md @@ -0,0 +1,15 @@ +--- +'@clerk/ui': minor +--- + +Improve RTL support by converting physical CSS properties (margins, padding, text alignment, borders) to logical equivalents and adding direction-aware arrow icons + +The changes included: + +- Positioning (left → insetInlineStart) +- Margins (marginLeft/Right → marginInlineStart/End) +- Padding (paddingLeft/Right → paddingInlineStart/End) +- Text alignment (left/right → start/end) +- Border radius (borderTopLeftRadius → borderStartStartRadius) +- Arrow icon flipping with scaleX(-1) in RTL +- Animation direction adjustments diff --git a/eslint.config.mjs b/eslint.config.mjs index 23ef3f174bd..1cef3d663c3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -171,6 +171,83 @@ const noUnstableMethods = { }, }; +const noPhysicalCssProperties = { + meta: { + type: 'problem', + docs: { + description: 'Enforce use of CSS logical properties instead of physical properties for RTL support', + recommended: false, + }, + messages: { + useLogicalProperty: + 'Use logical CSS property "{{logical}}" instead of physical property "{{physical}}" for RTL support.', + useLogicalTextAlign: + 'Use logical textAlign value "{{logical}}" instead of physical value "{{physical}}" for RTL support.', + }, + schema: [], + }, + create(context) { + // Mapping of physical properties to logical equivalents + const propertyMap = { + left: 'insetInlineStart', + right: 'insetInlineEnd', + marginLeft: 'marginInlineStart', + marginRight: 'marginInlineEnd', + paddingLeft: 'paddingInlineStart', + paddingRight: 'paddingInlineEnd', + borderLeft: 'borderInlineStart', + borderRight: 'borderInlineEnd', + borderLeftWidth: 'borderInlineStartWidth', + borderRightWidth: 'borderInlineEndWidth', + borderLeftStyle: 'borderInlineStartStyle', + borderRightStyle: 'borderInlineEndStyle', + borderLeftColor: 'borderInlineStartColor', + borderRightColor: 'borderInlineEndColor', + borderTopLeftRadius: 'borderStartStartRadius', + borderTopRightRadius: 'borderStartEndRadius', + borderBottomLeftRadius: 'borderEndStartRadius', + borderBottomRightRadius: 'borderEndEndRadius', + }; + + const checkProperty = (key, value) => { + const keyName = key.type === 'Identifier' ? key.name : key.value; + + // Check for physical property names + if (propertyMap[keyName]) { + context.report({ + node: key, + messageId: 'useLogicalProperty', + data: { + physical: keyName, + logical: propertyMap[keyName], + }, + }); + } + + // Check for textAlign with physical values + if (keyName === 'textAlign' && value) { + if (value.type === 'Literal' && (value.value === 'left' || value.value === 'right')) { + const logicalValue = value.value === 'left' ? 'start' : 'end'; + context.report({ + node: value, + messageId: 'useLogicalTextAlign', + data: { + physical: value.value, + logical: logicalValue, + }, + }); + } + } + }; + + return { + Property(node) { + checkProperty(node.key, node.value); + }, + }; + }, +}; + export default tseslint.config([ { name: 'repo/ignores', @@ -248,6 +325,7 @@ export default tseslint.config([ rules: { 'no-global-object': noGlobalObject, 'no-unstable-methods': noUnstableMethods, + 'no-physical-css-properties': noPhysicalCssProperties, }, }, 'simple-import-sort': pluginSimpleImportSort, @@ -458,6 +536,13 @@ export default tseslint.config([ 'custom-rules/no-unstable-methods': 'error', }, }, + { + name: 'packages/ui', + files: ['packages/ui/src/**/*'], + rules: { + 'custom-rules/no-physical-css-properties': 'error', + }, + }, { name: 'packages - vitest', files: ['packages/*/src/**/*.test.{ts,tsx}'], diff --git a/packages/clerk-js/sandbox/template.html b/packages/clerk-js/sandbox/template.html index 4983019bdf9..cae088d5fb6 100644 --- a/packages/clerk-js/sandbox/template.html +++ b/packages/clerk-js/sandbox/template.html @@ -1,5 +1,8 @@ - + clerk-js Sandbox diff --git a/packages/ui/src/common/InfiniteListSpinner.tsx b/packages/ui/src/common/InfiniteListSpinner.tsx index 97ae637e73f..7e053404f2f 100644 --- a/packages/ui/src/common/InfiniteListSpinner.tsx +++ b/packages/ui/src/common/InfiniteListSpinner.tsx @@ -16,6 +16,7 @@ export const InfiniteListSpinner = forwardRef((_, ref) => { sx={{ margin: 'auto', position: 'absolute', + // eslint-disable-next-line custom-rules/no-physical-css-properties -- Centering with transform: translateX(-50%) left: '50%', top: '50%', transform: 'translateY(-50%) translateX(-50%)', diff --git a/packages/ui/src/common/NotificationCountBadge.tsx b/packages/ui/src/common/NotificationCountBadge.tsx index 97d27ac6757..7fb99e121af 100644 --- a/packages/ui/src/common/NotificationCountBadge.tsx +++ b/packages/ui/src/common/NotificationCountBadge.tsx @@ -32,7 +32,7 @@ export const NotificationCountBadge = (props: NotificationCountBadgeProps) => { as='span' sx={[ t => ({ - marginLeft: t.space.$1x5, + marginInlineStart: t.space.$1x5, }), containerSx, ]} diff --git a/packages/ui/src/common/PrintableComponent.tsx b/packages/ui/src/common/PrintableComponent.tsx index 6cfc8c273c4..95542d4b041 100644 --- a/packages/ui/src/common/PrintableComponent.tsx +++ b/packages/ui/src/common/PrintableComponent.tsx @@ -24,6 +24,7 @@ export const PrintableComponent = (props: UsePrintableReturn['printableProps'] & return (
{children} diff --git a/packages/ui/src/common/organizations/OrganizationPreview.tsx b/packages/ui/src/common/organizations/OrganizationPreview.tsx index 7078c2d3d62..7fa78eb8fac 100644 --- a/packages/ui/src/common/organizations/OrganizationPreview.tsx +++ b/packages/ui/src/common/organizations/OrganizationPreview.tsx @@ -96,6 +96,7 @@ export const OrganizationPreviewSpinner = forwardRef((_, ref) => sx={{ margin: 'auto', position: 'absolute', + // eslint-disable-next-line custom-rules/no-physical-css-properties -- Centering with transform: translateX(-50%) left: '50%', top: '50%', transform: 'translateY(-50%) translateX(-50%)', diff --git a/packages/ui/src/components/APIKeys/APIKeyModal.tsx b/packages/ui/src/components/APIKeys/APIKeyModal.tsx index d1cdd43429c..1ffd6e85bfe 100644 --- a/packages/ui/src/components/APIKeys/APIKeyModal.tsx +++ b/packages/ui/src/components/APIKeys/APIKeyModal.tsx @@ -18,7 +18,7 @@ const getScopedPortalContainerStyles = (modalRoot?: React.MutableRefObject ({ position: 'absolute', - right: 0, + insetInlineEnd: 0, bottom: 0, backgroundColor: 'inherit', backdropFilter: `blur(${t.sizes.$2})`, diff --git a/packages/ui/src/components/APIKeys/ApiKeysTable.tsx b/packages/ui/src/components/APIKeys/ApiKeysTable.tsx index 819f65ecdc0..2584cf93bff 100644 --- a/packages/ui/src/components/APIKeys/ApiKeysTable.tsx +++ b/packages/ui/src/components/APIKeys/ApiKeysTable.tsx @@ -42,7 +42,7 @@ export const APIKeysTable = ({ Name Last used - {canManageAPIKeys && Actions} + {canManageAPIKeys && Actions} @@ -98,7 +98,7 @@ export const APIKeysTable = ({ {canManageAPIKeys && ( - + ({ - textAlign: 'left', + textAlign: 'start', padding: `${t.sizes.$4} ${t.sizes.$5} ${t.sizes.$4} ${t.sizes.$6}`, })} > diff --git a/packages/ui/src/components/APIKeys/RevokeAPIKeyConfirmationModal.tsx b/packages/ui/src/components/APIKeys/RevokeAPIKeyConfirmationModal.tsx index a8aa6e1d6fb..c21e709931b 100644 --- a/packages/ui/src/components/APIKeys/RevokeAPIKeyConfirmationModal.tsx +++ b/packages/ui/src/components/APIKeys/RevokeAPIKeyConfirmationModal.tsx @@ -74,7 +74,7 @@ export const RevokeAPIKeyConfirmationModal = ({ > ({ - textAlign: 'left', + textAlign: 'start', padding: `${t.sizes.$4} ${t.sizes.$5} ${t.sizes.$4} ${t.sizes.$6}`, })} > diff --git a/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx b/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx index 0bc7f29a13d..df71448620f 100644 --- a/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx +++ b/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx @@ -139,7 +139,7 @@ export const CreateOrganizationForm = withCardStateProvider((props: CreateOrgani headerSubtitle={props?.startPage?.headerSubtitle} headerTitleTextVariant={headerTitleTextVariant} headerSubtitleTextVariant={headerSubtitleTextVariant} - sx={t => ({ minHeight: t.sizes.$60, gap: t.space.$6, textAlign: 'left' })} + sx={t => ({ minHeight: t.sizes.$60, gap: t.space.$6, textAlign: 'start' })} > ({ minHeight: t.sizes.$60, textAlign: 'left' })} + sx={t => ({ minHeight: t.sizes.$60, textAlign: 'start' })} > {organization && ( { ({ width: '100%', - paddingLeft: t.sizes.$4, - paddingRight: t.sizes.$6, + paddingInlineStart: t.sizes.$4, + paddingInlineEnd: t.sizes.$6, whiteSpace: 'nowrap', })} > @@ -199,6 +199,7 @@ const ImpersonationFabInternal = () => { position: 'fixed', overflow: 'hidden', top: `var(${topProperty}, ${defaultTop}px)`, + // eslint-disable-next-line custom-rules/no-physical-css-properties -- Complex JS-based positioning via CSS custom properties right: `var(${rightProperty}, ${defaultRight}px)`, zIndex: t.zIndices.$fab, boxShadow: t.shadows.$fabShadow, diff --git a/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx b/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx index 8555248176f..efcd76b684a 100644 --- a/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx +++ b/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx @@ -81,7 +81,7 @@ export function OAuthConsentInternal() { sx={t => ({ position: 'absolute', bottom: `calc(${t.space.$3} * -1)`, - right: `calc(${t.space.$3} * -1)`, + insetInlineEnd: `calc(${t.space.$3} * -1)`, })} /> @@ -112,7 +112,7 @@ export function OAuthConsentInternal() { ({ - textAlign: 'left', + textAlign: 'start', borderWidth: t.borderWidths.$normal, borderStyle: t.borderStyles.$solid, borderColor: t.colors.$borderAlpha100, @@ -157,7 +157,7 @@ export function OAuthConsentInternal() { background: t.colors.$colorMutedForeground, borderRadius: t.radii.$circle, transform: 'translateY(-0.1875rem)', - marginRight: t.space.$2, + marginInlineEnd: t.space.$2, flexShrink: 0, }, })} diff --git a/packages/ui/src/components/OrganizationProfile/ActiveMembersList.tsx b/packages/ui/src/components/OrganizationProfile/ActiveMembersList.tsx index cfb28a0fa9f..ef713846990 100644 --- a/packages/ui/src/components/OrganizationProfile/ActiveMembersList.tsx +++ b/packages/ui/src/components/OrganizationProfile/ActiveMembersList.tsx @@ -125,7 +125,7 @@ const MemberRow = (props: { /> - + - + { justify={actionSlot ? 'between' : 'end'} sx={t => ({ width: '100%', - marginLeft: 'auto', + marginInlineStart: 'auto', padding: `${t.space.$none} ${t.space.$1}`, })} gap={actionSlot ? 2 : undefined} diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationGeneralPage.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationGeneralPage.tsx index d8a766f3223..a062f131014 100644 --- a/packages/ui/src/components/OrganizationProfile/OrganizationGeneralPage.tsx +++ b/packages/ui/src/components/OrganizationProfile/OrganizationGeneralPage.tsx @@ -162,7 +162,7 @@ const OrganizationDomainsSection = () => { ({ - paddingLeft: t.space.$9, + paddingInlineStart: t.space.$9, })} colorScheme='secondary' /> @@ -198,7 +198,7 @@ const OrganizationLeaveSection = () => { sx={t => ({ paddingTop: 0, paddingBottom: 0, - paddingLeft: t.space.$1, + paddingInlineStart: t.space.$1, })} id='organizationDanger' > @@ -252,7 +252,7 @@ const OrganizationDeleteSection = () => { sx={t => ({ paddingTop: 0, paddingBottom: 0, - paddingLeft: t.space.$1, + paddingInlineStart: t.space.$1, })} id={'organizationDanger'} > diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabInvitations.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabInvitations.tsx index 8b212b98018..5857aa1943d 100644 --- a/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabInvitations.tsx +++ b/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabInvitations.tsx @@ -30,8 +30,8 @@ export const OrganizationMembersTabInvitations = withCardStateProvider(() => { width: '100%', gap: t.space.$8, paddingBottom: t.space.$4, - paddingLeft: t.space.$1, - paddingRight: t.space.$1, + paddingInlineStart: t.space.$1, + paddingInlineEnd: t.space.$1, borderBottomWidth: t.borderWidths.$normal, borderBottomStyle: t.borderStyles.$solid, borderBottomColor: t.colors.$borderAlpha100, @@ -68,10 +68,10 @@ export const OrganizationMembersTabInvitations = withCardStateProvider(() => { 'organizationProfile.membersPage.invitationsTab.autoInvitations.headerSubtitle', )} sx={t => ({ - paddingLeft: t.space.$10, + paddingInlineStart: t.space.$10, color: t.colors.$colorMutedForeground, [mqu.md]: { - paddingLeft: 0, + paddingInlineStart: 0, }, })} /> diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabRequests.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabRequests.tsx index ef9231129ac..ca0c9e5302b 100644 --- a/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabRequests.tsx +++ b/packages/ui/src/components/OrganizationProfile/OrganizationMembersTabRequests.tsx @@ -29,8 +29,8 @@ export const OrganizationMembersTabRequests = () => { width: '100%', gap: t.space.$8, paddingBottom: t.space.$4, - paddingLeft: t.space.$1, - paddingRight: t.space.$1, + paddingInlineStart: t.space.$1, + paddingInlineEnd: t.space.$1, borderBottomWidth: t.borderWidths.$normal, borderBottomStyle: t.borderStyles.$solid, borderBottomColor: t.colors.$borderAlpha100, @@ -67,10 +67,10 @@ export const OrganizationMembersTabRequests = () => { 'organizationProfile.membersPage.requestsTab.autoSuggestions.headerSubtitle', )} sx={t => ({ - paddingLeft: t.space.$10, + paddingInlineStart: t.space.$10, color: t.colors.$colorMutedForeground, [mqu.md]: { - paddingLeft: 0, + paddingInlineStart: 0, }, })} /> diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx index fa2c5133528..7717294f89c 100644 --- a/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx +++ b/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx @@ -21,7 +21,7 @@ export const OrganizationProfileAvatarUploader = ( ({ - textAlign: 'left', + textAlign: 'start', marginBottom: t.space.$2, })} localizationKey={localizationKeys('organizationProfile.start.profileSection.uploadAction__title')} diff --git a/packages/ui/src/components/OrganizationProfile/RequestToJoinList.tsx b/packages/ui/src/components/OrganizationProfile/RequestToJoinList.tsx index e17f08f3980..24bfc86a93b 100644 --- a/packages/ui/src/components/OrganizationProfile/RequestToJoinList.tsx +++ b/packages/ui/src/components/OrganizationProfile/RequestToJoinList.tsx @@ -100,7 +100,7 @@ const RequestRow = withCardStateProvider( - + diff --git a/packages/ui/src/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx b/packages/ui/src/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx index 40f39578b47..52c43d01821 100644 --- a/packages/ui/src/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx +++ b/packages/ui/src/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx @@ -115,7 +115,7 @@ export const OrganizationSwitcherPopover = React.forwardRef ({ width: '100%', - paddingRight: t.space.$5, + paddingInlineEnd: t.space.$5, })} > ({ marginLeft: `${t.space.$2}` })} + sx={t => ({ marginInlineStart: `${t.space.$2}` })} /> ); @@ -115,7 +115,7 @@ const NotificationCountBadgeSwitcherTrigger = () => { containerSx={t => ({ position: 'absolute', top: `calc(${t.space.$2} * -1)`, - right: `calc(${t.space.$2} * -1)`, + insetInlineEnd: `calc(${t.space.$2} * -1)`, })} notificationCount={notificationCount} /> diff --git a/packages/ui/src/components/PricingTable/PricingTableDefault.tsx b/packages/ui/src/components/PricingTable/PricingTableDefault.tsx index 879ed453c3c..fd6397b82d1 100644 --- a/packages/ui/src/components/PricingTable/PricingTableDefault.tsx +++ b/packages/ui/src/components/PricingTable/PricingTableDefault.tsx @@ -157,7 +157,7 @@ function Card(props: CardProps) { borderColor: t.colors.$borderAlpha150, borderRadius: t.radii.$xl, overflow: 'hidden', - textAlign: 'left', + textAlign: 'start', })} data-variant={isCompact ? 'compact' : 'default'} > diff --git a/packages/ui/src/components/Statements/Statement.tsx b/packages/ui/src/components/Statements/Statement.tsx index 2cef2bc3e28..3400514a998 100644 --- a/packages/ui/src/components/Statements/Statement.tsx +++ b/packages/ui/src/components/Statements/Statement.tsx @@ -220,7 +220,7 @@ function SectionContentDetailsHeader({ {secondaryTitle && ( diff --git a/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx b/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx index 8d19c89c08b..43dc1b4a052 100644 --- a/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx +++ b/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx @@ -178,7 +178,7 @@ function SubscriptionRow({ subscription, length }: { subscription: BillingSubscr /> ({ marginRight: t.sizes.$1 })} + sx={t => ({ marginInlineEnd: t.sizes.$1 })} > {subscription.plan.name} @@ -199,7 +199,7 @@ function SubscriptionRow({ subscription, length }: { subscription: BillingSubscr ({ - textAlign: 'right', + textAlign: 'end', })} > diff --git a/packages/ui/src/components/UserButton/SessionActions.tsx b/packages/ui/src/components/UserButton/SessionActions.tsx index cd2ec7e9d9d..2947759741e 100644 --- a/packages/ui/src/components/UserButton/SessionActions.tsx +++ b/packages/ui/src/components/UserButton/SessionActions.tsx @@ -170,7 +170,7 @@ export const MultiSessionActions = (props: MultiSessionActionsProps) => { > ({ marginLeft: t.space.$12, padding: `0 ${t.space.$5} ${t.space.$4}`, gap: t.space.$2 })} + sx={t => ({ marginInlineStart: t.space.$12, padding: `0 ${t.space.$5} ${t.space.$4}`, gap: t.space.$2 })} > ({ - paddingLeft: t.space.$2, + paddingInlineStart: t.space.$2, }), ]} > diff --git a/packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx b/packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx index ff00fdb0f25..ad294468763 100644 --- a/packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx +++ b/packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx @@ -198,7 +198,7 @@ const ConnectedAccount = ({ account }: { account: ExternalAccountResource }) => ({ - paddingRight: t.sizes.$1x5, + paddingInlineEnd: t.sizes.$1x5, display: 'inline-block', })} localizationKey={connectedAccountErrorMessage} diff --git a/packages/ui/src/components/UserProfile/DeleteSection.tsx b/packages/ui/src/components/UserProfile/DeleteSection.tsx index ea08292f1c0..55392de00e4 100644 --- a/packages/ui/src/components/UserProfile/DeleteSection.tsx +++ b/packages/ui/src/components/UserProfile/DeleteSection.tsx @@ -27,7 +27,7 @@ export const DeleteSection = () => { ({ paddingLeft: t.space.$1 })} + sx={t => ({ paddingInlineStart: t.space.$1 })} > { borderTopColor: t.colors.$borderAlpha100, gridTemplateColumns: `repeat(3, minmax(0, 1fr))`, '>:not([hidden])~:not([hidden])': { - borderRightWidth: '0px', - borderLeftWidth: '1px', + borderInlineEndWidth: '0px', + borderInlineStartWidth: '1px', borderStyle: 'solid', borderColor: t.colors.$borderAlpha100, }, '>:first-child': { - borderBottomLeftRadius: t.radii.$lg, + borderEndStartRadius: t.radii.$lg, }, '>:last-child': { - borderBottomRightRadius: t.radii.$lg, + borderEndEndRadius: t.radii.$lg, }, })} > diff --git a/packages/ui/src/components/UserProfile/MfaPhoneCodeScreen.tsx b/packages/ui/src/components/UserProfile/MfaPhoneCodeScreen.tsx index 52b4ea80701..01bf70c47ad 100644 --- a/packages/ui/src/components/UserProfile/MfaPhoneCodeScreen.tsx +++ b/packages/ui/src/components/UserProfile/MfaPhoneCodeScreen.tsx @@ -208,7 +208,7 @@ const AddMfa = (props: AddMfaProps) => { icon={ ({ marginRight: t.space.$2 })} + sx={t => ({ marginInlineEnd: t.space.$2 })} /> } localizationKey={localizationKeys('userProfile.mfaPhoneCodePage.primaryButton__addPhoneNumber')} diff --git a/packages/ui/src/components/UserProfile/PasswordSection.tsx b/packages/ui/src/components/UserProfile/PasswordSection.tsx index f2390103512..0402a5c508b 100644 --- a/packages/ui/src/components/UserProfile/PasswordSection.tsx +++ b/packages/ui/src/components/UserProfile/PasswordSection.tsx @@ -37,7 +37,7 @@ export const PasswordSection = () => { ({ - paddingLeft: !passwordEnabled ? '0' : undefined, + paddingInlineStart: !passwordEnabled ? '0' : undefined, paddingTop: t.space.$0x25, paddingBottom: t.space.$0x25, })} diff --git a/packages/ui/src/components/UserProfile/UsernameSection.tsx b/packages/ui/src/components/UserProfile/UsernameSection.tsx index 2e74ce56c66..aa2c0d98979 100644 --- a/packages/ui/src/components/UserProfile/UsernameSection.tsx +++ b/packages/ui/src/components/UserProfile/UsernameSection.tsx @@ -36,7 +36,7 @@ export const UsernameSection = () => { {user.username && ( diff --git a/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx b/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx index 9844b8ef814..cc445a75774 100644 --- a/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx +++ b/packages/ui/src/components/devPrompts/KeylessPrompt/index.tsx @@ -118,10 +118,10 @@ const KeylessPromptInternal = (_props: KeylessPromptProps) => { sx={t => ({ position: 'fixed', bottom: '1.25rem', - right: '1.25rem', + insetInlineEnd: '1.25rem', height: `${t.sizes.$10}`, minWidth: '13.4rem', - paddingLeft: `${t.space.$3}`, + paddingInlineStart: `${t.space.$3}`, borderRadius: '1.25rem', transition: 'all 195ms cubic-bezier(0.2, 0.61, 0.1, 1)', @@ -520,6 +520,7 @@ const KeylessPromptInternal = (_props: KeylessPromptProps) => { href={`#${buttonIdentifier}`} css={css` position: fixed; + /* eslint-disable-next-line custom-rules/no-physical-css-properties -- Skip link - visually hidden pattern */ left: -999px; top: 1rem; z-index: 999999; @@ -531,6 +532,7 @@ const KeylessPromptInternal = (_props: KeylessPromptProps) => { text-decoration: underline; &:focus { + /* eslint-disable-next-line custom-rules/no-physical-css-properties -- Skip link - visually hidden pattern */ left: 1rem; outline: 2px solid; outline-offset: 2px; diff --git a/packages/ui/src/elements/Actions.tsx b/packages/ui/src/elements/Actions.tsx index ca5959372a3..b7ef9f0e7df 100644 --- a/packages/ui/src/elements/Actions.tsx +++ b/packages/ui/src/elements/Actions.tsx @@ -217,7 +217,7 @@ export const Action = (props: ActionProps) => { size={spinnerSize || 'xs'} elementDescriptor={descriptors.spinner} sx={t => ({ - marginRight: t.space.$1, + marginInlineEnd: t.space.$1, })} /> ) : ( diff --git a/packages/ui/src/elements/Alert.tsx b/packages/ui/src/elements/Alert.tsx index ce0f13ee5a6..21270cdc5dc 100644 --- a/packages/ui/src/elements/Alert.tsx +++ b/packages/ui/src/elements/Alert.tsx @@ -39,7 +39,7 @@ export const Alert = (props: AlertProps): JSX.Element | null => { elementDescriptor={descriptors.alertTextContainer} elementId={descriptors.alertTextContainer.setId(variant)} gap={1} - sx={{ textAlign: 'left' }} + sx={{ textAlign: 'start' }} > { overflow: 'hidden', background: t.colors.$colorShimmer, position: 'absolute', + top: 0, + // eslint-disable-next-line custom-rules/no-physical-css-properties -- Shimmer effect uses translateX animation + left: 0, width: '25%', height: '100%', transition: `all ${t.transitionDuration.$slow} ${t.transitionTiming.$easeOut}`, @@ -168,6 +171,9 @@ export const Avatar = (props: AvatarProps) => { boxSizing: 'border-box', content: "''", position: 'absolute', + top: 0, + // eslint-disable-next-line custom-rules/no-physical-css-properties -- Shimmer effect uses translateX animation + left: 0, width: '400%', height: '100%', transform: 'var(--cl-shimmer-hover-after-transform, skewX(45deg) translateX(75%))', diff --git a/packages/ui/src/elements/Badge.tsx b/packages/ui/src/elements/Badge.tsx index cf91b1ecdbf..44f4eef2fc5 100644 --- a/packages/ui/src/elements/Badge.tsx +++ b/packages/ui/src/elements/Badge.tsx @@ -25,10 +25,13 @@ export const LastAuthenticationStrategyBadge = ({ boxShadow: `0 0 0 1px ${t.colors.$colorBackground}`, ...(overlay && { position: 'absolute', - right: -1, + insetInlineEnd: -1, top: -1, transform: `translate(${t.space.$2x5}, calc(-50% - ${t.space.$0x5}))`, pointerEvents: 'none', + '[dir="rtl"] &': { + transform: `translate(-${t.space.$2x5}, calc(-50% - ${t.space.$0x5}))`, + }, }), }), sx, diff --git a/packages/ui/src/elements/Card/CardClerkAndPagesTag.tsx b/packages/ui/src/elements/Card/CardClerkAndPagesTag.tsx index 9008f1d498f..986bccfce8e 100644 --- a/packages/ui/src/elements/Card/CardClerkAndPagesTag.tsx +++ b/packages/ui/src/elements/Card/CardClerkAndPagesTag.tsx @@ -41,8 +41,7 @@ export const CardClerkAndPagesTag = React.memo( ({ gap: displayConfig.branded || withFooterPages ? t.space.$2 : 0, - marginLeft: 'auto', - marginRight: 'auto', + marginInline: 'auto', width: '100%', justifyContent: 'center', alignItems: 'center', diff --git a/packages/ui/src/elements/Card/CardContent.tsx b/packages/ui/src/elements/Card/CardContent.tsx index e49a6711f20..5e3061d2b72 100644 --- a/packages/ui/src/elements/Card/CardContent.tsx +++ b/packages/ui/src/elements/Card/CardContent.tsx @@ -71,7 +71,7 @@ export const CardContent = React.forwardRef((p zIndex: t.zIndices.$modal, position: 'absolute', top: t.space.$2, - right: t.space.$2, + insetInlineEnd: t.space.$2, padding: t.space.$3, })} /> diff --git a/packages/ui/src/elements/ClipboardInput.tsx b/packages/ui/src/elements/ClipboardInput.tsx index 7d8a448b723..b6c042e58d0 100644 --- a/packages/ui/src/elements/ClipboardInput.tsx +++ b/packages/ui/src/elements/ClipboardInput.tsx @@ -24,7 +24,7 @@ export const ClipboardInput = (props: ClipboardInputProps) => { {...rest} value={value} isDisabled - sx={theme => ({ paddingRight: theme.space.$8 })} + sx={theme => ({ paddingInlineEnd: theme.space.$8 })} />