mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 03:59:01 +08:00
feat: focus outline (#37483)
* feat: focus outline * feat: add tree focus
This commit is contained in:
parent
7e2eeb3b6e
commit
8e328d0ae2
@ -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`]: {
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
@ -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',
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -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': {
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
},
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
[tabindex='-1']:focus {
|
||||
outline: none !important;
|
||||
outline: none;
|
||||
}
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -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`]: {
|
||||
|
@ -25,6 +25,7 @@ const genTypographyStyle: GenerateStyle<TypographyToken> = token => {
|
||||
[componentCls]: {
|
||||
color: token.colorText,
|
||||
overflowWrap: 'break-word',
|
||||
lineHeight: token.lineHeight,
|
||||
'&&-secondary': {
|
||||
color: token.colorTextDescription,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user