关联问题
项目是使用 react + ant-design + taiwindcss 搭建。记录一下使用过程中的一些问题。
- taiwindcss 无法覆盖了 ant-design 默认样式
- taiwindcss 中 resset 样式覆盖了 ant-design 样式
- 自定义 ant 主题的 token zIndexBase, zIndexPopupBase 后 button 样式丢失
- ant-design 样式丢失
ant-design 旧浏览器兼容
背景
一些客户的浏览器老旧 chrome 低于 79,不支持 :is :where 选择器,出现样式丢失。
原因
Ant Design 5.x 支持最近 2 个版本的现代浏览器。默认情况下,这些特性在旧版浏览器中可能不被支持,
解决办法
ant-design.antgroup.com/docs/react/…
taiwindcss 无法覆盖了 ant-design 默认样式
案例
import { Button } from 'ant'
function App () {
return <Button type="primary" class="text-red-500"></Button>
}
css 分析
/* 来源 tailwindcss 的样式 */
.text-red-500 {
color: red;
}
/* 来源 ant 的样式 */
.ant-btn-primary {
color: blue;
}
可以看出两个 css 的选择器权重一样,都是 1 所以就看谁后定义。有时会出现 tw class无法覆盖 ant 的情况。
解决方法
打包时在每个 class 前面加一个前缀,增加权重,text-red-500 变成 :not(ant-) .text-red-500。这样,权重为 2,就一定能覆盖 ant 的样式。这个前缀可以由 postcss 打包时自定生成。
postcss.config.js
export default {
plugins: {
"tailwindcss": {},
"autoprefixer": {},
"postcss-increase-specificity": {
repeat: 1,
stackableRoot: ":not(.ant-)", // 重点!!!
overrideIds: false,
},
},
};
taiwindcss 中 resset 样式覆盖了 ant-design 样式
因为ant 中使用了 :where 选择器。首先我们要知道 where 的 css 权重是 0,它是方便设置一些基础样式,以便其他样式可以轻松覆盖。而 tainwindcss 会引入一个 preflight.css 文件,这个文件包含了一些初始化的样式。
所以出现了一个问题, preflight.css 里的样式会覆盖 ant-design 的样式。我们以 button 为例:
/* ant-design 的 button 默认样式。 */
:where(.css-dev-only-do-not-override-djtmh8).ant-btn-primary {
color: #fff;
background: #1677ff;
box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
}
/* preflight.css */
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-feature-settings: inherit; /* 1 */
font-variation-settings: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
}
通过计算权重,我们发现上面两个样式的权重都是 1,那么后定义的就会生效,preflight.css 会生效,结果 ant 的 button 颜色为 color: inherit; 会变成黑色。
处理方法:在 tailwind.config.js 中,禁用掉自动生成 preflight.css 文件。
corePlugins: {
preflight: false // 重点!!!
},
禁用掉 preflight.css 后可以一些默认布局样式有问题,我们可以复制/node_modules/tailwindcss/src/css/preflight.css 文件到自己的项目中,然后在 main.tsx 中引入。
重写 reset 文件
方案一
在上一个问题中,我们将 preflight.css 文件放在自己的项目管理,还是避免不了会覆盖 ant-design 中有用到 :where 选择器的样式。
解决方案(只截取部分代码):
*:not([class^="ant-"]),
::before:not([class^="ant-"]),
::after:not([class^="ant-"]) {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb;
}
[class*="ant-"][class*="border-"] {
border-style: solid;
}
:not([class^="ant-"]) ::before,
:not([class^="ant-"]) ::after {
--tw-content: "";
}
*:not([class^="ant-"]) {
box-sizing: border-box;
padding: 0;
margin: 0;
}
is(button,input,optgroup,select,textarea):not([class^="ant-"]) {
font-family: inherit; /* 1 */
font-feature-settings: inherit; /* 1 */
font-variation-settings: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
letter-spacing: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
}
在所有可能影响的样式前面加一个 not([class^="ant-"]) 选择器,意思是不会作用在 ant- 开头的 class 元素身上。
方案一优化(更优解)
如果 postcss 中使用了 postcss-increase-specificity插件, preflight.css 里的样式默认会加上前缀:not(.ant-),例如:
postcss.config.js
export default {
plugins: {
"tailwindcss": {},
"autoprefixer": {},
"postcss-increase-specificity": {
repeat: 1,
stackableRoot: ":not(.ant-)", // 重点!!!
overrideIds: false,
},
},
};
preflight.css,权重 1
*:not([class^="ant-"]) {}
最终生成的代码,权重 2
:not(.ant-) *:not([class^="ant-"]) {}
这样会导致 preflight.css 里的部分样式权重特别高,本来应该是一个最基础样式,反而因为权重变高,变得不容易被其他样式覆盖。
解决方案
- 还原 preflight.less 文件,不做任何修改
- 修改 postcss-increase-specificity.cjs 插件,支持排查部分文件
var postcss = require("postcss");
var objectAssign = require("object-assign");
var escapeStringRegexp = require("escape-string-regexp");
var CSS_ESCAPED_TAB = "\\9";
function increaseSpecificityOfRule(rule, opts) {
rule.selectors = rule.selectors.map(function (selector) {
if (
selector === "html" ||
selector === ":root" ||
selector === ":host" ||
selector === opts.stackableRoot
) {
return selector + opts.stackableRoot.repeat(opts.repeat);
}
return opts.stackableRoot.repeat(opts.repeat) + " " + selector;
});
if (opts.overrideIds) {
if (
new RegExp("#(?!" + escapeStringRegexp(CSS_ESCAPED_TAB) + ")").test(
rule.selector,
) ||
/\[id/.test(rule.selector)
) {
rule.walkDecls(function (decl) {
decl.important = true;
});
}
}
}
module.exports = postcss.plugin(
"postcss-increase-specificity2",
function (options) {
var defaults = {
repeat: 3,
overrideIds: true,
stackableRoot: ":not(#" + CSS_ESCAPED_TAB + ")",
excludeFiles: [], // 新增选项
};
var opts = objectAssign({}, defaults, options);
return function (css) {
// 获取当前文件路径
const filePath = css.source && css.source.input.file;
// 检查文件路径是否应该被排除
if (
filePath &&
opts.excludeFiles.some((pattern) => {
const regex = new RegExp(pattern);
return regex.test(filePath);
})
) {
return; // 跳过处理
}
css.walkRules(function (rule) {
var isInsideKeyframes =
rule.parent.type === "atrule" && rule.parent.name === "keyframes";
if (!isInsideKeyframes) {
increaseSpecificityOfRule(rule, opts);
}
});
};
},
);
- 修改 postcss.config.js 文件,配置 excludeFiles 属性
module.exports = () => {
return {
plugins: [
require("tailwindcss"),
require("autoprefixer"),
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("./plugins/postcss-increase-specificity.cjs")({
repeat: 1,
stackableRoot: ":not(.ant-)",
overrideIds: false,
excludeFiles: ["preflight.less"], // 新增!!!
}),
],
};
};
这样做的好处是, preflight.less 不会被 postcss-increase-specificity 插件处理,权重是最低。能轻易被 tailwindcss 和 ant-design 样式覆盖。
总结
一定要理清项目内的权重关系是: reset.css < ant.css < taiwindcss、custom.class
样式丢失的本质的问题还是 css 权重问题,我们需要不断去调整项目内的权重,以达到最终效果。权重调节可以使用 postcss 在所有样式前面加权重,也可以使用 :not 来避免作用到其他地方。