diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2024-10-07 18:32:15 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-08 20:02:47 +0000 |
commit | aef2ea10ecee8df9eadf3d3da4e63c06a9378b8b (patch) | |
tree | 840adef6a40df1e22a584f6e4a75b4cd77c1d814 /server/sonar-web/design-system | |
parent | f49678493a80f08984b13f8003fca362fe081c9f (diff) | |
download | sonarqube-aef2ea10ecee8df9eadf3d3da4e63c06a9378b8b.tar.gz sonarqube-aef2ea10ecee8df9eadf3d3da4e63c06a9378b8b.zip |
SONAR-22290 Fix focus indicator in legacy components
Diffstat (limited to 'server/sonar-web/design-system')
16 files changed, 124 insertions, 132 deletions
diff --git a/server/sonar-web/design-system/src/components/Dropdown.tsx b/server/sonar-web/design-system/src/components/Dropdown.tsx index ab273c08f0c..cf972125f20 100644 --- a/server/sonar-web/design-system/src/components/Dropdown.tsx +++ b/server/sonar-web/design-system/src/components/Dropdown.tsx @@ -18,13 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import { useIntl } from 'react-intl'; import { PopupPlacement, PopupZLevel } from '../helpers/positioning'; import { InputSizeKeys } from '../types/theme'; import { DropdownMenu } from './DropdownMenu'; import { DropdownToggler } from './DropdownToggler'; -import { InteractiveIcon } from './InteractiveIcon'; -import { MenuIcon } from './icons/MenuIcon'; type OnClickCallback = (event?: React.MouseEvent<HTMLElement>) => void; type A11yAttrs = Pick<React.AriaAttributes, 'aria-controls' | 'aria-expanded' | 'aria-haspopup'> & { @@ -137,31 +134,3 @@ export class Dropdown extends React.PureComponent<Readonly<Props>, State> { ); } } - -interface ActionsDropdownProps extends Omit<Props, 'children' | 'overlay'> { - ariaLabel?: string; - buttonSize?: 'small' | 'medium'; - children: React.ReactNode; - toggleClassName?: string; -} - -/** @deprecated Use DropdownMenu.Root and other DropdownMenu.* elements from Echoes instead. - * See the {@link https://xtranet-sonarsource.atlassian.net/wiki/spaces/Platform/pages/3354918914/DropdownMenus | Migration Guide} - */ -export function ActionsDropdown(props: Readonly<ActionsDropdownProps>) { - const { children, buttonSize, ariaLabel, toggleClassName, ...dropdownProps } = props; - - const intl = useIntl(); - - return ( - <Dropdown overlay={children} {...dropdownProps}> - <InteractiveIcon - Icon={MenuIcon} - aria-label={ariaLabel ?? intl.formatMessage({ id: 'menu' })} - className={toggleClassName} - size={buttonSize} - stopPropagation={false} - /> - </Dropdown> - ); -} diff --git a/server/sonar-web/design-system/src/components/DropdownMenu.tsx b/server/sonar-web/design-system/src/components/DropdownMenu.tsx index 82b89ee251e..dff4f3b11e6 100644 --- a/server/sonar-web/design-system/src/components/DropdownMenu.tsx +++ b/server/sonar-web/design-system/src/components/DropdownMenu.tsx @@ -23,7 +23,7 @@ import classNames from 'classnames'; import React, { ForwardedRef, forwardRef } from 'react'; import tw from 'twin.macro'; import { INPUT_SIZES } from '../helpers/constants'; -import { themeBorder, themeColor, themeContrast } from '../helpers/theme'; +import { themeColor, themeContrast } from '../helpers/theme'; import { InputSizeKeys, ThemedProps } from '../types/theme'; import { BaseLink, LinkProps } from './Link'; import NavLink from './NavLink'; @@ -370,12 +370,15 @@ const itemStyle = (props: ThemedProps) => css` color: var(--color); background-color: ${themeColor('dropdownMenuFocus')(props)}; text-decoration: none; - outline: ${themeBorder('focus', 'dropdownMenuFocusBorder')(props)}; - outline-offset: -4px; border: none; border-bottom: none; } + &:focus-visible { + borderLeft: '2px solid var(--echoes-color-focus-default)', + marginLeft: '-2px', + } + &:disabled, &.disabled { color: ${themeContrast('dropdownMenuDisabled')(props)}; diff --git a/server/sonar-web/design-system/src/components/FacetBox.tsx b/server/sonar-web/design-system/src/components/FacetBox.tsx index 22c4e344ce1..acf9a366e1f 100644 --- a/server/sonar-web/design-system/src/components/FacetBox.tsx +++ b/server/sonar-web/design-system/src/components/FacetBox.tsx @@ -188,6 +188,13 @@ const ChevronAndTitle = styled(BareButton)<{ ${tw`sw-items-center`}; cursor: ${({ expandable }) => (expandable ? 'pointer' : 'default')}; + + &:focus-visible { + background: transparent; + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: 4px; + border-radius: var(--echoes-border-radius-200); + } `; const ClearIcon = styled(DestructiveIcon)` diff --git a/server/sonar-web/design-system/src/components/SpotlightTour.tsx b/server/sonar-web/design-system/src/components/SpotlightTour.tsx index caa21689bb6..69131fcaec7 100644 --- a/server/sonar-web/design-system/src/components/SpotlightTour.tsx +++ b/server/sonar-web/design-system/src/components/SpotlightTour.tsx @@ -19,7 +19,14 @@ */ import { keyframes } from '@emotion/react'; import styled from '@emotion/styled'; -import { LinkStandalone } from '@sonarsource/echoes-react'; +import { + Button, + ButtonIcon, + ButtonVariety, + IconX, + LinkStandalone, + TooltipProvider, +} from '@sonarsource/echoes-react'; import React from 'react'; import { useIntl } from 'react-intl'; import ReactJoyride, { @@ -31,9 +38,6 @@ import { LinkProps } from 'react-router-dom'; import tw from 'twin.macro'; import { GLOBAL_POPUP_Z_INDEX, PopupZLevel, themeColor } from '../helpers'; import { findAnchor } from '../helpers/dom'; -import { ButtonPrimary } from '../sonar-aligned/components/buttons'; -import { ButtonLink, WrapperButton } from './buttons'; -import { CloseIcon } from './icons'; import { PopupWrapper } from './popups'; type Placement = 'left' | 'right' | 'top' | 'bottom' | 'center'; @@ -72,7 +76,7 @@ function TooltipComponent({ size, isLastStep, backProps, - skipProps, + skipProps: { 'aria-label': skipPropsAriaLabel, ...skipProps }, closeProps, primaryProps, stepXofYLabel, @@ -162,12 +166,13 @@ function TooltipComponent({ }} > <strong className="sw-typo-lg-semibold sw-mb-2">{step.title}</strong> - <WrapperButton - className="sw-w-[30px] sw-h-[30px] sw--mt-2 sw--mr-2 sw-flex sw-justify-center" + <ButtonIcon + Icon={IconX} + ariaLabel={skipPropsAriaLabel} + className="sw--mt-2 sw--mr-2" + variety={ButtonVariety.DefaultGhost} {...skipProps} - > - <CloseIcon className="sw-mr-0" /> - </WrapperButton> + /> </div> <div>{step.content}</div> @@ -188,15 +193,19 @@ function TooltipComponent({ <span /> <div> {index > 0 && ( - <ButtonLink className="sw-mr-4" {...backProps}> + <Button className="sw-mr-4" variety={ButtonVariety.DefaultGhost} {...backProps}> {backProps.title} - </ButtonLink> + </Button> )} {continuous && !isLastStep && ( - <ButtonPrimary {...primaryProps}>{primaryProps.title}</ButtonPrimary> + <Button variety={ButtonVariety.Primary} {...primaryProps}> + {primaryProps.title} + </Button> )} {(!continuous || isLastStep) && ( - <ButtonPrimary {...closeProps}>{closeProps.title}</ButtonPrimary> + <Button variety={ButtonVariety.Primary} {...closeProps}> + {closeProps.title} + </Button> )} </div> </div> @@ -253,13 +262,15 @@ export function SpotlightTour(props: SpotlightTourProps) { tooltipComponent={( tooltipProps: React.PropsWithChildren<TooltipRenderProps & { step: SpotlightTourStep }>, ) => ( - <TooltipComponent - actionLabel={actionLabel} - actionPath={actionPath} - stepXofYLabel={stepXofYLabel} - width={width} - {...tooltipProps} - /> + <TooltipProvider> + <TooltipComponent + actionLabel={actionLabel} + actionPath={actionPath} + stepXofYLabel={stepXofYLabel} + width={width} + {...tooltipProps} + /> + </TooltipProvider> )} {...otherProps} /> diff --git a/server/sonar-web/design-system/src/components/Switch.tsx b/server/sonar-web/design-system/src/components/Switch.tsx index b22e425e775..397338b3d04 100644 --- a/server/sonar-web/design-system/src/components/Switch.tsx +++ b/server/sonar-web/design-system/src/components/Switch.tsx @@ -20,7 +20,7 @@ import styled from '@emotion/styled'; import { ForwardedRef, forwardRef } from 'react'; import tw from 'twin.macro'; -import { themeBorder, themeColor, themeContrast, themeShadow } from '../helpers'; +import { themeColor, themeContrast, themeShadow } from '../helpers'; import { CheckIcon } from './icons'; interface Props { @@ -95,7 +95,7 @@ const StyledSwitch = styled.button<StyledProps>` background: ${({ active }) => (active ? themeColor('switchActive') : themeColor('switch'))}; border: none; transition: 0.3s ease; - transition-property: background, outline; + transition-property: background; &:hover:not(:disabled), &:active:not(:disabled), @@ -113,8 +113,8 @@ const StyledSwitch = styled.button<StyledProps>` &:focus:not(:disabled), &:active:not(:disabled) { - outline: ${({ active }) => - active ? themeBorder('focus', 'switchActive') : themeBorder('focus', 'switch')}; + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); } `; diff --git a/server/sonar-web/design-system/src/components/Tabs.tsx b/server/sonar-web/design-system/src/components/Tabs.tsx index e216eb2b33b..e498755333b 100644 --- a/server/sonar-web/design-system/src/components/Tabs.tsx +++ b/server/sonar-web/design-system/src/components/Tabs.tsx @@ -21,7 +21,7 @@ import styled from '@emotion/styled'; import { PropsWithChildren } from 'react'; import { FormattedMessage } from 'react-intl'; import tw from 'twin.macro'; -import { OPACITY_20_PERCENT, themeBorder, themeColor } from '../helpers'; +import { themeBorder, themeColor } from '../helpers'; import { BareButton } from '../sonar-aligned/components/buttons'; import { getTabId, getTabPanelId } from '../sonar-aligned/helpers/tabs'; import { Badge } from './Badge'; @@ -131,7 +131,7 @@ const TabButton = styled(BareButton)<{ } &:active { - outline: ${themeBorder('xsActive', 'tabSelected', OPACITY_20_PERCENT)}; + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); z-index: 1; } diff --git a/server/sonar-web/design-system/src/components/__tests__/Dropdown-test.tsx b/server/sonar-web/design-system/src/components/__tests__/Dropdown-test.tsx index eb3c7cfd4d7..fce2fd2bc67 100644 --- a/server/sonar-web/design-system/src/components/__tests__/Dropdown-test.tsx +++ b/server/sonar-web/design-system/src/components/__tests__/Dropdown-test.tsx @@ -20,7 +20,7 @@ import { screen } from '@testing-library/react'; import { renderWithRouter } from '../../helpers/testUtils'; import { ButtonSecondary } from '../../sonar-aligned/components/buttons'; -import { ActionsDropdown, Dropdown } from '../Dropdown'; +import { Dropdown } from '../Dropdown'; describe('Dropdown', () => { it('renders', async () => { @@ -90,18 +90,3 @@ describe('Dropdown', () => { ); } }); - -describe('ActionsDropdown', () => { - it('renders', () => { - setup(); - expect(screen.getByRole('button')).toHaveAccessibleName('menu'); - }); - - function setup() { - return renderWithRouter( - <ActionsDropdown id="test-menu"> - <div id="overlay" /> - </ActionsDropdown>, - ); - } -}); diff --git a/server/sonar-web/design-system/src/components/__tests__/SpotlightTour-test.tsx b/server/sonar-web/design-system/src/components/__tests__/SpotlightTour-test.tsx index 830e86bce97..f95b68f1a2a 100644 --- a/server/sonar-web/design-system/src/components/__tests__/SpotlightTour-test.tsx +++ b/server/sonar-web/design-system/src/components/__tests__/SpotlightTour-test.tsx @@ -28,42 +28,42 @@ it('should display the spotlight tour', async () => { renderSpotlightTour({ callback }); expect(await screen.findByRole('alertdialog')).toBeInTheDocument(); - expect(screen.getByRole('alertdialog')).toHaveTextContent( - 'Trust The FooFoo bar is bazstep 1 of 5next', - ); + let dialog = screen.getByRole('alertdialog'); + expect(dialog).toHaveTextContent('Trust The Foo'); + expect(dialog).toHaveTextContent('Foo bar is baz'); expect(screen.getByText('step 1 of 5')).toBeInTheDocument(); await user.click(screen.getByRole('button', { name: 'next' })); - expect(screen.getByRole('alertdialog')).toHaveTextContent( - 'Trust The BazBaz foo is barstep 2 of 5go_backnext', - ); + dialog = screen.getByRole('alertdialog'); + expect(dialog).toHaveTextContent('Trust The Baz'); + expect(dialog).toHaveTextContent('Baz foo is bar'); expect(callback).toHaveBeenCalled(); await user.click(screen.getByRole('button', { name: 'next' })); - expect(screen.getByRole('alertdialog')).toHaveTextContent( - 'Trust The BarBar baz is foostep 3 of 5go_backnext', - ); + dialog = screen.getByRole('alertdialog'); + expect(dialog).toHaveTextContent('Trust The Bar'); + expect(dialog).toHaveTextContent('Bar baz is foo'); await user.click(screen.getByRole('button', { name: 'next' })); - expect(screen.getByRole('alertdialog')).toHaveTextContent( - 'Trust The Foo 2Foo baz is barstep 4 of 5go_backnext', - ); + dialog = screen.getByRole('alertdialog'); + expect(dialog).toHaveTextContent('Trust The Foo 2'); + expect(dialog).toHaveTextContent('Foo baz is bar'); await user.click(screen.getByRole('button', { name: 'go_back' })); - expect(screen.getByRole('alertdialog')).toHaveTextContent( - 'Trust The BarBar baz is foostep 3 of 5go_backnext', - ); + dialog = screen.getByRole('alertdialog'); + expect(dialog).toHaveTextContent('Trust The Bar'); + expect(dialog).toHaveTextContent('Bar baz is foo'); await user.click(screen.getByRole('button', { name: 'next' })); await user.click(screen.getByRole('button', { name: 'next' })); - expect(screen.getByRole('alertdialog')).toHaveTextContent( - 'Trust The Baz 2Baz bar is foostep 5 of 5go_backclose', - ); + dialog = screen.getByRole('alertdialog'); + expect(dialog).toHaveTextContent('Trust The Baz 2'); + expect(dialog).toHaveTextContent('Baz bar is foo'); expect(screen.queryByRole('button', { name: 'next' })).not.toBeInTheDocument(); diff --git a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap index 6b27c088c74..1d7aad782e8 100644 --- a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap +++ b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap @@ -24,12 +24,7 @@ exports[`should highlight code content correctly 1`] = ` border: var(--border); color: var(--color); background-color: var(--background); - -webkit-transition: background-color 0.2s ease,outline 0.2s ease; - transition: background-color 0.2s ease,outline 0.2s ease; - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; + transition:background-color 0.2s ease,display: inline-flex; -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -64,9 +59,14 @@ exports[`should highlight code content correctly 1`] = ` } .emotion-4:focus, -.emotion-4:active { +.emotion-4:active, +.emotion-4:focus-visible { color: var(--color); - outline: 4px solid var(--focus); +} + +.emotion-4:focus-visible { + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); } .emotion-4:disabled, @@ -226,12 +226,7 @@ exports[`should show full size when multiline with no editing 1`] = ` border: var(--border); color: var(--color); background-color: var(--background); - -webkit-transition: background-color 0.2s ease,outline 0.2s ease; - transition: background-color 0.2s ease,outline 0.2s ease; - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; + transition:background-color 0.2s ease,display: inline-flex; -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -266,9 +261,14 @@ exports[`should show full size when multiline with no editing 1`] = ` } .emotion-4:focus, -.emotion-4:active { +.emotion-4:active, +.emotion-4:focus-visible { color: var(--color); - outline: 4px solid var(--focus); +} + +.emotion-4:focus-visible { + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); } .emotion-4:disabled, @@ -430,12 +430,7 @@ exports[`should show reduced size when single line with no editing 1`] = ` border: var(--border); color: var(--color); background-color: var(--background); - -webkit-transition: background-color 0.2s ease,outline 0.2s ease; - transition: background-color 0.2s ease,outline 0.2s ease; - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; + transition:background-color 0.2s ease,display: inline-flex; -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -472,9 +467,14 @@ exports[`should show reduced size when single line with no editing 1`] = ` } .emotion-4:focus, -.emotion-4:active { +.emotion-4:active, +.emotion-4:focus-visible { color: var(--color); - outline: 4px solid var(--focus); +} + +.emotion-4:focus-visible { + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); } .emotion-4:disabled, diff --git a/server/sonar-web/design-system/src/components/index.ts b/server/sonar-web/design-system/src/components/index.ts index da0bc81f8ef..8d7292db47b 100644 --- a/server/sonar-web/design-system/src/components/index.ts +++ b/server/sonar-web/design-system/src/components/index.ts @@ -42,7 +42,7 @@ export * from './CodeSyntaxHighlighter'; export * from './ColorsLegend'; export * from './CoverageIndicator'; export * from './DonutChart'; -export { ActionsDropdown, Dropdown } from './Dropdown'; +export { Dropdown } from './Dropdown'; export * from './DropdownMenu'; export { DropdownToggler } from './DropdownToggler'; export * from './DuplicationsIndicator'; diff --git a/server/sonar-web/design-system/src/components/input/InputField.tsx b/server/sonar-web/design-system/src/components/input/InputField.tsx index 2b6adb2bbff..5b3c9a04e5e 100644 --- a/server/sonar-web/design-system/src/components/input/InputField.tsx +++ b/server/sonar-web/design-system/src/components/input/InputField.tsx @@ -59,13 +59,13 @@ InputTextArea.displayName = 'InputTextArea'; const defaultStyle = (props: ThemedProps) => css` --border: ${themeBorder('default', 'inputBorder')(props)}; --focusBorder: ${themeBorder('default', 'inputFocus')(props)}; - --focusOutline: ${themeBorder('focus', 'inputFocus')(props)}; + --focusOutline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); `; const dangerStyle = (props: ThemedProps) => css` --border: ${themeBorder('default', 'inputDanger')(props)}; --focusBorder: ${themeBorder('default', 'inputDangerFocus')(props)}; - --focusOutline: ${themeBorder('focus', 'inputDangerFocus')(props)}; + --focusOutline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); `; const getInputVariant = (props: ThemedProps & { isInvalid?: boolean; isValid?: boolean }) => { @@ -102,6 +102,7 @@ const baseStyle = (props: ThemedProps) => css` &:focus-visible { border: var(--focusBorder); outline: var(--focusOutline); + outline-offset: var(--echoes-focus-border-offset-default); } &:disabled, diff --git a/server/sonar-web/design-system/src/components/input/InputSearch.tsx b/server/sonar-web/design-system/src/components/input/InputSearch.tsx index 9e9d5d76915..fd60b082ca5 100644 --- a/server/sonar-web/design-system/src/components/input/InputSearch.tsx +++ b/server/sonar-web/design-system/src/components/input/InputSearch.tsx @@ -226,7 +226,8 @@ export const StyledInputWrapper = styled.div` &:focus, &:active { border: ${themeBorder('default', 'inputFocus')}; - outline: ${themeBorder('focus', 'inputFocus')}; + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); } &::-webkit-search-decoration, @@ -251,6 +252,11 @@ export const StyledSearchIconWrapper = styled.div` export const StyledInteractiveIcon = styled(InteractiveIcon)` ${tw`sw-absolute`} ${tw`sw-right-2`} + + &:focus, + &:active { + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + } `; export const StyledNote = styled.span` diff --git a/server/sonar-web/design-system/src/components/input/SearchSelectDropdownControl.tsx b/server/sonar-web/design-system/src/components/input/SearchSelectDropdownControl.tsx index dbfcfb1854f..d5f40048bda 100644 --- a/server/sonar-web/design-system/src/components/input/SearchSelectDropdownControl.tsx +++ b/server/sonar-web/design-system/src/components/input/SearchSelectDropdownControl.tsx @@ -144,11 +144,11 @@ const StyledControl = styled.div` &:focus-visible, &:focus-within { border: ${themeBorder('default', 'inputFocus')}; - outline: ${themeBorder('focus', 'inputFocus')}; + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); &.is-discreet { ${tw`sw-rounded-1 sw-border-none`}; - outline: ${themeBorder('focus', 'discreetFocusBorder')}; + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); } } `; diff --git a/server/sonar-web/design-system/src/sonar-aligned/components/ToggleButton.tsx b/server/sonar-web/design-system/src/sonar-aligned/components/ToggleButton.tsx index 9c882e50fbc..1c83381b8d7 100644 --- a/server/sonar-web/design-system/src/sonar-aligned/components/ToggleButton.tsx +++ b/server/sonar-web/design-system/src/sonar-aligned/components/ToggleButton.tsx @@ -113,9 +113,9 @@ const OptionButton = styled(ButtonSecondary)<{ selected: boolean }>` color: ${themeContrast('toggleHover')}; } - &:focus, - &:active { - outline: ${themeBorder('focus', 'toggleFocus')}; + &:focus-visible { + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); z-index: 1; } `; diff --git a/server/sonar-web/design-system/src/sonar-aligned/components/buttons/Button.tsx b/server/sonar-web/design-system/src/sonar-aligned/components/buttons/Button.tsx index 8c3a171086a..ee91b9109d5 100644 --- a/server/sonar-web/design-system/src/sonar-aligned/components/buttons/Button.tsx +++ b/server/sonar-web/design-system/src/sonar-aligned/components/buttons/Button.tsx @@ -128,7 +128,6 @@ export const buttonStyle = (props: ThemedProps) => css` background-color: var(--background); transition: background-color 0.2s ease, - outline 0.2s ease; ${tw`sw-inline-flex sw-items-center`} ${tw`sw-h-control`} @@ -143,9 +142,14 @@ export const buttonStyle = (props: ThemedProps) => css` } &:focus, - &:active { + &:active, + &:focus-visible { color: var(--color); - outline: ${themeBorder('focus', 'var(--focus)')(props)}; + } + + &:focus-visible { + outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default); + outline-offset: var(--echoes-focus-border-offset-default); } &:disabled, diff --git a/server/sonar-web/design-system/src/sonar-aligned/components/input/SelectCommon.tsx b/server/sonar-web/design-system/src/sonar-aligned/components/input/SelectCommon.tsx index 8e89171aeb7..a5c8b75b996 100644 --- a/server/sonar-web/design-system/src/sonar-aligned/components/input/SelectCommon.tsx +++ b/server/sonar-web/design-system/src/sonar-aligned/components/input/SelectCommon.tsx @@ -147,7 +147,11 @@ export function selectStyle< cursor: 'pointer', background: themeColor('inputBackground')({ theme }), transition: 'border 0.2s ease, outline 0.2s ease', - outline: isFocused && !menuIsOpen ? themeBorder('focus', 'inputFocus')({ theme }) : 'none', + outline: + isFocused && !menuIsOpen + ? 'var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default)' + : 'none', + borderRadius: '4px', ...(isDisabled && { color: themeContrast('inputDisabled')({ theme }), background: themeColor('inputDisabled')({ theme }), @@ -164,9 +168,11 @@ export function selectStyle< }), option: (base, { isFocused, isSelected }) => ({ ...base, + borderLeft: '2px solid transparent', ...((isSelected || isFocused) && { background: themeColor('selectOptionSelected')({ theme }), color: themeContrast('primaryLight')({ theme }), + borderLeftColor: 'var(--echoes-color-focus-default)', }), }), singleValue: (base) => ({ |