--- category: 开始 title: Tailwind 搭配使用 icon: doc-tailwind localeCode: zh-CN order: 9 brief: 更优雅地使用 TailwindCSS 与 Semi --- # 注意
本页将提供 TailwindCSS (只支持 v3,tailwind V4 暂未支持) 等原子类样式库与 Semi 共同使用时遇到的一些问题的最佳实践。 这些问题在其他组件库与 Tailwind 共同使用时候也会经常遇到,但 Semi 提供了官方解决方案,建议按照本文说明,正确配置项目。 Semi 不依赖任何第三方样式库,没有安装 Tailwind 一样可以运行,如果你没有使用 Tailwind 等原子类库,请直接关闭此页即可。 ### 1. 解决样式覆盖优先级问题 #### 问题表现 在组件中使用部分原子类时没有效果 或 组件库样式异常。 #### 原因分析 使用 Tailwind 时,Tailwind 通过 className 对 dom 进行样式修改,同样作用于 Semi 组件库。同时 Tailwind 默认开启了 [Preflight](https://tailwindcss.com/docs/preflight) 来重置浏览器默认样式。 此时根据你项目的配置和入口的 import,有两种可能: 1. Tailwind 比 Semi 的样式先引入,Semi 优先级更高 2. Semi 样式 比 Tailwind 先引入,Tailwind 优先级更高 如果是 1,则会出现 Tailwind 在添加某些原子类时,如果组件样式已经定义了某个 css 属性,原子类的优先级比 Semi 优先级低,此时原子类失效。 例如在 1 的前提下,对 Button 组件设置 padding,会出现失效的情况。 如果是 2,因为 Tailwind 优先级较高,其对浏览器默认样式覆盖的 Preflight 会同时覆盖掉 Semi 的样式。 例如在 2 的前提下,light 的 Button 的背景色会被覆盖为 transparent,导致样式表现异常。 #### 解决方案 无论 Tailwind 和 组件库哪一方优先级高,都会出现问题,因此解决方式在于正确处理 Tailwind 样式中 Preflight 和用户需要的原子类的优先级相对于组件库优先级的关系。 ** 1. 开启 Semi 插件 (>= 2.59.0) ** ```shell yarn add -D @douyinfe/semi-webpack-plugin ``` ** 2. 在项目中的配置文件中 ** - webpack 用户:在 webpack.config.js 引入 Semi webpack 插件并开启 cssLayer ```js const SemiPlugin = require('@douyinfe/semi-webpack-plugin').default; module.exports = { // ... plugins: [ new SemiPlugin({ cssLayer:true, /* ...options */ }) ] // ... }; ``` - rspack 用户:在 rspack.config.js 引入 Semi webpack 插件并开启 cssLayer ```js const {SemiRspackPlugin} = require('@douyinfe/semi-rspack-plugin'); module.exports = { // ... plugins: [ new SemiRspackPlugin({ cssLayer:true }) ] }; ``` 使用非 webpack rspack 构建的用户请参照原理自行对 semi 的 css 进行 layer 包裹 ** 3. 修改 Tailwind 入口配置** Tailwind 入口的 CSS 通常是包含了下面三行的文件 ```css @tailwind base; @tailwind components; @tailwind utilities; ``` 将其修改为(直接复制) ```css @layer tailwind-base,semi,tailwind-components,tailwind-utils; @layer tailwind-base{ @tailwind base; } @layer tailwind-components{ @tailwind components; } @layer tailwind-utils { @tailwind utilities; } ``` 并在项目的 JS 入口文件(即 App.tsx 或 index.js)处**最上方** import 上面修改的文件。(通常一个 Tailwind 项目对于上面文件的 import 已经处理好,只要将该 import 语句提到所有 import 语句前即可) CSS Layer 要求浏览器版本高于 Chromium 99 (兼容性表格),如果你的网站需要低版本浏览器访问,需要添加 CSS Layer 的 Polyfill,请参考此 Polyfill 的 PostCSS 插件文档 #### 原理 通过 CSS Layer 特性,实现不同来源的样式的优先级设置。 开启插件后,所有的 Semi 样式都会被 `@layer {xxxx}` 包裹。另外,我们也手动设置了项目中的 Tailwind 的各种类型的样式的 Layer。 另外,我们配置了各种 Layer 的优先级顺序: ```css @layer tailwind-base,semi,tailwind-components,tailwind-utils; ``` 上述 CSS 的含义为,base(含 Preflight)优先级最低,Semi 次之,用户设置的原子类样式(padding-[xxx] 等)优先级最高,这样即可解决上面遇到的问题。 ### 2.解决在 Tailwind 原子类中使用 Semi Token 的问题 (可选) Tailwind 支持用户配置自己的 Token 来实现主题。同时 Semi 也提供了自己的主题方案与对应 Token 如果想在项目中直接使用 Semi Token,例如将一个 span 的文字颜色设置为 `--semi-color-text-0` 来实现亮暗色的颜色自动切换与主题保持一致,需要单独设置 css `color: var(--semi-color-text-0)`,很不方便。 Semi 提供了 Tailwind 的主题配置文件,用于将 Semi 的 Token 映射为原子类 Token,上述需求可以直接给 span 设置 `text-semi-color-text-0` 即可。 在 Tailwind 配置中 (即 `tainwind.config.js`) 配置以下内容即可: ```js module.export = { theme:{ colors:{ "semi-color-white": "var(--semi-color-white)", "semi-color-black": "var(--semi-color-black)", "semi-color-primary": "var(--semi-color-primary)", "semi-color-primary-hover": "var(--semi-color-primary-hover)", "semi-color-primary-active": "var(--semi-color-primary-active)", "semi-color-primary-disabled": "var(--semi-color-primary-disabled)", "semi-color-primary-light-default": "var(--semi-color-primary-light-default)", "semi-color-primary-light-hover": "var(--semi-color-primary-light-hover)", "semi-color-primary-light-active": "var(--semi-color-primary-light-active)", "semi-color-secondary": "var(--semi-color-secondary)", "semi-color-secondary-hover": "var(--semi-color-secondary-hover)", "semi-color-secondary-active": "var(--semi-color-secondary-active)", "semi-color-secondary-disabled": "var(--semi-color-secondary-disabled)", "semi-color-secondary-light-default": "var(--semi-color-secondary-light-default)", "semi-color-secondary-light-hover": "var(--semi-color-secondary-light-hover)", "semi-color-secondary-light-active": "var(--semi-color-secondary-light-active)", "semi-color-tertiary": "var(--semi-color-tertiary)", "semi-color-tertiary-hover": "var(--semi-color-tertiary-hover)", "semi-color-tertiary-active": "var(--semi-color-tertiary-active)", "semi-color-tertiary-light-default": "var(--semi-color-tertiary-light-default)", "semi-color-tertiary-light-hover": "var(--semi-color-tertiary-light-hover)", "semi-color-tertiary-light-active": "var(--semi-color-tertiary-light-active)", "semi-color-default": "var(--semi-color-default)", "semi-color-default-hover": "var(--semi-color-default-hover)", "semi-color-default-active": "var(--semi-color-default-active)", "semi-color-info": "var(--semi-color-info)", "semi-color-info-hover": "var(--semi-color-info-hover)", "semi-color-info-active": "var(--semi-color-info-active)", "semi-color-info-disabled": "var(--semi-color-info-disabled)", "semi-color-info-light-default": "var(--semi-color-info-light-default)", "semi-color-info-light-hover": "var(--semi-color-info-light-hover)", "semi-color-info-light-active": "var(--semi-color-info-light-active)", "semi-color-success": "var(--semi-color-success)", "semi-color-success-hover": "var(--semi-color-success-hover)", "semi-color-success-active": "var(--semi-color-success-active)", "semi-color-success-disabled": "var(--semi-color-success-disabled)", "semi-color-success-light-default": "var(--semi-color-success-light-default)", "semi-color-success-light-hover": "var(--semi-color-success-light-hover)", "semi-color-success-light-active": "var(--semi-color-success-light-active)", "semi-color-danger": "var(--semi-color-danger)", "semi-color-danger-hover": "var(--semi-color-danger-hover)", "semi-color-danger-active": "var(--semi-color-danger-active)", "semi-color-danger-light-default": "var(--semi-color-danger-light-default)", "semi-color-danger-light-hover": "var(--semi-color-danger-light-hover)", "semi-color-danger-light-active": "var(--semi-color-danger-light-active)", "semi-color-warning": "var(--semi-color-warning)", "semi-color-warning-hover": "var(--semi-color-warning-hover)", "semi-color-warning-active": "var(--semi-color-warning-active)", "semi-color-warning-light-default": "var(--semi-color-warning-light-default)", "semi-color-warning-light-hover": "var(--semi-color-warning-light-hover)", "semi-color-warning-light-active": "var(--semi-color-warning-light-active)", "semi-color-focus-border": "var(--semi-color-focus-border)", "semi-color-disabled-text": "var(--semi-color-disabled-text)", "semi-color-disabled-border": "var(--semi-color-disabled-border)", "semi-color-disabled-bg": "var(--semi-color-disabled-bg)", "semi-color-disabled-fill": "var(--semi-color-disabled-fill)", "semi-color-shadow": "var(--semi-color-shadow)", "semi-color-link": "var(--semi-color-link)", "semi-color-link-hover": "var(--semi-color-link-hover)", "semi-color-link-active": "var(--semi-color-link-active)", "semi-color-link-visited": "var(--semi-color-link-visited)", "semi-color-border": "var(--semi-color-border)", "semi-color-nav-bg": "var(--semi-color-nav-bg)", "semi-color-overlay-bg": "var(--semi-color-overlay-bg)", "semi-color-fill-0": "var(--semi-color-fill-0)", "semi-color-fill-1": "var(--semi-color-fill-1)", "semi-color-fill-2": "var(--semi-color-fill-2)", "semi-color-bg-0": "var(--semi-color-bg-0)", "semi-color-bg-1": "var(--semi-color-bg-1)", "semi-color-bg-2": "var(--semi-color-bg-2)", "semi-color-bg-3": "var(--semi-color-bg-3)", "semi-color-bg-4": "var(--semi-color-bg-4)", "semi-color-text-0": "var(--semi-color-text-0)", "semi-color-text-1": "var(--semi-color-text-1)", "semi-color-text-2": "var(--semi-color-text-2)", "semi-color-text-3": "var(--semi-color-text-3)", "semi-color-highlight-bg": "var(--semi-color-highlight-bg)", "semi-color-highlight": "var(--semi-color-highlight)", "semi-color-data-0": "var(--semi-color-data-0)", "semi-color-data-1": "var(--semi-color-data-1)", "semi-color-data-2": "var(--semi-color-data-2)", "semi-color-data-3": "var(--semi-color-data-3)", "semi-color-data-4": "var(--semi-color-data-4)", "semi-color-data-5": "var(--semi-color-data-5)", "semi-color-data-6": "var(--semi-color-data-6)", "semi-color-data-7": "var(--semi-color-data-7)", "semi-color-data-8": "var(--semi-color-data-8)", "semi-color-data-9": "var(--semi-color-data-9)", "semi-color-data-10": "var(--semi-color-data-10)", "semi-color-data-11": "var(--semi-color-data-11)", "semi-color-data-12": "var(--semi-color-data-12)", "semi-color-data-13": "var(--semi-color-data-13)", "semi-color-data-14": "var(--semi-color-data-14)", "semi-color-data-15": "var(--semi-color-data-15)", "semi-color-data-16": "var(--semi-color-data-16)", "semi-color-data-17": "var(--semi-color-data-17)", "semi-color-data-18": "var(--semi-color-data-18)", "semi-color-data-19": "var(--semi-color-data-19)" }, extend:{ borderRadius:{ "semi-border-radius-extra-small": "var(--semi-border-radius-extra-small)", "semi-border-radius-small": "var(--semi-border-radius-small)", "semi-border-radius-medium": "var(--semi-border-radius-medium)", "semi-border-radius-large": "var(--semi-border-radius-large)", "semi-border-radius-circle": "var(--semi-border-radius-circle)", "semi-border-radius-full": "var(--semi-border-radius-full)", } } } } ```