feat: focus outline (#37483)

* feat: focus outline

* feat: add tree focus
This commit is contained in:
MadCcc 2022-09-09 10:53:03 +08:00 committed by GitHub
parent 7e2eeb3b6e
commit 8e328d0ae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 65 additions and 77 deletions

View File

@ -1,7 +1,7 @@
import type { CSSObject } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusStyle, resetComponent } from '../../style';
interface BreadcrumbToken extends FullToken<'Breadcrumb'> {
breadcrumbBaseColor: string;
@ -48,6 +48,8 @@ const genBreadcrumbStyle: GenerateStyle<BreadcrumbToken, CSSObject> = token => {
color: token.breadcrumbLinkColorHover,
backgroundColor: token.colorBgTextHover,
},
...genFocusStyle(token),
},
[`li:last-child > ${componentCls}-separator`]: {

View File

@ -2,6 +2,7 @@ import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import genGroupStyle from './group';
import { genFocusStyle } from '../../style';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {}
@ -45,13 +46,17 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
[`&${componentCls}-block`]: {
width: '100%',
},
'&:not(:disabled)': {
...genFocusStyle(token),
},
},
};
};
const genHoverActiveButtonStyle = (hoverStyle: CSSObject, activeStyle: CSSObject): CSSObject => ({
'&:not(:disabled)': {
'&:hover, &:focus': hoverStyle,
'&:hover': hoverStyle,
'&:active': activeStyle,
},
});

View File

