diff --git a/change/@fluentui-web-components-d424d9c8-3c64-4787-bb4b-e3323ae05884.json b/change/@fluentui-web-components-d424d9c8-3c64-4787-bb4b-e3323ae05884.json new file mode 100644 index 00000000000000..7e5f03576ad0bc --- /dev/null +++ b/change/@fluentui-web-components-d424d9c8-3c64-4787-bb4b-e3323ae05884.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Standardized focus treatment to use `outline` instead of a combination of `border` and `box-shadow`", + "packageName": "@fluentui/web-components", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/docs/api-report.md b/packages/web-components/docs/api-report.md index 84250ba5eeee95..17cf813173b604 100644 --- a/packages/web-components/docs/api-report.md +++ b/packages/web-components/docs/api-report.md @@ -789,6 +789,12 @@ export const focusStrokeOuterRecipe: DesignToken; // @public (undocumented) export const focusStrokeWidth: CSSDesignToken; +// @public +export const focusTreatmentBase: CSSDirective; + +// @public +export const focusTreatmentTight: CSSDirective; + // @public (undocumented) export const fontWeight: CSSDesignToken; diff --git a/packages/web-components/src/accordion/accordion-item/accordion-item.styles.ts b/packages/web-components/src/accordion/accordion-item/accordion-item.styles.ts index 31eaf5be37ea9b..718f4f8d1201d0 100644 --- a/packages/web-components/src/accordion/accordion-item/accordion-item.styles.ts +++ b/packages/web-components/src/accordion/accordion-item/accordion-item.styles.ts @@ -12,8 +12,6 @@ import { Swatch } from '../../color/swatch'; import { controlCornerRadius, designUnit, - focusStrokeOuter, - focusStrokeWidth, layerCornerRadius, neutralFillLayerAltRest, neutralFillLayerRecipe, @@ -23,6 +21,7 @@ import { neutralStrokeLayerRest, strokeWidth, } from '../../design-tokens'; +import { focusTreatmentBase } from '../../styles/focus'; import { typeRampBase } from '../../styles/patterns/type-ramp'; import { heightNumber } from '../../styles/size'; @@ -96,18 +95,16 @@ export const accordionItemStyles: ( .button::before { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; + top: calc(${strokeWidth} * -1px); + left: calc(${strokeWidth} * -1px); + right: calc(${strokeWidth} * -1px); + bottom: calc(${strokeWidth} * -1px); cursor: pointer; } .button:${focusVisible}::before { - outline: none; - border: calc(${strokeWidth} * 1px) solid ${focusStrokeOuter}; + ${focusTreatmentBase} border-radius: calc(${layerCornerRadius} * 1px); - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${focusStrokeOuter}; } :host(.expanded) .button:${focusVisible}::before { @@ -184,8 +181,7 @@ export const accordionItemStyles: ( forcedColorsStylesheetBehavior( css` .button:${focusVisible}::before { - border-color: ${SystemColors.Highlight}; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${SystemColors.Highlight}; + outline-color: ${SystemColors.Highlight}; } .icon { fill: ${SystemColors.ButtonText}; diff --git a/packages/web-components/src/breadcrumb-item/breadcrumb-item.styles.ts b/packages/web-components/src/breadcrumb-item/breadcrumb-item.styles.ts index 7844bb39a56137..b08e47d4c50875 100644 --- a/packages/web-components/src/breadcrumb-item/breadcrumb-item.styles.ts +++ b/packages/web-components/src/breadcrumb-item/breadcrumb-item.styles.ts @@ -9,15 +9,13 @@ import { import { SystemColors } from '@microsoft/fast-web-utilities'; import { controlCornerRadius, - focusStrokeOuter, - focusStrokeWidth, neutralForegroundActive, neutralForegroundHover, neutralForegroundRest, - strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; import { heightNumber } from '../styles/index'; +import { focusTreatmentTight } from '../styles/focus'; export const breadcrumbItemStyles: ( context: ElementDefinitionContext, @@ -32,7 +30,6 @@ export const breadcrumbItemStyles: ( ${typeRampBase}; min-width: calc(${heightNumber} * 1px); border-radius: calc(${controlCornerRadius} * 1px); - outline: none; } .listitem { @@ -63,12 +60,8 @@ export const breadcrumbItemStyles: ( color: ${neutralForegroundActive}; } - .control:${focusVisible}::after { - content: ''; - position: absolute; - inset: calc(${strokeWidth} * -1px); - box-shadow: 0 0 0 calc(${focusStrokeWidth} * 1px) ${focusStrokeOuter} inset; - border-radius: inherit; + .control:${focusVisible} { + ${focusTreatmentTight} } :host(:not([href])), @@ -115,8 +108,8 @@ export const breadcrumbItemStyles: ( color: ${SystemColors.HighlightText}; fill: currentcolor; } - :host([href]) .control:${focusVisible}::after { - box-shadow: 0 0 0 calc(${focusStrokeWidth} * 1px) ${SystemColors.LinkText} inset; + .control:${focusVisible} { + outline-color: ${SystemColors.LinkText}; } `, ), diff --git a/packages/web-components/src/checkbox/checkbox.styles.ts b/packages/web-components/src/checkbox/checkbox.styles.ts index 20e9563d8ef897..589c2233b68bf1 100644 --- a/packages/web-components/src/checkbox/checkbox.styles.ts +++ b/packages/web-components/src/checkbox/checkbox.styles.ts @@ -16,8 +16,6 @@ import { controlCornerRadius, designUnit, disabledOpacity, - fillColor, - focusStrokeOuter, foregroundOnAccentRest, neutralFillInputAltActive, neutralFillInputAltFocus, @@ -30,6 +28,7 @@ import { strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentTight } from '../styles/focus'; export const checkboxStyles: (context: ElementDefinitionContext, definition: CheckboxOptions) => ElementStyles = ( context: ElementDefinitionContext, @@ -55,7 +54,6 @@ export const checkboxStyles: (context: ElementDefinitionContext, definition: Che border-radius: calc(${controlCornerRadius} * 1px); border: calc(${strokeWidth} * 1px) solid ${neutralStrokeStrongRest}; background: ${neutralFillInputAltRest}; - outline: none; cursor: pointer; } @@ -107,9 +105,8 @@ export const checkboxStyles: (context: ElementDefinitionContext, definition: Che } :host(:${focusVisible}) .control { - box-shadow: 0 0 0 1px ${fillColor}, 0 0 0 3px ${focusStrokeOuter}; background: ${neutralFillInputAltFocus}; - border-color: ${focusStrokeOuter}; + ${focusTreatmentTight} } :host(.checked) .control { @@ -160,7 +157,7 @@ export const checkboxStyles: (context: ElementDefinitionContext, definition: Che } :host(:${focusVisible}) .control { forced-color-adjust: none; - box-shadow: 0 0 0 1px ${SystemColors.Field}, 0 0 0 3px ${SystemColors.FieldText}; + outline-color: ${SystemColors.FieldText}; background: ${SystemColors.Field}; border-color: ${SystemColors.Highlight}; } @@ -173,9 +170,6 @@ export const checkboxStyles: (context: ElementDefinitionContext, definition: Che background: ${SystemColors.HighlightText}; border-color: ${SystemColors.Highlight}; } - :host(.checked:${focusVisible}) .control { - box-shadow: 0 0 0 1px ${SystemColors.Field}, 0 0 0 3px ${SystemColors.FieldText}; - } :host(.checked) slot[name='checked-indicator'], :host(.checked) slot[name='indeterminate-indicator'] { fill: ${SystemColors.HighlightText}; diff --git a/packages/web-components/src/combobox/combobox.styles.ts b/packages/web-components/src/combobox/combobox.styles.ts index 0f05172d3b45f4..8e7e0061baf3ac 100644 --- a/packages/web-components/src/combobox/combobox.styles.ts +++ b/packages/web-components/src/combobox/combobox.styles.ts @@ -1,8 +1,8 @@ import { css, ElementStyles } from '@microsoft/fast-element'; -import { ComboboxOptions, disabledCursor, ElementDefinitionContext, focusVisible } from '@microsoft/fast-foundation'; +import { ComboboxOptions, disabledCursor, ElementDefinitionContext } from '@microsoft/fast-foundation'; import { selectFilledStyles, selectStyles } from '../select/select.styles'; import { appearanceBehavior } from '../utilities/behaviors'; -import { strokeWidth } from '../design-tokens'; +import { neutralFillInputActive, neutralFillInputHover, neutralFillInputRest, neutralStrokeInputActive, neutralStrokeInputHover, neutralStrokeInputRest, strokeWidth } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; export const comboboxStyles: (context: ElementDefinitionContext, definition: ComboboxOptions) => ElementStyles = ( @@ -12,6 +12,21 @@ export const comboboxStyles: (context: ElementDefinitionContext, definition: Com css` ${selectStyles(context, definition)} + :host { + background: padding-box linear-gradient(${neutralFillInputRest}, ${neutralFillInputRest}), + border-box ${neutralStrokeInputRest}; + } + + :host(:not([disabled]):not([open]):hover) { + background: padding-box linear-gradient(${neutralFillInputHover}, ${neutralFillInputHover}), + border-box ${neutralStrokeInputHover}; + } + + :host(:not([disabled]):not([open]):active) { + background: padding-box linear-gradient(${neutralFillInputActive}, ${neutralFillInputActive}), + border-box ${neutralStrokeInputActive}; + } + :host(:empty) .listbox { display: none; } @@ -35,12 +50,6 @@ export const comboboxStyles: (context: ElementDefinitionContext, definition: Com height: calc(100% - ${strokeWidth} * 1px)); margin: auto 0; width: 100%; - } - - .selected-value:hover, - .selected-value:${focusVisible}, - .selected-value:disabled, - .selected-value:active { outline: none; } `.withBehaviors( diff --git a/packages/web-components/src/data-grid/data-grid-cell.styles.ts b/packages/web-components/src/data-grid/data-grid-cell.styles.ts index c761584b60dd47..7af16ffcfdcbb9 100644 --- a/packages/web-components/src/data-grid/data-grid-cell.styles.ts +++ b/packages/web-components/src/data-grid/data-grid-cell.styles.ts @@ -9,11 +9,12 @@ import { import { controlCornerRadius, designUnit, - focusStrokeOuter, focusStrokeWidth, neutralForegroundRest, + strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentBase } from '../styles/focus'; export const dataGridCellStyles: ( context: ElementDefinitionContext, @@ -21,13 +22,12 @@ export const dataGridCellStyles: ( ) => ElementStyles = (context: ElementDefinitionContext, definition: FoundationElementDefinition) => css` :host { - padding: calc(${designUnit} * 1px) calc(${designUnit} * 3px); + padding: calc((${designUnit} + ${focusStrokeWidth} - ${strokeWidth}) * 1px) calc(((${designUnit} * 3) + ${focusStrokeWidth} - ${strokeWidth}) * 1px); color: ${neutralForegroundRest}; box-sizing: border-box; ${typeRampBase} - border: transparent calc(${focusStrokeWidth} * 1px) solid; + border: transparent calc(${strokeWidth} * 1px) solid; overflow: hidden; - outline: none; white-space: nowrap; border-radius: calc(${controlCornerRadius} * 1px); } @@ -37,22 +37,19 @@ export const dataGridCellStyles: ( } :host(:${focusVisible}) { - border-color: ${focusStrokeOuter}; + ${focusTreatmentBase} } `.withBehaviors( forcedColorsStylesheetBehavior( css` :host { forced-color-adjust: none; - border-color: transparent; background: ${SystemColors.Field}; color: ${SystemColors.FieldText}; } :host(:${focusVisible}) { - border-color: ${SystemColors.FieldText}; - box-shadow: 0 0 0 2px inset ${SystemColors.Field}; - color: ${SystemColors.FieldText}; + outline-color: ${SystemColors.FieldText}; } `, ), diff --git a/packages/web-components/src/flipper/flipper.styles.ts b/packages/web-components/src/flipper/flipper.styles.ts index ed5692712cf28e..397069fda8ef0e 100644 --- a/packages/web-components/src/flipper/flipper.styles.ts +++ b/packages/web-components/src/flipper/flipper.styles.ts @@ -13,14 +13,14 @@ import { controlCornerRadius, designUnit, disabledOpacity, - focusStrokeOuter, - focusStrokeWidth, neutralFillRest, neutralFillStrongActive, neutralFillStrongHover, neutralFillStrongRest, neutralStrokeControlRest, + strokeWidth, } from '../design-tokens'; +import { focusTreatmentBase } from '../styles/focus'; export const flipperStyles: (context: ElementDefinitionContext, definition: FlipperOptions) => ElementStyles = ( context: ElementDefinitionContext, @@ -36,9 +36,8 @@ export const flipperStyles: (context: ElementDefinitionContext, definition: Flip background: padding-box linear-gradient(${neutralFillRest}, ${neutralFillRest}), border-box ${neutralStrokeControlRest}; box-sizing: border-box; - border: calc(${focusStrokeWidth} * 1px) solid transparent; + border: calc(${strokeWidth} * 1px) solid transparent; border-radius: calc(${controlCornerRadius} * 1px); - outline: none; padding: 0; } @@ -66,7 +65,7 @@ export const flipperStyles: (context: ElementDefinitionContext, definition: Flip } :host(:${focusVisible}) { - border-color: ${focusStrokeOuter}; + ${focusTreatmentBase} } :host::-moz-focus-inner { @@ -104,8 +103,7 @@ export const flipperStyles: (context: ElementDefinitionContext, definition: Flip } :host(:${focusVisible}) { forced-color-adjust: none; - border-color: ${SystemColors.Highlight}; - box-shadow: 0 0 0 2px ${SystemColors.ButtonFace}, 0 0 0 4px ${SystemColors.ButtonText}; + outline-color: ${SystemColors.Highlight}; } `, ), diff --git a/packages/web-components/src/listbox-option/listbox-option.styles.ts b/packages/web-components/src/listbox-option/listbox-option.styles.ts index 3869cb88aac264..cf03f9a2a0c442 100644 --- a/packages/web-components/src/listbox-option/listbox-option.styles.ts +++ b/packages/web-components/src/listbox-option/listbox-option.styles.ts @@ -14,7 +14,6 @@ import { controlCornerRadius, designUnit, disabledOpacity, - focusStrokeOuter, focusStrokeWidth, neutralFillSecondaryActive, neutralFillSecondaryHover, @@ -24,8 +23,10 @@ import { neutralFillStealthHover, neutralFillStealthRest, neutralForegroundRest, + strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentBase } from '../styles/focus'; export const optionStyles: ( context: ElementDefinitionContext, @@ -37,16 +38,15 @@ export const optionStyles: ( ${typeRampBase} background: ${neutralFillStealthRest}; border-radius: calc(${controlCornerRadius} * 1px); - border: calc(${focusStrokeWidth} * 1px) solid transparent; + border: calc(${strokeWidth} * 1px) solid transparent; box-sizing: border-box; color: ${neutralForegroundRest}; cursor: pointer; fill: currentcolor; height: calc(${heightNumber} * 1px); - outline: none; overflow: hidden; align-items: center; - padding: 0 calc(${designUnit} * 2.25px); + padding: 0 calc(((${designUnit} * 3) - ${strokeWidth} - 1) * 1px); user-select: none; white-space: nowrap; } @@ -55,7 +55,7 @@ export const optionStyles: ( content: ''; display: block; position: absolute; - left: 0; + left: calc((${focusStrokeWidth} - ${strokeWidth}) * 1px); top: calc((${heightNumber} / 4) - ${focusStrokeWidth} * 1px); width: 3px; height: calc((${heightNumber} / 2) * 1px); @@ -81,7 +81,7 @@ export const optionStyles: ( } :host(:${focusVisible}) { - border-color: ${focusStrokeOuter}; + ${focusTreatmentBase} background: ${neutralFillStealthFocus}; } @@ -156,6 +156,9 @@ export const optionStyles: ( fill: currentcolor; opacity: 1; } + :host(:${focusVisible}) { + outline-color: ${SystemColors.CanvasText}; + } `, ), ); diff --git a/packages/web-components/src/listbox/listbox.styles.ts b/packages/web-components/src/listbox/listbox.styles.ts index 9537045888a4a0..98ab211e32a9d3 100644 --- a/packages/web-components/src/listbox/listbox.styles.ts +++ b/packages/web-components/src/listbox/listbox.styles.ts @@ -8,10 +8,10 @@ import { import { controlCornerRadius, designUnit, - focusStrokeOuter, neutralStrokeRest, strokeWidth, } from '../design-tokens'; +import { focusTreatmentBase } from '../styles/focus'; export const listboxStyles: ( context: ElementDefinitionContext, @@ -24,7 +24,6 @@ export const listboxStyles: ( box-sizing: border-box; flex-direction: column; padding: calc(${designUnit} * 1px) 0; - outline: none; } ::slotted(${context.tagFor(ListboxOption)}) { @@ -32,7 +31,6 @@ export const listboxStyles: ( } :host(:focus-within:not([disabled])) { - border-color: ${focusStrokeOuter}; - box-shadow: 0 0 0 1px ${focusStrokeOuter} inset; + ${focusTreatmentBase} } ` diff --git a/packages/web-components/src/menu-item/menu-item.styles.ts b/packages/web-components/src/menu-item/menu-item.styles.ts index 227bbcb2272078..6dfc6a2d00d8d1 100644 --- a/packages/web-components/src/menu-item/menu-item.styles.ts +++ b/packages/web-components/src/menu-item/menu-item.styles.ts @@ -12,8 +12,6 @@ import { DirectionalStyleSheetBehavior, heightNumber } from '../styles/index'; import { controlCornerRadius, disabledOpacity, - focusStrokeOuter, - focusStrokeWidth, neutralFillStealthActive, neutralFillStealthHover, neutralForegroundHint, @@ -21,6 +19,7 @@ import { strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentBase } from '../styles/focus'; export const menuItemStyles: (context: ElementDefinitionContext, definition: MenuItemOptions) => ElementStyles = ( context: ElementDefinitionContext, @@ -31,7 +30,6 @@ export const menuItemStyles: (context: ElementDefinitionContext, definition: Men contain: layout; overflow: visible; ${typeRampBase} - outline: none; box-sizing: border-box; height: calc(${heightNumber} * 1px); grid-template-columns: minmax(32px, auto) 1fr minmax(32px, auto); @@ -86,8 +84,7 @@ export const menuItemStyles: (context: ElementDefinitionContext, definition: Men } :host(:${focusVisible}) { - border: calc(${strokeWidth} * 1px) solid ${focusStrokeOuter}; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${focusStrokeOuter}; + ${focusTreatmentBase} } :host(:not([disabled]):hover) { @@ -173,7 +170,6 @@ export const menuItemStyles: (context: ElementDefinitionContext, definition: Men justify-content: center; position: relative; box-sizing: border-box; - outline: none; } :host .checkbox-indicator, @@ -221,13 +217,11 @@ export const menuItemStyles: (context: ElementDefinitionContext, definition: Men } :host(.expanded) { background: ${SystemColors.Highlight}; - border-color: ${SystemColors.Highlight}; color: ${SystemColors.HighlightText}; } :host(:${focusVisible}) { background: ${SystemColors.Highlight}; - border-color: ${SystemColors.ButtonText}; - box-shadow: 0 0 0 calc(${strokeWidth} * 1px) inset ${SystemColors.HighlightText}; + outline-color: ${SystemColors.ButtonText}; color: ${SystemColors.HighlightText}; fill: currentcolor; } @@ -243,7 +237,7 @@ export const menuItemStyles: (context: ElementDefinitionContext, definition: Men opacity: 1; } :host([disabled]:${focusVisible}) { - border-color: ${SystemColors.GrayText}; + outline-color: ${SystemColors.GrayText}; } :host .expanded-toggle, :host .checkbox, diff --git a/packages/web-components/src/menu/menu.styles.ts b/packages/web-components/src/menu/menu.styles.ts index c36f16776fb4cc..2935999cf06bb0 100644 --- a/packages/web-components/src/menu/menu.styles.ts +++ b/packages/web-components/src/menu/menu.styles.ts @@ -20,7 +20,7 @@ export const menuStyles: ( border: calc(${strokeWidth} * 1px) solid transparent; border-radius: calc(${layerCornerRadius} * 1px); box-shadow: ${elevationShadowFlyout}; - padding: calc(${designUnit} * 1px) 0; + padding: calc((${designUnit} - ${strokeWidth}) * 1px) 0; max-width: 368px; min-width: 64px; } diff --git a/packages/web-components/src/progress/progress-ring/progress-ring.styles.ts b/packages/web-components/src/progress/progress-ring/progress-ring.styles.ts index e3e4c5a49776a4..a42897c1f3ad27 100644 --- a/packages/web-components/src/progress/progress-ring/progress-ring.styles.ts +++ b/packages/web-components/src/progress/progress-ring/progress-ring.styles.ts @@ -16,7 +16,6 @@ export const progressRingStyles: ( css` ${display('flex')} :host { align-items: center; - outline: none; height: calc(${heightNumber} * 1px); width: calc(${heightNumber} * 1px); } diff --git a/packages/web-components/src/progress/progress/progress.styles.ts b/packages/web-components/src/progress/progress/progress.styles.ts index a0b502a4d42c62..249df334ee10a3 100644 --- a/packages/web-components/src/progress/progress/progress.styles.ts +++ b/packages/web-components/src/progress/progress/progress.styles.ts @@ -21,7 +21,6 @@ export const progressStyles: (context: ElementDefinitionContext, definition: Pro css` ${display('flex')} :host { align-items: center; - outline: none; height: calc((${strokeWidth} * 3) * 1px); } diff --git a/packages/web-components/src/radio/radio.styles.ts b/packages/web-components/src/radio/radio.styles.ts index 1d5aef8794ff84..641ea2eb3ca372 100644 --- a/packages/web-components/src/radio/radio.styles.ts +++ b/packages/web-components/src/radio/radio.styles.ts @@ -15,8 +15,6 @@ import { accentFillRest, designUnit, disabledOpacity, - fillColor, - focusStrokeOuter, foregroundOnAccentRest, neutralFillInputAltActive, neutralFillInputAltFocus, @@ -29,6 +27,7 @@ import { strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentTight } from '../styles/focus'; export const radioStyles: (context: ElementDefinitionContext, definition: RadioOptions) => ElementStyles = ( context: ElementDefinitionContext, @@ -58,7 +57,6 @@ export const radioStyles: (context: ElementDefinitionContext, definition: RadioO border-radius: 50%; border: calc(${strokeWidth} * 1px) solid ${neutralStrokeStrongRest}; background: ${neutralFillInputAltRest}; - outline: none; cursor: pointer; } @@ -108,9 +106,8 @@ export const radioStyles: (context: ElementDefinitionContext, definition: RadioO } :host(:${focusVisible}) .control { - box-shadow: 0 0 0 1px ${fillColor}, 0 0 0 3px ${focusStrokeOuter}; + ${focusTreatmentTight} background: ${neutralFillInputAltFocus}; - border-color: ${focusStrokeOuter}; } :host(.checked) .control { @@ -155,18 +152,14 @@ export const radioStyles: (context: ElementDefinitionContext, definition: RadioO } :host(:${focusVisible}) .control { forced-color-adjust: none; - box-shadow: 0 0 0 1px ${SystemColors.Field}, 0 0 0 3px ${SystemColors.FieldText}; background: ${SystemColors.Field}; - border-color: ${SystemColors.Highlight}; + outline-color: ${SystemColors.FieldText}; } :host(.checked:not(.disabled):hover) .control, :host(.checked:not(.disabled):active) .control { border-color: ${SystemColors.Highlight}; background: ${SystemColors.Highlight}; } - :host(.checked:${focusVisible}) .control { - box-shadow: 0 0 0 1px ${SystemColors.Field}, 0 0 0 3px ${SystemColors.FieldText}; - } :host(.checked) slot[name='checked-indicator'] { fill: ${SystemColors.Highlight}; } diff --git a/packages/web-components/src/select/select.styles.ts b/packages/web-components/src/select/select.styles.ts index 61b88e68c70437..3580a6dcb318d3 100644 --- a/packages/web-components/src/select/select.styles.ts +++ b/packages/web-components/src/select/select.styles.ts @@ -17,7 +17,6 @@ import { designUnit, disabledOpacity, fillColor, - focusStrokeOuter, layerCornerRadius, neutralFillActive, neutralFillHover, @@ -35,6 +34,7 @@ import { strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentBase } from '../styles/focus'; export const selectFilledStyles: (context: ElementDefinitionContext, definition: SelectOptions) => ElementStyles = ( context: ElementDefinitionContext, @@ -64,12 +64,6 @@ export const selectFilledStyles: (context: ElementDefinitionContext, definition: :host(:not([disabled]):not([open]):active) { border-color: ${SystemColors.Highlight}; } - :host(:${focusVisible}) { - forced-color-adjust: none; - background: transparent; - border-color: ${SystemColors.Highlight}; - box-shadow: 0 0 0 1px inset ${SystemColors.Highlight}; - } `, ) ); @@ -156,9 +150,7 @@ export const selectStyles = (context, definition) => } :host(:${focusVisible}) { - border-color: ${focusStrokeOuter}; - outline: none; - box-shadow: 0 0 0 1px inset ${focusStrokeOuter}; + ${focusTreatmentBase} } :host([disabled]) { @@ -235,8 +227,7 @@ export const selectStyles = (context, definition) => } :host(:${focusVisible}) { forced-color-adjust: none; - border-color: ${SystemColors.Highlight}; - box-shadow: 0 0 0 1px inset ${SystemColors.Highlight}; + outline-color: ${SystemColors.Highlight}; } :host([open]) .listbox { background: ${SystemColors.ButtonFace}; diff --git a/packages/web-components/src/styles/focus.ts b/packages/web-components/src/styles/focus.ts new file mode 100644 index 00000000000000..cb5d22389a7e7c --- /dev/null +++ b/packages/web-components/src/styles/focus.ts @@ -0,0 +1,23 @@ +import { cssPartial } from '@microsoft/fast-element'; +import { focusStrokeOuter, focusStrokeWidth, strokeWidth } from '../design-tokens'; + +/** + * Partial CSS for the focus treatment for most typical sized components like Button, Menu Item, etc. + * + * @public + */ +export const focusTreatmentBase = cssPartial` + outline: calc(${focusStrokeWidth} * 1px) solid ${focusStrokeOuter}; + outline-offset: calc(${focusStrokeWidth} * -1px); +`; + +/** + * Partial CSS for the focus treatment for tighter components with spacing constraints, like Checkbox + * and Radio, or plain text like Hypertext appearance Anchor or Breadcrumb Item. + * + * @public + */ +export const focusTreatmentTight = cssPartial` + outline: calc(${focusStrokeWidth} * 1px) solid ${focusStrokeOuter}; + outline-offset: calc(${strokeWidth} * 1px); +`; diff --git a/packages/web-components/src/styles/index.ts b/packages/web-components/src/styles/index.ts index 678d41b288a832..a7def19ce9136a 100644 --- a/packages/web-components/src/styles/index.ts +++ b/packages/web-components/src/styles/index.ts @@ -1,4 +1,5 @@ export * from './direction'; export * from './elevation'; +export * from './focus'; export * from './patterns/'; export * from './size'; diff --git a/packages/web-components/src/styles/patterns/button.styles.ts b/packages/web-components/src/styles/patterns/button.styles.ts index f34d1082116275..de364724422e81 100644 --- a/packages/web-components/src/styles/patterns/button.styles.ts +++ b/packages/web-components/src/styles/patterns/button.styles.ts @@ -22,7 +22,6 @@ import { density, designUnit, focusStrokeInner, - focusStrokeOuter, focusStrokeWidth, foregroundOnAccentActive, foregroundOnAccentHover, @@ -43,6 +42,7 @@ import { strokeWidth, } from '../../design-tokens'; import { typeRampBase } from '../../styles/patterns/type-ramp'; +import { focusTreatmentBase, focusTreatmentTight } from '../focus'; /** * @internal @@ -57,7 +57,6 @@ export const baseButtonStyles = ( ${display('inline-flex')} :host { position: relative; box-sizing: border-box; - outline: none; ${typeRampBase} height: calc(${heightNumber} * 1px); min-width: calc(${heightNumber} * 1px); @@ -109,8 +108,7 @@ export const baseButtonStyles = ( } :host .control:${focusVisible} { - border-color: ${focusStrokeOuter} !important; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${focusStrokeOuter} inset !important; + ${focusTreatmentBase} } :host .control${nonInteractivitySelector} { @@ -159,8 +157,7 @@ export const baseButtonStyles = ( :host(:${focusVisible}) .control { forced-color-adjust: none; background: ${SystemColors.ButtonFace}; - border-color: ${SystemColors.Highlight} !important; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${SystemColors.Highlight} !important; + outline-color: ${SystemColors.Highlight}; } :host([href]) .control { background: ${SystemColors.ButtonFace}; @@ -177,8 +174,7 @@ export const baseButtonStyles = ( } :host([href]) .control:${focusVisible}{ forced-color-adjust: none; - border-color: ${SystemColors.LinkText} !important; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${SystemColors.LinkText} !important; + outline-color: ${SystemColors.LinkText}; } `, ), @@ -213,8 +209,8 @@ export const AccentButtonStyles = ( } :host .control:${focusVisible} { - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${focusStrokeOuter} inset, - 0 0 0 calc(((${focusStrokeWidth} + ${strokeWidth}) - ${strokeWidth}) * 1px) ${focusStrokeInner} inset !important; + ${focusTreatmentBase} + box-shadow: 0 0 0 calc(((${focusStrokeWidth} + ${strokeWidth}) - ${strokeWidth}) * 1px) ${focusStrokeInner} inset !important; } :host .control${nonInteractivitySelector} { @@ -236,8 +232,8 @@ export const AccentButtonStyles = ( } :host .control:${focusVisible} { background: ${SystemColors.Highlight}; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${SystemColors.Highlight} inset, - 0 0 0 calc(((${focusStrokeWidth} + ${strokeWidth}) - ${strokeWidth}) * 1px) ${SystemColors.HighlightText} inset !important; + outline-color: ${SystemColors.Highlight}; + box-shadow: 0 0 0 calc(((${focusStrokeWidth} + ${strokeWidth}) - ${strokeWidth}) * 1px) ${SystemColors.HighlightText} inset !important; } :host([href]) .control { background: ${SystemColors.LinkText}; @@ -246,14 +242,13 @@ export const AccentButtonStyles = ( :host([href]) .control:hover { background: ${SystemColors.ButtonFace}; border-color: ${SystemColors.LinkText}; - box-shadow: none; color: ${SystemColors.LinkText}; fill: currentcolor; } :host([href]) .control:${focusVisible} { background: ${SystemColors.LinkText}; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${SystemColors.LinkText} inset, - 0 0 0 calc(((${focusStrokeWidth} + ${strokeWidth}) - ${strokeWidth}) * 1px) ${SystemColors.HighlightText} inset !important; + outline-color: ${SystemColors.LinkText}; + box-shadow: 0 0 0 calc(((${focusStrokeWidth} + ${strokeWidth}) - ${strokeWidth}) * 1px) ${SystemColors.HighlightText} inset !important; color: ${SystemColors.HighlightText}; fill: currentcolor; } @@ -311,7 +306,7 @@ export const HypertextStyles = ( } :host .control:${focusVisible} { - box-shadow: 0 0 0 calc(${focusStrokeWidth} * 1px) ${focusStrokeOuter} !important; + ${focusTreatmentTight} } :host .control${nonInteractivitySelector} { @@ -374,7 +369,6 @@ export const LightweightButtonStyles = ( :host .control:${focusVisible} { border-color: ${SystemColors.Highlight}; background: ${SystemColors.Highlight}; - box-shadow: none; color: ${SystemColors.HighlightText}; } :host([href]) .control { @@ -384,7 +378,6 @@ export const LightweightButtonStyles = ( :host([href]) .control:hover, :host([href]) .control:${focusVisible} { background: ${SystemColors.ButtonFace}; - box-shadow: none; color: ${SystemColors.LinkText}; } `, @@ -432,7 +425,7 @@ export const OutlineButtonStyles = ( border-color: ${SystemColors.LinkText}; } :host([href]) .control:hover { - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${SystemColors.LinkText}; + outline-color: ${SystemColors.LinkText}; color: ${SystemColors.LinkText}; } `, @@ -478,7 +471,6 @@ export const StealthButtonStyles = ( :host .control:${focusVisible} { background: ${SystemColors.Highlight}; border-color: ${SystemColors.Highlight}; - box-shadow: none !important; color: ${SystemColors.HighlightText}; fill: currentcolor; } @@ -490,7 +482,6 @@ export const StealthButtonStyles = ( :host([href]) .control:${focusVisible} { background: ${SystemColors.LinkText}; border-color: ${SystemColors.LinkText}; - box-shadow: none !important; color: ${SystemColors.HighlightText}; fill: currentcolor; } diff --git a/packages/web-components/src/styles/patterns/input.styles.ts b/packages/web-components/src/styles/patterns/input.styles.ts index 610ccd5c76a0a1..fdb8c5cb54d683 100644 --- a/packages/web-components/src/styles/patterns/input.styles.ts +++ b/packages/web-components/src/styles/patterns/input.styles.ts @@ -3,7 +3,6 @@ import { DesignToken, disabledCursor, ElementDefinitionContext, - focusVisible, FoundationElementDefinition, } from '@microsoft/fast-foundation'; import { SystemColors } from '@microsoft/fast-web-utilities'; @@ -74,7 +73,6 @@ export const inputStyles: ( ${typeRampBase} color: ${neutralForegroundRest}; fill: currentcolor; - outline: none; user-select: none; position: relative; } @@ -95,12 +93,6 @@ export const inputStyles: ( .control { width: 100%; - } - - .control:hover, - .control:${focusVisible}, - .control:disabled, - .control:active { outline: none; } @@ -224,7 +216,6 @@ export const inputFilledStyles: ( :host(:focus-within:not([disabled])) ${rootSelector} { border-color: transparent; - box-shadow: none; } `; diff --git a/packages/web-components/src/switch/switch.styles.ts b/packages/web-components/src/switch/switch.styles.ts index f0ff793923e50d..5f6f8c3fa67168 100644 --- a/packages/web-components/src/switch/switch.styles.ts +++ b/packages/web-components/src/switch/switch.styles.ts @@ -16,8 +16,6 @@ import { bodyFont, designUnit, disabledOpacity, - fillColor, - focusStrokeOuter, foregroundOnAccentActive, foregroundOnAccentHover, foregroundOnAccentRest, @@ -32,6 +30,7 @@ import { strokeWidth, } from '../design-tokens'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentTight } from '../styles/focus'; export const switchStyles: (context: ElementDefinitionContext, definition: SwitchOptions) => ElementStyles = ( context: ElementDefinitionContext, @@ -69,7 +68,6 @@ export const switchStyles: (context: ElementDefinitionContext, definition: Switc .switch { position: relative; - outline: none; box-sizing: border-box; width: calc(((${heightNumber} / 2) + ${designUnit}) * 2px); height: calc(((${heightNumber} / 2) + ${designUnit}) * 1px); @@ -90,9 +88,8 @@ export const switchStyles: (context: ElementDefinitionContext, definition: Switc } :host(:${focusVisible}) .switch { - box-shadow: 0 0 0 1px ${fillColor}, 0 0 0 3px ${focusStrokeOuter}; + ${focusTreatmentTight} background: ${neutralFillInputAltFocus}; - border-color: ${focusStrokeOuter}; } :host(.checked) .switch { @@ -166,11 +163,6 @@ export const switchStyles: (context: ElementDefinitionContext, definition: Switc fill: ${foregroundOnAccentActive}; } - :host(.checked:${focusVisible}:not(.disabled)) .switch { - box-shadow: 0 0 0 1px ${fillColor}, 0 0 0 3px ${focusStrokeOuter}; - border-color: transparent; - } - .unchecked-message { display: block; } @@ -237,15 +229,9 @@ export const switchStyles: (context: ElementDefinitionContext, definition: Switc } :host(:${focusVisible}) .switch { forced-color-adjust: none; - background: ${SystemColors.Field}; + background: ${SystemColors.Field}; border-color: ${SystemColors.Highlight}; - box-shadow: 0 0 0 1px ${SystemColors.Highlight}, 0 0 0 3px ${SystemColors.FieldText}; - } - :host(.checked:${focusVisible}:not(.disabled)) .switch { - forced-color-adjust: none; - background: ${SystemColors.Highlight}; - box-shadow: 0 0 0 1px ${SystemColors.Field}, 0 0 0 3px ${SystemColors.FieldText}; - border-color: ${SystemColors.Field}; + outline-color: ${SystemColors.FieldText}; } :host(.disabled) { opacity: 1; diff --git a/packages/web-components/src/tabs/tab/tab.styles.ts b/packages/web-components/src/tabs/tab/tab.styles.ts index aa6bdfd4f77468..1f56be856a648a 100644 --- a/packages/web-components/src/tabs/tab/tab.styles.ts +++ b/packages/web-components/src/tabs/tab/tab.styles.ts @@ -12,12 +12,11 @@ import { controlCornerRadius, density, designUnit, - focusStrokeOuter, - focusStrokeWidth, neutralForegroundRest, strokeWidth, } from '../../design-tokens'; import { typeRampBase } from '../../styles/patterns/type-ramp'; +import { focusTreatmentBase } from '../../styles/focus'; export const tabStyles: (context: ElementDefinitionContext, definition: FoundationElementDefinition) => ElementStyles = (context: ElementDefinitionContext, definition: FoundationElementDefinition) => @@ -34,7 +33,6 @@ export const tabStyles: (context: ElementDefinitionContext, definition: Foundati justify-content: center; grid-row: 1 / 3; cursor: pointer; - outline: none; } :host([aria-selected='true']) { @@ -47,8 +45,7 @@ export const tabStyles: (context: ElementDefinitionContext, definition: Foundati } :host(:${focusVisible}) { - border-color: ${focusStrokeOuter}; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${focusStrokeOuter} inset; + ${focusTreatmentBase} } :host(.vertical) { @@ -90,8 +87,7 @@ export const tabStyles: (context: ElementDefinitionContext, definition: Foundati } :host(:${focusVisible}) { background: transparent; - border-color: ${SystemColors.ButtonText}; - box-shadow: none; + outline-color: ${SystemColors.ButtonText}; } `, ), diff --git a/packages/web-components/src/text-area/text-area.stories.ts b/packages/web-components/src/text-area/text-area.stories.ts index c69e25f309001e..6fd839ae872edd 100644 --- a/packages/web-components/src/text-area/text-area.stories.ts +++ b/packages/web-components/src/text-area/text-area.stories.ts @@ -5,8 +5,8 @@ export default { component: fluentTextArea, argTypes: { appearance: { - defaultValue: 'outlined', - options: ['filled', 'outlined'], + defaultValue: 'outline', + options: ['filled', 'outline'], control: { type: 'radio' }, }, autoFocus: { diff --git a/packages/web-components/src/toolbar/toolbar.styles.ts b/packages/web-components/src/toolbar/toolbar.styles.ts index d3ed4abdc4ca92..75c1ebac76f312 100644 --- a/packages/web-components/src/toolbar/toolbar.styles.ts +++ b/packages/web-components/src/toolbar/toolbar.styles.ts @@ -7,7 +7,8 @@ import { FoundationElementDefinition, } from '@microsoft/fast-foundation'; import { SystemColors } from '@microsoft/fast-web-utilities'; -import { designUnit, fillColor, focusStrokeWidth, neutralStrokeFocus, strokeWidth } from '../design-tokens'; +import { designUnit, fillColor } from '../design-tokens'; +import { focusTreatmentBase } from '../styles/focus'; export const toolbarStyles: ( context: ElementDefinitionContext, @@ -24,7 +25,7 @@ export const toolbarStyles: ( } :host(${focusVisible}) { - outline: calc(${strokeWidth} * 1px) solid ${neutralStrokeFocus}; + ${focusTreatmentBase} } .positioning-region { @@ -78,7 +79,7 @@ export const toolbarStyles: ( forcedColorsStylesheetBehavior( css` :host(:${focusVisible}) { - box-shadow: 0 0 0 calc(${focusStrokeWidth} * 1px) ${SystemColors.Highlight}; + outline-color: ${SystemColors.Highlight}; color: ${SystemColors.ButtonText}; forced-color-adjust: none; } diff --git a/packages/web-components/src/tree-item/tree-item.styles.ts b/packages/web-components/src/tree-item/tree-item.styles.ts index 95f20b7194f941..28a6efc4dd63d1 100644 --- a/packages/web-components/src/tree-item/tree-item.styles.ts +++ b/packages/web-components/src/tree-item/tree-item.styles.ts @@ -18,7 +18,6 @@ import { density, designUnit, disabledOpacity, - focusStrokeOuter, focusStrokeWidth, neutralFillSecondaryRecipe, neutralFillSecondaryRest, @@ -31,6 +30,7 @@ import { } from '../design-tokens'; import { Swatch } from '../color/swatch'; import { typeRampBase } from '../styles/patterns/type-ramp'; +import { focusTreatmentBase } from '../styles/focus'; const ltr = css` .expand-collapse-button svg { @@ -96,14 +96,6 @@ export const treeItemStyles: (context: ElementDefinitionContext, definition: Tre --tree-item-nested-width: 0; } - :host(:focus) > .positioning-region { - outline: none; - } - - :host(:focus) .content-region { - outline: none; - } - .positioning-region { display: flex; position: relative; @@ -115,8 +107,7 @@ export const treeItemStyles: (context: ElementDefinitionContext, definition: Tre } :host(:${focusVisible}) .positioning-region { - border-color: ${focusStrokeOuter}; - box-shadow: 0 0 0 calc((${focusStrokeWidth} - ${strokeWidth}) * 1px) ${focusStrokeOuter} inset; + ${focusTreatmentBase} } .positioning-region::before { @@ -156,7 +147,6 @@ export const treeItemStyles: (context: ElementDefinitionContext, definition: Tre background: none; border: none; border-radius: calc(${controlCornerRadius} * 1px); - outline: none; ${ /* Width and Height should be based off calc(glyph-size-number + (design-unit * 4) * 1px) - update when density story is figured out */ '' @@ -269,8 +259,7 @@ export const treeItemStyles: (context: ElementDefinitionContext, definition: Tre } :host(:${focusVisible}) .positioning-region { forced-color-adjust: none; - border-color: ${SystemColors.ButtonText}; - box-shadow: 0 0 0 2px inset ${SystemColors.ButtonFace}; + outline-color: ${SystemColors.ButtonFace}; } :host([disabled]), :host([disabled]) .content-region, diff --git a/packages/web-components/src/tree-view/tree-view.styles.ts b/packages/web-components/src/tree-view/tree-view.styles.ts index e55a6c889a6644..84ddd37d2b77c5 100644 --- a/packages/web-components/src/tree-view/tree-view.styles.ts +++ b/packages/web-components/src/tree-view/tree-view.styles.ts @@ -15,8 +15,4 @@ export const treeViewStyles: ( min-width: fit-content; font-size: 0; } - - :host:focus-visible { - outline: none; - } `;