diff --git a/components/carousel/style/index.ts b/components/carousel/style/index.ts index a2ff1e87c2..b10ae186b4 100644 --- a/components/carousel/style/index.ts +++ b/components/carousel/style/index.ts @@ -194,8 +194,8 @@ const genArrowsStyle: GenerateStyle = (token) => { width: arrowLength, height: arrowLength, border: `0 solid currentcolor`, - borderInlineWidth: '2px 0', - borderBlockWidth: '2px 0', + borderInlineStartWidth: 2, + borderBlockStartWidth: 2, borderRadius: 1, content: '""', }, diff --git a/components/color-picker/style/color-block.ts b/components/color-picker/style/color-block.ts index 647d80c510..f6c1fd30a4 100644 --- a/components/color-picker/style/color-block.ts +++ b/components/color-picker/style/color-block.ts @@ -5,9 +5,10 @@ import type { ColorPickerToken } from './index'; /** * @private Internal usage only + * see: https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/conic-gradient#checkerboard */ export const getTransBg = (size: string, colorFill: string): CSSObject => ({ - backgroundImage: `conic-gradient(${colorFill} 0 25%, transparent 0 50%, ${colorFill} 0 75%, transparent 0)`, + backgroundImage: `conic-gradient(${colorFill} 25%, transparent 25% 50%, ${colorFill} 50% 75%, transparent 75% 100%)`, backgroundSize: `${size} ${size}`, }); diff --git a/components/date-picker/style/panel.ts b/components/date-picker/style/panel.ts index 005281d0f6..dcc34a5b23 100644 --- a/components/date-picker/style/panel.ts +++ b/components/date-picker/style/panel.ts @@ -321,8 +321,8 @@ export const genPanelStyle = (token: SharedPickerToken): CSSObject => { width: pickerControlIconSize, height: pickerControlIconSize, border: `0 solid currentcolor`, - borderBlockWidth: `${unit(pickerControlIconBorderWidth)} 0`, - borderInlineWidth: `${unit(pickerControlIconBorderWidth)} 0`, + borderBlockStartWidth: pickerControlIconBorderWidth, + borderInlineStartWidth: pickerControlIconBorderWidth, content: '""', }, }, @@ -337,8 +337,8 @@ export const genPanelStyle = (token: SharedPickerToken): CSSObject => { width: pickerControlIconSize, height: pickerControlIconSize, border: '0 solid currentcolor', - borderBlockWidth: `${unit(pickerControlIconBorderWidth)} 0`, - borderInlineWidth: `${unit(pickerControlIconBorderWidth)} 0`, + borderBlockStartWidth: pickerControlIconBorderWidth, + borderInlineStartWidth: pickerControlIconBorderWidth, content: '""', }, }, diff --git a/components/float-button/style/keyframes.ts b/components/float-button/style/keyframes.ts index 7623f4c865..d7b190e33d 100644 --- a/components/float-button/style/keyframes.ts +++ b/components/float-button/style/keyframes.ts @@ -31,7 +31,7 @@ const floatButtonGroupMotion = (token: FloatButtonToken) => { }); const moveRightIn = new Keyframes('antFloatButtonMoveRightIn', { '0%': { - transform: `translate3d(${calc(floatButtonSize).mul(-1).equal()}, 0, 0)`, + transform: `translate3d(${unit(calc(floatButtonSize).mul(-1).equal())}, 0, 0)`, transformOrigin: '0 0', opacity: 0, }, @@ -48,14 +48,14 @@ const floatButtonGroupMotion = (token: FloatButtonToken) => { opacity: 1, }, '100%': { - transform: `translate3d(${calc(floatButtonSize).mul(-1).equal()}, 0, 0)`, + transform: `translate3d(${unit(calc(floatButtonSize).mul(-1).equal())}, 0, 0)`, transformOrigin: '0 0', opacity: 0, }, }); const moveBottomIn = new Keyframes('antFloatButtonMoveBottomIn', { '0%': { - transform: `translate3d(0, ${calc(floatButtonSize).mul(-1).equal()}, 0)`, + transform: `translate3d(0, ${unit(calc(floatButtonSize).mul(-1).equal())}, 0)`, transformOrigin: '0 0', opacity: 0, }, @@ -72,7 +72,7 @@ const floatButtonGroupMotion = (token: FloatButtonToken) => { opacity: 1, }, '100%': { - transform: `translate3d(0, ${calc(floatButtonSize).mul(-1).equal()}, 0)`, + transform: `translate3d(0, ${unit(calc(floatButtonSize).mul(-1).equal())}, 0)`, transformOrigin: '0 0', opacity: 0, }, diff --git a/components/form/style/index.ts b/components/form/style/index.ts index 49d161cea6..f0bdcad3ef 100644 --- a/components/form/style/index.ts +++ b/components/form/style/index.ts @@ -283,7 +283,7 @@ const genFormItemStyle: GenerateStyle = (token) => { marginInlineStart: token.marginXXS, color: token.colorTextDescription, - [`&.${formItemCls}-required-mark-hidden`]: { + [`&${formItemCls}-required-mark-hidden`]: { display: 'none', }, }, diff --git a/components/input-number/style/index.ts b/components/input-number/style/index.ts index 523a072bfc..8757e4fb7f 100644 --- a/components/input-number/style/index.ts +++ b/components/input-number/style/index.ts @@ -1,6 +1,11 @@ import { unit } from '@ant-design/cssinjs'; -import { genBasicInputStyle, genInputGroupStyle, genPlaceholderStyle, initInputToken } from '../../input/style'; +import { + genBasicInputStyle, + genInputGroupStyle, + genPlaceholderStyle, + initInputToken, +} from '../../input/style'; import { genBorderlessStyle, genFilledGroupStyle, @@ -235,7 +240,6 @@ const genInputNumberStyles: GenerateStyle = (token: InputNumbe '&[type="number"]::-webkit-inner-spin-button, &[type="number"]::-webkit-outer-spin-button': { margin: 0, - webkitAppearance: 'none', appearance: 'none', }, }, diff --git a/components/input/style/index.ts b/components/input/style/index.ts index e7805a848c..e2ce83dd43 100644 --- a/components/input/style/index.ts +++ b/components/input/style/index.ts @@ -400,7 +400,7 @@ export const genInputStyle: GenerateStyle = (token: InputToken) => { '&[type="search"]::-webkit-search-cancel-button, &[type="search"]::-webkit-search-decoration': { - '-webkit-appearance': 'none', + appearance: 'none', }, }, }; diff --git a/components/input/style/variants.ts b/components/input/style/variants.ts index 2fb98c3f18..7011b380e7 100644 --- a/components/input/style/variants.ts +++ b/components/input/style/variants.ts @@ -223,7 +223,7 @@ const genBaseFilledStyle = ( borderColor: 'transparent', 'input&, & input, textarea&, & textarea': { - color: options?.inputColor, + color: options?.inputColor ?? 'unset', }, '&:hover': { diff --git a/components/mentions/style/index.ts b/components/mentions/style/index.ts index 1e9254c531..d46ccd5a04 100644 --- a/components/mentions/style/index.ts +++ b/components/mentions/style/index.ts @@ -1,6 +1,11 @@ import { unit } from '@ant-design/cssinjs'; -import { genBasicInputStyle, genPlaceholderStyle, initComponentToken, initInputToken } from '../../input/style'; +import { + genBasicInputStyle, + genPlaceholderStyle, + initComponentToken, + initInputToken, +} from '../../input/style'; import type { SharedComponentToken, SharedInputToken } from '../../input/style/token'; import { genBorderlessStyle, @@ -163,7 +168,7 @@ const genMentionsStyle: GenerateStyle = (token) => { [`> textarea, ${componentCls}-measure`]: { color: colorText, boxSizing: 'border-box', - minHeight: token.calc(controlHeight).sub(2), + minHeight: token.calc(controlHeight).sub(2).equal(), margin: 0, padding: `${unit(paddingBlock)} ${unit(paddingInline)}`, overflow: 'inherit', diff --git a/components/notification/style/index.ts b/components/notification/style/index.ts index b0950ed98d..f787415895 100644 --- a/components/notification/style/index.ts +++ b/components/notification/style/index.ts @@ -213,7 +213,6 @@ export const genNoticeStyle = (token: NotificationToken): CSSObject => { position: 'absolute', display: 'block', appearance: 'none', - WebkitAppearance: 'none', inlineSize: `calc(100% - ${unit(borderRadiusLG)} * 2)`, left: { _skip_check_: true, diff --git a/components/select/style/index.ts b/components/select/style/index.ts index 0c8c1ec5e3..e2475a4802 100644 --- a/components/select/style/index.ts +++ b/components/select/style/index.ts @@ -62,7 +62,7 @@ const getSearchInputWithoutBorderStyle: GenerateStyle = '&::-webkit-search-cancel-button': { display: 'none', - '-webkit-appearance': 'none', + appearance: 'none', }, }, }; diff --git a/components/steps/style/index.ts b/components/steps/style/index.ts index 1ac60ce09a..6cc19160e1 100644 --- a/components/steps/style/index.ts +++ b/components/steps/style/index.ts @@ -419,7 +419,7 @@ export const prepareComponentToken: GetDefaultToken<'Steps'> = (token) => ({ dotSize: token.controlHeight / 4, dotCurrentSize: token.controlHeightLG / 4, navArrowColor: token.colorTextDisabled, - navContentMaxWidth: 'auto', + navContentMaxWidth: 'unset', descriptionMaxWidth: 140, waitIconColor: token.wireframe ? token.colorTextDisabled : token.colorTextLabel, waitIconBgColor: token.wireframe ? token.colorBgContainer : token.colorFillContent, diff --git a/components/table/style/sticky.ts b/components/table/style/sticky.ts index f11c55438e..9c3e606f67 100644 --- a/components/table/style/sticky.ts +++ b/components/table/style/sticky.ts @@ -48,7 +48,7 @@ const genStickyStyle: GenerateStyle = (token) => { height: tableScrollThumbSize, backgroundColor: tableScrollThumbBg, borderRadius: stickyScrollBarBorderRadius, - transition: `all ${token.motionDurationSlow}, transform none`, + transition: `all ${token.motionDurationSlow}, transform 0s`, position: 'absolute', bottom: 0, diff --git a/components/tree-select/style/index.ts b/components/tree-select/style/index.ts index 626b1b2d47..244d55359e 100644 --- a/components/tree-select/style/index.ts +++ b/components/tree-select/style/index.ts @@ -39,6 +39,7 @@ const genBaseStyle: GenerateStyle = (token) => { mergeToken(token, { colorBgContainer: colorBgElevated, }), + false, // 不需要 directory tree 的样式 ), { [treeCls]: { diff --git a/components/tree/style/index.ts b/components/tree/style/index.ts index 0e3034dcbb..2492d45732 100644 --- a/components/tree/style/index.ts +++ b/components/tree/style/index.ts @@ -432,6 +432,11 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject => export const genTreeStyle = ( prefixCls: string, token: AliasToken & TreeSharedToken & CSSUtil, + /** + * 是否启用目录树样式 + * @default true + */ + enableDirectory = true, ): CSSInterpolation => { const treeCls = `.${prefixCls}`; const treeNodeCls = `${treeCls}-treenode`; @@ -448,8 +453,8 @@ export const genTreeStyle = ( // Basic genBaseStyle(prefixCls, treeToken), // Directory - genDirectoryStyle(treeToken), - ]; + enableDirectory && genDirectoryStyle(treeToken), + ].filter(Boolean); }; export const initComponentToken = (token: AliasToken): TreeSharedToken => { diff --git a/package.json b/package.json index 64836199dc..2dc3fa5ede 100644 --- a/package.json +++ b/package.json @@ -192,6 +192,7 @@ "@types/adm-zip": "^0.5.6", "@types/ali-oss": "^6.16.11", "@types/cli-progress": "^3.11.6", + "@types/css-tree": "^2.3.10", "@types/fs-extra": "^11.0.4", "@types/gtag.js": "^0.0.20", "@types/http-server": "^0.12.4", @@ -232,6 +233,8 @@ "cli-progress": "^3.12.0", "cross-env": "^7.0.3", "cross-fetch": "^4.0.0", + "css-tree": "^3.1.0", + "csstree-validator": "^4.0.1", "cypress-image-diff-html-report": "2.2.0", "dekko": "^0.2.1", "dotenv": "^16.4.5", diff --git a/scripts/check-cssinjs.tsx b/scripts/check-cssinjs.tsx index 4eca3ac447..3c41f9e3f6 100644 --- a/scripts/check-cssinjs.tsx +++ b/scripts/check-cssinjs.tsx @@ -1,5 +1,8 @@ +import path from 'path'; import React from 'react'; import { + createCache, + extractStyle, legacyNotSelectorLinter, logicalPropertiesLinter, NaNLinter, @@ -7,11 +10,19 @@ import { StyleProvider, } from '@ant-design/cssinjs'; import chalk from 'chalk'; +import { parse } from 'css-tree'; +import type { SyntaxParseError } from 'css-tree'; +import { validate } from 'csstree-validator'; +import fs from 'fs-extra'; +import isCI from 'is-ci'; import ReactDOMServer from 'react-dom/server'; import { ConfigProvider } from '../components'; import { generateCssinjs } from './generate-cssinjs'; +const tmpDir = path.join(`${__filename}.tmp`); +fs.emptyDirSync(tmpDir); + console.log(chalk.green(`🔥 Checking CSS-in-JS...`)); let errorCount = 0; @@ -25,6 +36,20 @@ console.error = (msg: any) => { } }; +// https://github.com/csstree/validator/blob/7df8ca/lib/validate.js#L187 +function cssValidate(css: string, filename: string) { + const errors: SyntaxParseError[] = []; + const ast = parse(css, { + filename, + positions: true, + onParseError(error) { + errors.push(error); + }, + }); + + return errors.concat(validate(ast)); +} + async function checkCSSVar() { await generateCssinjs({ key: 'check', @@ -39,6 +64,38 @@ async function checkCSSVar() { }, }); } +async function checkCSSContent() { + const errors = new Map(); + await generateCssinjs({ + key: 'css-validate', + render(Component: any, filePath: string) { + const cache = createCache(); + ReactDOMServer.renderToString( + + + , + ); + + const css = extractStyle(cache, { types: 'style', plain: true }); + let showPath = filePath; + if (!isCI) { + const [, name] = filePath.split(path.sep); + const writeLocalPath = path.join(tmpDir, `${name}.css`); + showPath = path.relative(process.cwd(), writeLocalPath); + fs.writeFileSync(writeLocalPath, `/* ${filePath} */\n${css}`); + } + errors.set(filePath, cssValidate(css, showPath)); + }, + }); + + for (const [filePath, error] of errors) { + if (error.length > 0) { + errorCount += error.length; + console.log(chalk.red(`❌ ${filePath} has ${error.length} errors:`)); + console.log(error); + } + } +} (async () => { await generateCssinjs({ @@ -55,6 +112,7 @@ async function checkCSSVar() { }); await checkCSSVar(); + await checkCSSContent(); if (errorCount > 0) { console.log(chalk.red(`❌ CSS-in-JS check failed with ${errorCount} errors.`)); diff --git a/scripts/generate-cssinjs.ts b/scripts/generate-cssinjs.ts index e16412607d..7b64d4ee51 100644 --- a/scripts/generate-cssinjs.ts +++ b/scripts/generate-cssinjs.ts @@ -7,7 +7,7 @@ type StyleFn = (prefix?: string) => void; interface GenCssinjsOptions { key: string; - render: (component: React.FC) => void; + render: (Component: React.FC, filepath: string) => void; beforeRender?: (componentName: string) => void; } @@ -35,6 +35,10 @@ export const generateCssinjs = ({ key, beforeRender, render }: GenCssinjsOptions useRowStyle(prefixCls); useColStyle(prefixCls); }; + } else if (file.includes('tree-select')) { + const originalUseStyle = (await import(absPath)).default; + useStyle = (prefixCls, treePrefixCls = `${prefixCls}-tree`) => + originalUseStyle(prefixCls, treePrefixCls); } else { useStyle = (await import(absPath)).default; } @@ -43,6 +47,6 @@ export const generateCssinjs = ({ key, beforeRender, render }: GenCssinjsOptions return React.createElement('div'); }; beforeRender?.(componentName); - render?.(Demo); + render?.(Demo, path.relative(process.cwd(), file)); }), ); diff --git a/typings/custom-typings.d.ts b/typings/custom-typings.d.ts index 758a40db17..0a8c955599 100644 --- a/typings/custom-typings.d.ts +++ b/typings/custom-typings.d.ts @@ -29,3 +29,5 @@ declare module '@npmcli/run-script' { declare module '@microflash/rehype-figure'; declare module 'dekko'; + +declare module 'csstree-validator';