refactor(cssinjs): simplify transition style generation (#56826)

* chore: code optimization

* update
This commit is contained in:
lijianan
2026-02-04 14:54:08 +08:00
committed by GitHub
parent 45d8f9a2d4
commit 1a789f1e5b
24 changed files with 138 additions and 97 deletions

View File

@@ -8,7 +8,14 @@ export interface ComponentToken {}
export interface WaveToken extends FullToken<'Wave'> {}
const genWaveStyle: GenerateStyle<WaveToken> = (token) => {
const { componentCls, colorPrimary, antCls } = token;
const {
componentCls,
colorPrimary,
motionDurationSlow,
motionEaseInOut,
motionEaseOutCirc,
antCls,
} = token;
const [, varRef] = genCssVar(antCls, 'wave');
return {
[componentCls]: {
@@ -22,20 +29,18 @@ const genWaveStyle: GenerateStyle<WaveToken> = (token) => {
// =================== Motion ===================
'&.wave-motion-appear': {
transition: [
`box-shadow 0.4s ${token.motionEaseOutCirc}`,
`opacity 2s ${token.motionEaseOutCirc}`,
].join(','),
transition: [`box-shadow 0.4s`, `opacity 2s`]
.map((prop) => `${prop} ${motionEaseOutCirc}`)
.join(','),
'&-active': {
boxShadow: `0 0 0 6px currentcolor`,
opacity: 0,
},
'&.wave-quick': {
transition: [
`box-shadow ${token.motionDurationSlow} ${token.motionEaseInOut}`,
`opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
].join(','),
transition: [`box-shadow`, `opacity`]
.map((prop) => `${prop} ${motionDurationSlow} ${motionEaseInOut}`)
.join(','),
},
},
},

View File

@@ -98,9 +98,9 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
[`&${componentCls}-motion-leave`]: {
overflow: 'hidden',
opacity: 1,
transition: `max-height ${duration} ${motionEaseInOutCirc}, opacity ${duration} ${motionEaseInOutCirc},
padding-top ${duration} ${motionEaseInOutCirc}, padding-bottom ${duration} ${motionEaseInOutCirc},
margin-bottom ${duration} ${motionEaseInOutCirc}`,
transition: [`max-height`, `opacity`, `padding-top`, `padding-bottom`, `margin-bottom`]
.map((prop) => `${prop} ${duration} ${motionEaseInOutCirc}`)
.join(', '),
},
[`&${componentCls}-motion-leave-active`]: {

View File

@@ -170,7 +170,9 @@ const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token) => {
[`${componentCls}-ink`]: {
position: 'absolute',
bottom: 0,
transition: `left ${motionDurationSlow} ease-in-out, width ${motionDurationSlow} ease-in-out`,
transition: [`left`, `width`]
.map((prop) => `${prop} ${motionDurationSlow} ease-in-out`)
.join(', '),
height: lineWidthBold,
backgroundColor: colorPrimary,
},

View File

@@ -315,6 +315,7 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
boxShadowTertiary,
bodyPadding,
extraColor,
motionDurationMid,
} = token;
return {
@@ -379,7 +380,9 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
[`${componentCls}-hoverable`]: {
cursor: 'pointer',
transition: `box-shadow ${token.motionDurationMid}, border-color ${token.motionDurationMid}`,
transition: [`box-shadow`, `border-color`]
.map((prop) => `${prop} ${motionDurationMid}`)
.join(', '),
'&:hover': {
borderColor: 'transparent',

View File

@@ -101,7 +101,9 @@ const genPickerStyle: GenerateStyle<PickerToken> = (token) => {
alignItems: 'center',
lineHeight: 1,
borderRadius,
transition: `border ${motionDurationMid}, box-shadow ${motionDurationMid}, background ${motionDurationMid}`,
transition: [`border`, `box-shadow`, `background-color`]
.map((prop) => `${prop} ${motionDurationMid}`)
.join(', '),
[`${componentCls}-prefix`]: {
flex: '0 0 auto',

View File

@@ -116,7 +116,9 @@ export const genOverflowStyle = (
marginBlock: INTERNAL_FIXED_ITEM_MARGIN,
borderRadius: borderRadiusSM,
cursor: 'default',
transition: `font-size ${motionDurationSlow}, line-height ${motionDurationSlow}, height ${motionDurationSlow}`,
transition: [`font-size`, `line-height`, `height`]
.map((prop) => `${prop} ${motionDurationSlow}`)
.join(', '),
marginInlineEnd: token.calc(INTERNAL_FIXED_ITEM_MARGIN).mul(2).equal(),
paddingInlineStart: paddingXS,
paddingInlineEnd: token.calc(paddingXS).div(2).equal(),

View File

@@ -2,7 +2,7 @@ import type { FormToken } from '.';
import type { GenerateStyle } from '../../theme/internal';
const genFormValidateMotionStyle: GenerateStyle<FormToken> = (token) => {
const { componentCls } = token;
const { componentCls, motionDurationFast, motionEaseInOut } = token;
const helpCls = `${componentCls}-show-help`;
const helpItemCls = `${componentCls}-show-help-item`;
@@ -10,7 +10,7 @@ const genFormValidateMotionStyle: GenerateStyle<FormToken> = (token) => {
return {
[helpCls]: {
// Explain holder
transition: `opacity ${token.motionDurationFast} ${token.motionEaseInOut}`,
transition: `opacity ${motionDurationFast} ${motionEaseInOut}`,
'&-appear, &-enter': {
opacity: 0,
@@ -31,9 +31,9 @@ const genFormValidateMotionStyle: GenerateStyle<FormToken> = (token) => {
// Explain
[helpItemCls]: {
overflow: 'hidden',
transition: `height ${token.motionDurationFast} ${token.motionEaseInOut},
opacity ${token.motionDurationFast} ${token.motionEaseInOut},
transform ${token.motionDurationFast} ${token.motionEaseInOut} !important`,
transition: `${['height', 'opacity', 'transform']
.map((prop) => `${prop} ${motionDurationFast} ${motionEaseInOut}`)
.join(', ')} !important`,
[`&${helpItemCls}-appear, &${helpItemCls}-enter`]: {
transform: `translateY(-5px)`,

View File

@@ -43,10 +43,9 @@ const getHorizontalStyle: GenerateStyle<MenuToken> = (token) => {
},
[`${componentCls}-item, ${componentCls}-submenu-title`]: {
transition: [
`border-color ${motionDurationSlow}`,
`background-color ${motionDurationSlow}`,
].join(','),
transition: [`border-color`, `background-color`]
.map((prop) => `${prop} ${motionDurationSlow}`)
.join(','),
},
// ===================== Sub Menu =====================

View File

@@ -516,12 +516,9 @@ const genSubMenuArrowStyle = (token: MenuToken): CSSObject => {
height: token.calc(menuArrowSize).mul(0.15).equal(),
backgroundColor: 'currentcolor',
borderRadius,
transition: [
`background-color ${motionDurationSlow} ${motionEaseInOut}`,
`transform ${motionDurationSlow} ${motionEaseInOut}`,
`top ${motionDurationSlow} ${motionEaseInOut}`,
`color ${motionDurationSlow} ${motionEaseInOut}`,
].join(','),
transition: [`background-color`, `transform`, `top`, `color`]
.map((prop) => `${prop} ${motionDurationSlow} ${motionEaseInOut}`)
.join(','),
content: '""',
},
@@ -619,10 +616,9 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
},
[`&-horizontal ${componentCls}-submenu`]: {
transition: [
`border-color ${motionDurationSlow} ${motionEaseInOut}`,
`background-color ${motionDurationSlow} ${motionEaseInOut}`,
].join(','),
transition: [`border-color`, `background-color`]
.map((prop) => `${prop} ${motionDurationSlow} ${motionEaseInOut}`)
.join(','),
},
[`${componentCls}-submenu, ${componentCls}-submenu-inline`]: {
@@ -635,10 +631,9 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
[`${componentCls}-submenu ${componentCls}-sub`]: {
cursor: 'initial',
transition: [
`background-color ${motionDurationSlow} ${motionEaseInOut}`,
`padding ${motionDurationSlow} ${motionEaseInOut}`,
].join(','),
transition: [`background-color`, `padding`]
.map((prop) => `${prop} ${motionDurationSlow} ${motionEaseInOut}`)
.join(','),
},
[`${componentCls}-title-content`]: {

View File

@@ -239,10 +239,9 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
borderInlineEnd: `${unit(activeBarWidth)} solid ${itemSelectedColor}`,
transform: 'scaleY(0.0001)',
opacity: 0,
transition: [
`transform ${motionDurationMid} ${motionEaseOut}`,
`opacity ${motionDurationMid} ${motionEaseOut}`,
].join(','),
transition: [`transform`, `opacity`]
.map((prop) => `${prop} ${motionDurationMid} ${motionEaseOut}`)
.join(','),
content: '""',
},
@@ -258,10 +257,9 @@ const getThemeStyle = (token: MenuToken, themeSuffix: string): CSSInterpolation
'&::after': {
transform: 'scaleY(1)',
opacity: 1,
transition: [
`transform ${motionDurationMid} ${motionEaseInOut}`,
`opacity ${motionDurationMid} ${motionEaseInOut}`,
].join(','),
transition: [`transform`, `opacity`]
.map((prop) => `${prop} ${motionDurationMid} ${motionEaseInOut}`)
.join(','),
},
},
},

View File

@@ -184,7 +184,7 @@ export const genModalMaskStyle: GenerateStyle<TokenWithCommonCls<AliasToken>> =
};
const genModalStyle: GenerateStyle<ModalToken> = (token) => {
const { componentCls } = token;
const { componentCls, motionDurationMid } = token;
return [
// ======================== Root =========================
@@ -284,8 +284,9 @@ const genModalStyle: GenerateStyle<ModalToken> = (token) => {
border: 0,
outline: 0,
cursor: 'pointer',
transition: `color ${token.motionDurationMid}, background-color ${token.motionDurationMid}`,
transition: ['color', 'background-color']
.map((prop) => `${prop} ${motionDurationMid}`)
.join(', '),
'&-x': {
display: 'flex',
fontSize: token.fontSizeLG,

View File

@@ -136,6 +136,7 @@ export const genNoticeStyle = (token: NotificationToken): CSSObject => {
colorErrorBg,
colorInfoBg,
colorWarningBg,
motionDurationMid,
} = token;
const noticeCls = `${componentCls}-notice`;
@@ -223,7 +224,10 @@ export const genNoticeStyle = (token: NotificationToken): CSSObject => {
width: token.notificationCloseButtonSize,
height: token.notificationCloseButtonSize,
borderRadius: token.borderRadiusSM,
transition: `background-color ${token.motionDurationMid}, color ${token.motionDurationMid}`,
transition: ['color', 'background-color']
.map((prop) => `${prop} ${motionDurationMid}`)
.join(', '),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',

View File

@@ -388,11 +388,9 @@ const getRadioButtonStyle: GenerateStyle<RadioToken> = (token) => {
borderBlockStartWidth: calc(lineWidth).add(0.02).equal(),
borderInlineEndWidth: lineWidth,
cursor: 'pointer',
transition: [
`color ${motionDurationMid}`,
`background-color ${motionDurationMid}`,
`box-shadow ${motionDurationMid}`,
].join(','),
transition: [`color`, `background-color`, `box-shadow`]
.map((prop) => `${prop} ${motionDurationMid}`)
.join(','),
a: {
color: buttonColor,

View File

@@ -78,7 +78,7 @@ const segmentedTextEllipsisCss: CSSObject = {
// ============================== Styles ==============================
const genSegmentedStyle: GenerateStyle<SegmentedToken> = (token: SegmentedToken) => {
const { componentCls } = token;
const { componentCls, motionDurationSlow, motionEaseInOut } = token;
const labelHeight = token
.calc(token.controlHeight)
@@ -262,8 +262,10 @@ const genSegmentedStyle: GenerateStyle<SegmentedToken> = (token: SegmentedToken)
// transition effect when `appear-active`
[`${componentCls}-thumb-motion-appear-active`]: {
transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOut}, width ${token.motionDurationSlow} ${token.motionEaseInOut}`,
willChange: 'transform, width',
transition: [`transform`, `width`]
.map((prop) => `${prop} ${motionDurationSlow} ${motionEaseInOut}`)
.join(', '),
},
[`&${componentCls}-shape-round`]: {

View File

@@ -13,7 +13,7 @@ export type { ComponentToken };
// =============================== Base ===============================
const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
const { antCls, componentCls, inputPaddingHorizontalBase } = token;
const { antCls, componentCls, motionDurationMid, inputPaddingHorizontalBase } = token;
const hoverShowClearStyle: CSSObject = {
[`${componentCls}-clear`]: {
@@ -66,7 +66,9 @@ const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
textTransform: 'none',
cursor: 'pointer',
opacity: 0,
transition: `color ${token.motionDurationMid} ease, opacity ${token.motionDurationSlow} ease`,
transition: ['color', 'opacity']
.map((prop) => `${prop} ${motionDurationMid} ease`)
.join(', '),
textRendering: 'auto',
// https://github.com/ant-design/ant-design/issues/54205
// Force GPU compositing on Safari to prevent flickering on opacity/transform transitions

View File

@@ -41,7 +41,7 @@ const antRotate = new Keyframes('antRotate', {
});
const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject => {
const { componentCls, calc } = token;
const { componentCls, motionDurationSlow, calc } = token;
return {
[componentCls]: {
...resetComponent(token),
@@ -52,7 +52,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
textAlign: 'center',
verticalAlign: 'middle',
opacity: 0,
transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOutCirc}`,
transition: `transform ${motionDurationSlow} ${token.motionEaseInOutCirc}`,
'&-spinning': {
position: 'relative',
@@ -148,7 +148,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
[`${componentCls}-container`]: {
position: 'relative',
transition: `opacity ${token.motionDurationSlow}`,
transition: `opacity ${motionDurationSlow}`,
'&::after': {
position: 'absolute',
@@ -161,7 +161,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
height: '100%',
background: token.colorBgContainer,
opacity: 0,
transition: `all ${token.motionDurationSlow}`,
transition: `all ${motionDurationSlow}`,
content: '""',
pointerEvents: 'none',
},
@@ -193,7 +193,9 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
height: '1em',
fontSize: token.dotSize,
display: 'inline-block',
transition: `transform ${token.motionDurationSlow} ease, opacity ${token.motionDurationSlow} ease`,
transition: [`transform`, `opacity`]
.map((prop) => `${prop} ${motionDurationSlow} ease`)
.join(', '),
transformOrigin: '50% 50%',
lineHeight: 1,
color: token.colorPrimary,
@@ -272,7 +274,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
'&-circle': {
strokeLinecap: 'round',
transition: ['stroke-dashoffset', 'stroke-dasharray', 'stroke', 'stroke-width', 'opacity']
.map((item) => `${item} ${token.motionDurationSlow} ease`)
.map((item) => `${item} ${motionDurationSlow} ease`)
.join(','),
fillOpacity: 0,
stroke: 'currentcolor',

View File

@@ -135,7 +135,7 @@ export const genCommonStyle = (
export const genFocusOutline = (token: AliasToken, offset?: number): CSSObject => ({
outline: `${unit(token.lineWidthFocus)} solid ${token.colorPrimaryBorder}`,
outlineOffset: offset ?? 1,
transition: 'outline-offset 0s, outline 0s',
transition: [`outline-offset`, `outline`].map((prop) => `${prop} 0s`).join(', '),
});
export const genFocusStyle = (token: AliasToken, offset?: number): CSSObject => ({

View File

@@ -1,23 +1,26 @@
import type { AliasToken, GenerateStyle, TokenWithCommonCls } from '../../theme/internal';
const genCollapseMotion: GenerateStyle<TokenWithCommonCls<AliasToken>> = (token) => ({
[token.componentCls]: {
// For common/openAnimation
[`${token.antCls}-motion-collapse-legacy`]: {
overflow: 'hidden',
'&-active': {
transition: `height ${token.motionDurationMid} ${token.motionEaseInOut},
opacity ${token.motionDurationMid} ${token.motionEaseInOut} !important`,
const genCollapseMotion: GenerateStyle<TokenWithCommonCls<AliasToken>> = (token) => {
const { componentCls, antCls, motionDurationMid, motionEaseInOut } = token;
return {
[componentCls]: {
// For common/openAnimation
[`${antCls}-motion-collapse-legacy`]: {
overflow: 'hidden',
'&-active': {
transition: `${['height', 'opacity']
.map((prop) => `${prop} ${motionDurationMid} ${motionEaseInOut}`)
.join(', ')} !important`,
},
},
[`${antCls}-motion-collapse`]: {
overflow: 'hidden',
transition: `${['height', 'opacity']
.map((prop) => `${prop} ${motionDurationMid} ${motionEaseInOut}`)
.join(', ')} !important`,
},
},
[`${token.antCls}-motion-collapse`]: {
overflow: 'hidden',
transition: `height ${token.motionDurationMid} ${token.motionEaseInOut},
opacity ${token.motionDurationMid} ${token.motionEaseInOut} !important`,
},
},
});
};
};
export default genCollapseMotion;

View File

@@ -250,6 +250,7 @@ const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = (token) => {
innerMinMargin,
innerMaxMargin,
handleSize,
switchDuration,
calc,
} = token;
const switchInnerCls = `${componentCls}-inner`;
@@ -266,15 +267,19 @@ const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = (token) => {
height: '100%',
paddingInlineStart: innerMaxMargin,
paddingInlineEnd: innerMinMargin,
transition: `padding-inline-start ${token.switchDuration} ease-in-out, padding-inline-end ${token.switchDuration} ease-in-out`,
transition: [`padding-inline-start`, `padding-inline-end`]
.map((prop) => `${prop} ${switchDuration} ease-in-out`)
.join(', '),
[`${switchInnerCls}-checked, ${switchInnerCls}-unchecked`]: {
display: 'block',
color: token.colorTextLightSolid,
fontSize: token.fontSizeSM,
transition: `margin-inline-start ${token.switchDuration} ease-in-out, margin-inline-end ${token.switchDuration} ease-in-out`,
pointerEvents: 'none',
minHeight: trackHeight,
transition: [`margin-inline-start`, `margin-inline-end`]
.map((prop) => `${prop} ${switchDuration} ease-in-out`)
.join(', '),
},
[`${switchInnerCls}-checked`]: {

View File

@@ -345,8 +345,10 @@ const genTableStyle: GenerateStyle<TableToken, CSSObject> = (token) => {
[`${componentCls}-tbody`]: {
'> tr': {
'> th, > td': {
transition: `background-color ${motionDurationMid}, border-color ${motionDurationMid}`,
borderBottom: tableBorder,
transition: [`background-color`, `border-color`]
.map((prop) => `${prop} ${motionDurationMid}`)
.join(', '),
// ========================= Nest Table ===========================
[`

View File

@@ -369,6 +369,7 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
horizontalMargin,
verticalItemPadding,
verticalItemMargin,
motionDurationSlow,
calc,
} = token;
return {
@@ -397,8 +398,9 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
height: token.lineWidthBold,
'&-animated': {
transition: `width ${token.motionDurationSlow}, left ${token.motionDurationSlow},
right ${token.motionDurationSlow}`,
transition: ['width', 'left', 'right']
.map((prop) => `${prop} ${motionDurationSlow}`)
.join(', '),
},
},
@@ -524,7 +526,7 @@ const genPositionStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject
width: token.lineWidthBold,
'&-animated': {
transition: `height ${token.motionDurationSlow}, top ${token.motionDurationSlow}`,
transition: ['height', 'top'].map((prop) => `${prop} ${motionDurationSlow}`).join(', '),
},
},

View File

@@ -67,6 +67,7 @@ const genBaseStyle: GenerateStyle<TourToken> = (token) => {
motionDurationSlow,
antCls,
primaryPrevBtnBg,
motionDurationMid,
} = token;
const [varName, varRef] = genCssVar(antCls, 'tooltip');
@@ -117,7 +118,11 @@ const genBaseStyle: GenerateStyle<TourToken> = (token) => {
width: closeBtnSize,
height: closeBtnSize,
borderRadius: token.borderRadiusSM,
transition: `background-color ${token.motionDurationMid}, color ${token.motionDurationMid}`,
transition: ['color', 'background-color']
.map((prop) => `${prop} ${motionDurationMid}`)
.join(', '),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',

View File

@@ -116,6 +116,7 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
treeNodePadding,
titleHeight,
indentSize,
motionDurationMid,
nodeSelectedBg,
nodeHoverBg,
colorTextQuaternary,
@@ -352,7 +353,13 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
background: 'transparent',
borderRadius: token.borderRadius,
cursor: 'pointer',
transition: `all ${token.motionDurationMid}, border 0s, line-height 0s, box-shadow 0s`,
transition: [
`all ${motionDurationMid}`,
'border 0s',
'line-height 0s',
'box-shadow 0s',
].join(', '),
...getDropIndicatorStyle(prefixCls, token),
'&:hover': {

View File

@@ -5,7 +5,7 @@ import { clearFix, textEllipsis } from '../../style';
import type { GenerateStyle } from '../../theme/internal';
const genListStyle: GenerateStyle<UploadToken> = (token) => {
const { componentCls, iconCls, fontSize, lineHeight, calc } = token;
const { componentCls, iconCls, fontSize, lineHeight, motionDurationSlow, calc } = token;
const itemCls = `${componentCls}-list-item`;
const actionsCls = `${itemCls}-actions`;
const actionCls = `${itemCls}-action`;
@@ -23,7 +23,7 @@ const genListStyle: GenerateStyle<UploadToken> = (token) => {
fontSize,
display: 'flex',
alignItems: 'center',
transition: `background-color ${token.motionDurationSlow}`,
transition: `background-color ${motionDurationSlow}`,
borderRadius: token.borderRadiusSM,
'&:hover': {
@@ -35,7 +35,7 @@ const genListStyle: GenerateStyle<UploadToken> = (token) => {
padding: `0 ${unit(token.paddingXS)}`,
lineHeight,
flex: 'auto',
transition: `all ${token.motionDurationSlow}`,
transition: `all ${motionDurationSlow}`,
},
[actionsCls]: {
@@ -47,7 +47,7 @@ const genListStyle: GenerateStyle<UploadToken> = (token) => {
[iconCls]: {
color: token.actionsColor,
transition: `all ${token.motionDurationSlow}`,
transition: `all ${motionDurationSlow}`,
},
[`
@@ -100,7 +100,9 @@ const genListStyle: GenerateStyle<UploadToken> = (token) => {
},
[`${componentCls}-list-item-container`]: {
transition: `opacity ${token.motionDurationSlow}, height ${token.motionDurationSlow}`,
transition: ['opacity', 'height']
.map((prop) => `${prop} ${motionDurationSlow}`)
.join(', '),
// For smooth removing animation
'&::before': {