diff --git a/change/@fluentui-react-switch-608a7efd-4e5c-461f-a202-04608d8a629e.json b/change/@fluentui-react-switch-608a7efd-4e5c-461f-a202-04608d8a629e.json new file mode 100644 index 0000000000000..d188717c29f5f --- /dev/null +++ b/change/@fluentui-react-switch-608a7efd-4e5c-461f-a202-04608d8a629e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "chore: Update Switch to use griffel reset styles", + "packageName": "@fluentui/react-switch", + "email": "behowell@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-switch/src/components/Switch/useSwitchStyles.ts b/packages/react-components/react-switch/src/components/Switch/useSwitchStyles.ts index 11f43114c38cb..15e7255f6728a 100644 --- a/packages/react-components/react-switch/src/components/Switch/useSwitchStyles.ts +++ b/packages/react-components/react-switch/src/components/Switch/useSwitchStyles.ts @@ -1,6 +1,6 @@ import { createFocusOutlineStyle } from '@fluentui/react-tabster'; import { tokens } from '@fluentui/react-theme'; -import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { makeResetStyles, makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { SlotClassNames } from '@fluentui/react-utilities'; import type { SwitchSlots, SwitchState } from './Switch.types'; @@ -22,175 +22,172 @@ const trackHeight = 20; const trackWidth = 40; const thumbSize = trackHeight - spaceBetweenThumbAndTrack; -const useRootStyles = makeStyles({ - base: { - alignItems: 'flex-start', - boxSizing: 'border-box', - display: 'inline-flex', - position: 'relative', +const useRootBaseClassName = makeResetStyles({ + alignItems: 'flex-start', + boxSizing: 'border-box', + display: 'inline-flex', + position: 'relative', - ...createFocusOutlineStyle({ style: {}, selector: 'focus-within' }), - }, + ...createFocusOutlineStyle({ style: {}, selector: 'focus-within' }), +}); +const useRootStyles = makeStyles({ vertical: { flexDirection: 'column', }, }); -const useIndicatorStyles = makeStyles({ - base: { - ...shorthands.borderRadius(tokens.borderRadiusCircular), - ...shorthands.borderStyle('solid'), - ...shorthands.borderWidth('1px'), - lineHeight: 0, - boxSizing: 'border-box', - fill: 'currentColor', - flexShrink: 0, - fontSize: `${thumbSize}px`, - height: `${trackHeight}px`, - ...shorthands.margin(tokens.spacingVerticalS, tokens.spacingHorizontalS), - pointerEvents: 'none', +const useIndicatorBaseClassName = makeResetStyles({ + borderRadius: tokens.borderRadiusCircular, + border: '1px solid', + lineHeight: 0, + boxSizing: 'border-box', + fill: 'currentColor', + flexShrink: 0, + fontSize: `${thumbSize}px`, + height: `${trackHeight}px`, + margin: tokens.spacingVerticalS + ' ' + tokens.spacingHorizontalS, + pointerEvents: 'none', + transitionDuration: tokens.durationNormal, + transitionTimingFunction: tokens.curveEasyEase, + transitionProperty: 'background, border, color', + width: `${trackWidth}px`, + + '@media screen and (prefers-reduced-motion: reduce)': { + transitionDuration: '0.01ms', + }, + + '> *': { transitionDuration: tokens.durationNormal, transitionTimingFunction: tokens.curveEasyEase, - transitionProperty: 'background, border, color', - width: `${trackWidth}px`, + transitionProperty: 'transform', '@media screen and (prefers-reduced-motion: reduce)': { transitionDuration: '0.01ms', }, - - '> *': { - transitionDuration: tokens.durationNormal, - transitionTimingFunction: tokens.curveEasyEase, - transitionProperty: 'transform', - - '@media screen and (prefers-reduced-motion: reduce)': { - transitionDuration: '0.01ms', - }, - }, }, +}); +const useIndicatorStyles = makeStyles({ labelAbove: { marginTop: 0, }, }); -const useInputStyles = makeStyles({ - base: { - boxSizing: 'border-box', - cursor: 'pointer', - height: '100%', - ...shorthands.margin(0), - opacity: 0, - position: 'absolute', +const useInputBaseClassName = makeResetStyles({ + boxSizing: 'border-box', + cursor: 'pointer', + height: '100%', + margin: 0, + opacity: 0, + position: 'absolute', + + // Calculate the width of the hidden input by taking into account the size of the indicator + the padding around it. + // This is done so that clicking on that "empty space" still toggles the switch. + width: `calc(${trackWidth}px + 2 * ${tokens.spacingHorizontalS})`, + + // Checked (both enabled and disabled) + ':checked': { + [`& ~ .${switchClassNames.indicator}`]: { + '> *': { + transform: `translateX(${trackWidth - thumbSize - spaceBetweenThumbAndTrack}px)`, + }, + }, + }, - // Calculate the width of the hidden input by taking into account the size of the indicator + the padding around it. - // This is done so that clicking on that "empty space" still toggles the switch. - width: `calc(${trackWidth}px + 2 * ${tokens.spacingHorizontalS})`, + // Disabled (both checked and unchecked) + ':disabled': { + cursor: 'default', - // Checked (both enabled and disabled) - ':checked': { - [`& ~ .${switchClassNames.indicator}`]: { - '> *': { - transform: `translateX(${trackWidth - thumbSize - spaceBetweenThumbAndTrack}px)`, - }, - }, + [`& ~ .${switchClassNames.indicator}`]: { + color: tokens.colorNeutralForegroundDisabled, }, - // Disabled (both checked and unchecked) - ':disabled': { + [`& ~ .${switchClassNames.label}`]: { cursor: 'default', + color: tokens.colorNeutralForegroundDisabled, + }, + }, - [`& ~ .${switchClassNames.indicator}`]: { - color: tokens.colorNeutralForegroundDisabled, - }, + // Enabled and unchecked + ':enabled:not(:checked)': { + [`& ~ .${switchClassNames.indicator}`]: { + color: tokens.colorNeutralStrokeAccessible, + borderColor: tokens.colorNeutralStrokeAccessible, + }, - [`& ~ .${switchClassNames.label}`]: { - cursor: 'default', - color: tokens.colorNeutralForegroundDisabled, - }, + [`& ~ .${switchClassNames.label}`]: { + color: tokens.colorNeutralForeground1, }, - // Enabled and unchecked - ':enabled:not(:checked)': { + ':hover': { [`& ~ .${switchClassNames.indicator}`]: { - color: tokens.colorNeutralStrokeAccessible, - ...shorthands.borderColor(tokens.colorNeutralStrokeAccessible), - }, - - [`& ~ .${switchClassNames.label}`]: { - color: tokens.colorNeutralForeground1, + color: tokens.colorNeutralStrokeAccessibleHover, + borderColor: tokens.colorNeutralStrokeAccessibleHover, }, + }, - ':hover': { - [`& ~ .${switchClassNames.indicator}`]: { - color: tokens.colorNeutralStrokeAccessibleHover, - ...shorthands.borderColor(tokens.colorNeutralStrokeAccessibleHover), - }, + ':hover:active': { + [`& ~ .${switchClassNames.indicator}`]: { + color: tokens.colorNeutralStrokeAccessiblePressed, + borderColor: tokens.colorNeutralStrokeAccessiblePressed, }, + }, + }, - ':hover:active': { - [`& ~ .${switchClassNames.indicator}`]: { - color: tokens.colorNeutralStrokeAccessiblePressed, - ...shorthands.borderColor(tokens.colorNeutralStrokeAccessiblePressed), - }, - }, + // Enabled and checked + ':enabled:checked': { + [`& ~ .${switchClassNames.indicator}`]: { + backgroundColor: tokens.colorCompoundBrandBackground, + color: tokens.colorNeutralForegroundInverted, + borderColor: tokens.colorTransparentStroke, }, - // Enabled and checked - ':enabled:checked': { + ':hover': { [`& ~ .${switchClassNames.indicator}`]: { - backgroundColor: tokens.colorCompoundBrandBackground, - color: tokens.colorNeutralForegroundInverted, - ...shorthands.borderColor(tokens.colorTransparentStroke), + backgroundColor: tokens.colorCompoundBrandBackgroundHover, + borderColor: tokens.colorTransparentStrokeInteractive, }, + }, - ':hover': { - [`& ~ .${switchClassNames.indicator}`]: { - backgroundColor: tokens.colorCompoundBrandBackgroundHover, - ...shorthands.borderColor(tokens.colorTransparentStrokeInteractive), - }, + ':hover:active': { + [`& ~ .${switchClassNames.indicator}`]: { + backgroundColor: tokens.colorCompoundBrandBackgroundPressed, + borderColor: tokens.colorTransparentStrokeInteractive, }, + }, + }, - ':hover:active': { - [`& ~ .${switchClassNames.indicator}`]: { - backgroundColor: tokens.colorCompoundBrandBackgroundPressed, - ...shorthands.borderColor(tokens.colorTransparentStrokeInteractive), - }, - }, + // Disabled and unchecked + ':disabled:not(:checked)': { + [`& ~ .${switchClassNames.indicator}`]: { + borderColor: tokens.colorNeutralStrokeDisabled, }, + }, - // Disabled and unchecked - ':disabled:not(:checked)': { - [`& ~ .${switchClassNames.indicator}`]: { - ...shorthands.borderColor(tokens.colorNeutralStrokeDisabled), - }, + // Disabled and checked + ':disabled:checked': { + [`& ~ .${switchClassNames.indicator}`]: { + backgroundColor: tokens.colorNeutralBackgroundDisabled, + borderColor: tokens.colorTransparentStrokeDisabled, }, + }, - // Disabled and checked - ':disabled:checked': { + '@media (forced-colors: active)': { + ':disabled': { [`& ~ .${switchClassNames.indicator}`]: { - backgroundColor: tokens.colorNeutralBackgroundDisabled, - ...shorthands.borderColor(tokens.colorTransparentStrokeDisabled), + color: 'GrayText', + borderColor: 'GrayText', }, - }, - }, - highContrast: { - '@media (forced-colors: active)': { - ':disabled': { - [`& ~ .${switchClassNames.indicator}`]: { - color: 'GrayText', - ...shorthands.borderColor('GrayText'), - }, - - [`& ~ .${switchClassNames.label}`]: { - color: 'GrayText', - }, + [`& ~ .${switchClassNames.label}`]: { + color: 'GrayText', }, }, }, +}); +const useInputStyles = makeStyles({ before: { right: 0, top: 0, @@ -206,6 +203,7 @@ const useInputStyles = makeStyles({ }, }); +// Can't use makeResetStyles here because Label is a component that may itself use makeResetStyles. const useLabelStyles = makeStyles({ base: { cursor: 'pointer', @@ -234,8 +232,11 @@ const useLabelStyles = makeStyles({ * Apply styling to the Switch slots based on the state */ export const useSwitchStyles_unstable = (state: SwitchState): SwitchState => { + const rootBaseClassName = useRootBaseClassName(); const rootStyles = useRootStyles(); + const indicatorBaseClassName = useIndicatorBaseClassName(); const indicatorStyles = useIndicatorStyles(); + const inputBaseClassName = useInputBaseClassName(); const inputStyles = useInputStyles(); const labelStyles = useLabelStyles(); @@ -243,22 +244,21 @@ export const useSwitchStyles_unstable = (state: SwitchState): SwitchState => { state.root.className = mergeClasses( switchClassNames.root, - rootStyles.base, + rootBaseClassName, labelPosition === 'above' && rootStyles.vertical, state.root.className, ); state.indicator.className = mergeClasses( switchClassNames.indicator, - indicatorStyles.base, + indicatorBaseClassName, label && labelPosition === 'above' && indicatorStyles.labelAbove, state.indicator.className, ); state.input.className = mergeClasses( switchClassNames.input, - inputStyles.base, - inputStyles.highContrast, + inputBaseClassName, label && inputStyles[labelPosition], state.input.className, );