From a5c2e3b8b2f7c26a0513fb13adb269a3f2249620 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 5 May 2025 09:21:48 -0400 Subject: [PATCH 1/3] handle switch feedback --- .../ui/customizables/elementDescriptors.ts | 2 + packages/clerk-js/src/ui/elements/Switch.tsx | 59 ++++++++++--------- .../clerk-js/src/ui/styledSystem/common.ts | 19 ++++-- packages/types/src/appearance.ts | 2 + 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts index 283e33ea458..f2ed2bdabe9 100644 --- a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts +++ b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts @@ -312,7 +312,9 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'segmentedControlButton', 'switchRoot', + 'switchIndicator', 'switchThumb', + 'switchLabel', 'alert', 'alertIcon', diff --git a/packages/clerk-js/src/ui/elements/Switch.tsx b/packages/clerk-js/src/ui/elements/Switch.tsx index c768993ab16..6b58c99fbed 100644 --- a/packages/clerk-js/src/ui/elements/Switch.tsx +++ b/packages/clerk-js/src/ui/elements/Switch.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useId, useState } from 'react'; +import React, { forwardRef, useState } from 'react'; import type { LocalizationKey } from '../customizables'; import { descriptors, Flex, Text } from '../customizables'; @@ -14,44 +14,47 @@ interface SwitchProps { } export const Switch = forwardRef( - ({ checked: controlledChecked, defaultChecked, onChange, disabled = false, 'aria-label': ariaLabel, label }, ref) => { + ({ checked: controlledChecked, defaultChecked, onChange, disabled = false, label }, ref) => { const [internalChecked, setInternalChecked] = useState(!!defaultChecked); const isControlled = controlledChecked !== undefined; const checked = isControlled ? controlledChecked : internalChecked; - const labelId = useId(); - const handleToggle = (_: React.MouseEvent | React.KeyboardEvent) => { + const handleChange = (e: React.ChangeEvent) => { if (disabled) return; if (!isControlled) { - setInternalChecked(!checked); - } - onChange?.(!checked); - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === ' ' || e.key === 'Enter') { - e.preventDefault(); - handleToggle(e); + setInternalChecked(e.target.checked); } + onChange?.(e.target.checked); }; return ( ({ + '&:has(input:focus-visible) > span': { + ...common.focusRingStyles(t), + }, + })} > - + ({ width: t.sizes.$6, height: t.sizes.$4, @@ -65,7 +68,6 @@ export const Switch = forwardRef( outline: 'none', boxSizing: 'border-box', boxShadow: '0px 0px 0px 1px rgba(0, 0, 0, 0.06) inset', - ...common.focusRing(t), })} > ( {label && ( ({ + paddingInlineStart: t.sizes.$2, cursor: disabled ? 'not-allowed' : 'pointer', userSelect: 'none', - }} + })} /> )} diff --git a/packages/clerk-js/src/ui/styledSystem/common.ts b/packages/clerk-js/src/ui/styledSystem/common.ts index b60230e10c2..471711397ab 100644 --- a/packages/clerk-js/src/ui/styledSystem/common.ts +++ b/packages/clerk-js/src/ui/styledSystem/common.ts @@ -130,15 +130,21 @@ const borderColor = (t: InternalTheme, props?: any) => { } as const; }; +const focusRingStyles = (t: InternalTheme) => { + return { + '&::-moz-focus-inner': { border: '0' }, + WebkitTapHighlightColor: 'transparent', + boxShadow: t.shadows.$focusRing.replace('{{color}}', t.colors.$neutralAlpha200), + transitionProperty: t.transitionProperty.$common, + transitionTimingFunction: t.transitionTiming.$common, + transitionDuration: t.transitionDuration.$focusRing, + } as const; +}; + const focusRing = (t: InternalTheme) => { return { '&:focus': { - '&::-moz-focus-inner': { border: '0' }, - WebkitTapHighlightColor: 'transparent', - boxShadow: t.shadows.$focusRing.replace('{{color}}', t.colors.$neutralAlpha200), - transitionProperty: t.transitionProperty.$common, - transitionTimingFunction: t.transitionTiming.$common, - transitionDuration: t.transitionDuration.$focusRing, + ...focusRingStyles(t), }, } as const; }; @@ -198,6 +204,7 @@ const visuallyHidden = () => export const common = { textVariants, borderVariants, + focusRingStyles, focusRing, disabled, borderColor, diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index 56548641cf8..159392ee94d 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -283,7 +283,9 @@ export type ElementsConfig = { segmentedControlButton: WithOptions; switchRoot: WithOptions; + switchIndicator: WithOptions; switchThumb: WithOptions; + switchLabel: WithOptions; avatarBox: WithOptions; avatarImage: WithOptions; From deb67bd0d86df8b2de5219416cd3dc0821c3f4a1 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 5 May 2025 09:28:06 -0400 Subject: [PATCH 2/3] fit-content --- packages/clerk-js/src/ui/elements/Switch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/clerk-js/src/ui/elements/Switch.tsx b/packages/clerk-js/src/ui/elements/Switch.tsx index 6b58c99fbed..c1264803a10 100644 --- a/packages/clerk-js/src/ui/elements/Switch.tsx +++ b/packages/clerk-js/src/ui/elements/Switch.tsx @@ -35,6 +35,7 @@ export const Switch = forwardRef( align='center' as='label' sx={t => ({ + width: 'fit-content', '&:has(input:focus-visible) > span': { ...common.focusRingStyles(t), }, From c313a0a97b4b5a83a59f8edd3d1cb5f2439c4b0b Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 5 May 2025 09:30:32 -0400 Subject: [PATCH 3/3] add data-checked attribute --- packages/clerk-js/src/ui/elements/Switch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/clerk-js/src/ui/elements/Switch.tsx b/packages/clerk-js/src/ui/elements/Switch.tsx index c1264803a10..f7e979a5036 100644 --- a/packages/clerk-js/src/ui/elements/Switch.tsx +++ b/packages/clerk-js/src/ui/elements/Switch.tsx @@ -56,6 +56,7 @@ export const Switch = forwardRef( ({ width: t.sizes.$6, height: t.sizes.$4,