@ -1,7 +1,7 @@
import { Keyframes } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusOutline, resetComponent } from '../../style';
export interface ComponentToken {}
@ -87,6 +87,10 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = token => {
height: '100%',
cursor: 'pointer',
opacity: 0,
[`&:focus-visible + ${checkboxCls}-inner`]: {
...genFocusOutline(token),
},
},
// Wrapper > Checkbox > inner
@ -164,8 +168,7 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = token => {
${wrapperCls}:not(${wrapperCls}-disabled),
${checkboxCls}:not(${checkboxCls}-disabled)
`]: {
[`&:hover ${checkboxCls}-inner,
${checkboxCls}-input:focus + ${checkboxCls}-inner`]: {
[`&:hover ${checkboxCls}-inner`]: {
borderColor: token.colorPrimary,
},
},
@ -207,8 +210,7 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = token => {
${wrapperCls}-checked:not(${wrapperCls}-disabled),
${checkboxCls}-checked:not(${checkboxCls}-disabled)
`]: {
[`&:hover ${checkboxCls}-inner,
${checkboxCls}-input:focus + ${checkboxCls}-inner`]: {
[`&:hover ${checkboxCls}-inner`]: {
backgroundColor: token.colorPrimaryHover,
borderColor: 'transparent',
},

View File

@ -10,7 +10,7 @@ import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import genButtonStyle from './button';
import genStatusStyle from './status';
import { resetComponent, roundedArrow } from '../../style';
import { genFocusStyle, resetComponent, roundedArrow } from '../../style';
export interface ComponentToken {
zIndexPopup: number;
@ -267,6 +267,7 @@ const genBaseStyle: GenerateStyle<DropdownToken> = token => {
borderRadius: token.controlRadiusLG,
outline: 'none',
boxShadow: token.boxShadowSecondary,
...genFocusStyle(token),
[`${menuCls}-item-group-title`]: {
padding: `${dropdownPaddingVertical}px ${controlPaddingHorizontal}px`,
@ -334,6 +335,8 @@ const genBaseStyle: GenerateStyle<DropdownToken> = token => {
backgroundColor: token.controlItemBgHover,
},
...genFocusStyle(token),
'&-selected': {
color: token.colorPrimary,
backgroundColor: token.controlItemBgActive,

View File

@ -1,13 +1,10 @@
import type { CSSInterpolation } from '@ant-design/cssinjs';
import { genFocusOutline } from '../../style';
import type { MenuToken } from '.';
const accessibilityFocus = (token: MenuToken) => {
const { controlOutlineWidth, colorPrimaryHover } = token;
return {
boxShadow: `0 0 0 ${controlOutlineWidth}px ${colorPrimaryHover}`,
};
};
const accessibilityFocus = (token: MenuToken) => ({
...genFocusOutline(token),
});
const getThemeStyle = (token: MenuToken): CSSInterpolation => {
const {

View File

@ -7,7 +7,7 @@ import {
} from '../../input/style';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
interface PaginationToken extends InputToken<FullToken<'Pagination'>> {
paginationItemSize: number;
@ -209,7 +209,7 @@ const genPaginationSimpleStyle: GenerateStyle<PaginationToken, CSSObject> = toke
border: `${token.controlLineWidth}px ${token.controlLineType} ${token.colorBorder}`,
borderRadius: token.radiusBase,
outline: 'none',
transition: `border-color ${token.motionDurationSlow}`,
transition: `border-color ${token.motionDurationFast}`,
'&:hover': {
borderColor: token.colorPrimary,
@ -217,7 +217,7 @@ const genPaginationSimpleStyle: GenerateStyle<PaginationToken, CSSObject> = toke
'&:focus': {
borderColor: token.colorPrimaryHover,
boxShadow: `${token.inputOutlineOffset} 0 ${token.controlOutlineWidth} ${token.controlOutline}`,
boxShadow: `${token.inputOutlineOffset}px 0 ${token.controlOutlineWidth}px ${token.controlOutline}`,
},
'&[disabled]': {
@ -290,6 +290,7 @@ const genPaginationJumpStyle: GenerateStyle<PaginationToken, CSSObject> = token
[`${componentCls}-item-ellipsis`]: {
opacity: 0,
},
...genFocusOutline(token),
},
},
@ -318,7 +319,7 @@ const genPaginationJumpStyle: GenerateStyle<PaginationToken, CSSObject> = token
listStyle: 'none',
borderRadius: token.radiusBase,
cursor: 'pointer',
transition: `all ${token.motionDurationSlow}`,
transition: `all ${token.motionDurationFast}`,
},
[`${componentCls}-prev, ${componentCls}-next`]: {
@ -342,12 +343,11 @@ const genPaginationJumpStyle: GenerateStyle<PaginationToken, CSSObject> = token
border: `${token.controlLineWidth}px ${token.controlLineType} transparent`,
borderRadius: token.radiusBase,
outline: 'none',
transition: `all ${token.motionDurationSlow}`,
transition: `border ${token.motionDurationFast}`,
},
[`&:focus-visible ${componentCls}-item-link`]: {
color: token.colorPrimary,
borderColor: token.colorPrimary,
...genFocusOutline(token),
},
[`&:hover ${componentCls}-item-link`]: {
@ -424,7 +424,7 @@ const genPaginationItemStyle: GenerateStyle<PaginationToken, CSSObject> = token
},
'&:hover': {
transition: `all ${token.motionDurationSlow}`,
transition: `all ${token.motionDurationFast}`,
a: {
color: token.colorPrimary,
@ -433,14 +433,7 @@ const genPaginationItemStyle: GenerateStyle<PaginationToken, CSSObject> = token
// cannot merge with `&:hover`
// see https://github.com/ant-design/ant-design/pull/34002
'&:focus-visible': {
borderColor: token.colorPrimary,
transition: `all ${token.motionDurationSlow}`,
a: {
color: token.colorPrimary,
},
},
...genFocusStyle(token),
'&-active': {
fontWeight: token.paginationFontWeightActive,
@ -455,17 +448,9 @@ const genPaginationItemStyle: GenerateStyle<PaginationToken, CSSObject> = token
borderColor: token.colorPrimaryHover,
},
'&:focus-visible': {
borderColor: token.colorPrimaryHover,
},
'&:hover a': {
color: token.colorPrimaryHover,
},
'&:focus-visible a': {
color: token.colorPrimaryHover,
},
},
},
};

View File

@ -1,7 +1,7 @@
import { Keyframes } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusOutline, resetComponent } from '../../style';
// ============================== Tokens ==============================
export interface ComponentToken {}
@ -69,7 +69,6 @@ const getRadioBasicStyle: GenerateStyle<RadioToken> = token => {
radioWrapperMarginRight,
radioDotColor,
radioTop,
radioFocusShadow,
radioSize,
motionDurationSlow,
motionDurationFast,
@ -141,13 +140,12 @@ const getRadioBasicStyle: GenerateStyle<RadioToken> = token => {
},
[`${componentCls}-wrapper:hover &,
&:hover ${radioInnerPrefixCls},
&-input:focus + ${radioInnerPrefixCls}`]: {
&:hover ${radioInnerPrefixCls}`]: {
borderColor: radioDotColor,
},
[`${componentCls}-input:focus + ${radioInnerPrefixCls}`]: {
boxShadow: radioFocusShadow,
[`${componentCls}-input:focus-visible + ${radioInnerPrefixCls}`]: {
...genFocusOutline(token),
},
[`${componentCls}:hover::after, ${componentCls}-wrapper:hover &::after`]: {
@ -274,7 +272,6 @@ const getRadioButtonStyle: GenerateStyle<RadioToken> = token => {
controlRadiusSM,
controlRadiusLG,
radioDotColor,
radioButtonFocusShadow,
radioButtonCheckedBg,
radioButtonHoverColor,
radioButtonActiveColor,
@ -393,8 +390,8 @@ const getRadioButtonStyle: GenerateStyle<RadioToken> = token => {
color: radioDotColor,
},
'&:focus-within': {
boxShadow: radioButtonFocusShadow,
'&:has(:focus-visible)': {
...genFocusOutline(token),
},
[`${componentCls}-inner, input[type='checkbox'], input[type='radio']`]: {
@ -435,10 +432,6 @@ const getRadioButtonStyle: GenerateStyle<RadioToken> = token => {
backgroundColor: radioButtonActiveColor,
},
},
'&:focus-within': {
boxShadow: radioButtonFocusShadow,
},
},
[`${componentCls}-group-solid &-checked:not(&-disabled)`]: {
@ -457,10 +450,6 @@ const getRadioButtonStyle: GenerateStyle<RadioToken> = token => {
background: radioButtonActiveColor,
borderColor: radioButtonActiveColor,
},
'&:focus-within': {
boxShadow: radioButtonFocusShadow,
},
},
'&-disabled': {

View File

@ -86,7 +86,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = token => {
zIndex: 1,
},
'&:hover, &:active, &:focus': {
'&:hover, &:active, &:focus-visible': {
boxShadow: `none`,
outlineWidth: token.handleLineWidthHover,
outlineColor: token.colorPrimary,

View File

@ -95,3 +95,15 @@ export const genLinkStyle = (token: DerivativeToken): CSSObject => ({
},
},
});
export const genFocusOutline = (token: DerivativeToken): CSSObject => ({
outline: `${token.lineWidth * 4}px solid ${token.colorPrimaryBorder}`,
outlineOffset: 1,
transition: 'outline-offset 0s, outline 0s',
});
export const genFocusStyle = (token: DerivativeToken): CSSObject => ({
'&:focus-visible': {
...genFocusOutline(token),
},
});

View File

@ -28,7 +28,7 @@ body {
margin: 0;
}
[tabindex='-1']:focus {
outline: none !important;
outline: none;
}
hr {
box-sizing: content-box;

View File

@ -2,7 +2,7 @@ import type { CSSObject } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusStyle, resetComponent } from '../../style';
interface SwitchToken extends FullToken<'Switch'> {
switchMinWidth: number;
@ -163,18 +163,7 @@ const genSwitchStyle = (token: SwitchToken): CSSObject => {
background: token.colorTextTertiary,
},
'&:focus-visible': {
outline: 0,
boxShadow: `0 0 0 ${token.controlOutlineWidth}px ${token.controlTmpOutline}`,
},
[`&${token.componentCls}-checked:focus-visible`]: {
boxShadow: `0 0 0 ${token.controlOutlineWidth}px ${token.controlOutline}`,
},
'&:focus:hover': {
boxShadow: 'none',
},
...genFocusStyle(token),
[`&${token.componentCls}-checked`]: {
background: token.switchColor,

View File

@ -1,7 +1,7 @@
import type { CSSObject } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusStyle, resetComponent } from '../../style';
import genMotionStyle from './motion';
export interface ComponentToken {
@ -542,9 +542,10 @@ const genTabStyle: GenerateStyle<TabsToken, CSSObject> = (token: TabsToken) => {
outline: 'none',
cursor: 'pointer',
'&-btn, &-remove': {
'&:focus, &:active': {
'&:focus:not(:focus-visible), &:active': {
color: tabsActiveColor,
},
...genFocusStyle(token),
},
'&-btn': {
outline: 'none',
@ -809,9 +810,11 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
color: tabsHoverColor,
},
'&:active, &:focus': {
'&:active, &:focus:not(:focus-visible)': {
color: tabsActiveColor,
},
...genFocusStyle(token),
},
},

View File

@ -4,7 +4,7 @@ import { genCollapseMotion } from '../../style/motion';
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
import type { DerivativeToken } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent } from '../../style';
import { genFocusOutline, resetComponent } from '../../style';
// ============================ Keyframes =============================
const treeNodeFX = new Keyframes('ant-tree-node-fx-do-not-use', {
@ -89,7 +89,7 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
},
'&-focused:not(:hover):not(&-active-focused)': {
background: token.controlOutline,
...genFocusOutline(token),
},
// =================== Virtual List ===================
@ -153,7 +153,7 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
},
[`&-active ${treeCls}-node-content-wrapper`]: {
background: token.controlItemBgHover,
...genFocusOutline(token),
},
[`&:not(&-disabled).filter-node ${treeCls}-title`]: {

View File

@ -25,6 +25,7 @@ const genTypographyStyle: GenerateStyle<TypographyToken> = token => {
[componentCls]: {
color: token.colorText,
overflowWrap: 'break-word',
lineHeight: token.lineHeight,
'&&-secondary': {
color: token.colorTextDescription,
},