diff --git a/apps/web-antdv-next/src/adapter/component/index.ts b/apps/web-antdv-next/src/adapter/component/index.ts
index 4a505fdb..ac3ac5f6 100644
--- a/apps/web-antdv-next/src/adapter/component/index.ts
+++ b/apps/web-antdv-next/src/adapter/component/index.ts
@@ -726,9 +726,7 @@ async function initComponentAdapter() {
modelValueProp: 'value',
}),
Input: withDefaultPlaceholder(Input, 'input'),
- InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
- style: { width: '100%' },
- }),
+ InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
diff --git a/apps/web-antdv-next/src/app.vue b/apps/web-antdv-next/src/app.vue
index 518f5068..46366e91 100644
--- a/apps/web-antdv-next/src/app.vue
+++ b/apps/web-antdv-next/src/app.vue
@@ -4,7 +4,7 @@ import { computed, watch } from 'vue';
import { useAntdDesignTokens } from '@vben/hooks';
import { preferences, usePreferences } from '@vben/preferences';
-import { App, ConfigProvider, theme } from 'antdv-next';
+import { App, ConfigProvider, StyleProvider, theme } from 'antdv-next';
import { antdLocale } from '#/locales';
@@ -39,9 +39,12 @@ watch(
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/apps/web-ele/src/adapter/component/index.ts b/apps/web-ele/src/adapter/component/index.ts
index 28d87c56..d4ba55c1 100644
--- a/apps/web-ele/src/adapter/component/index.ts
+++ b/apps/web-ele/src/adapter/component/index.ts
@@ -284,9 +284,7 @@ async function initComponentAdapter() {
inputComponent: ElInput,
}),
Input: withDefaultPlaceholder(ElInput, 'input'),
- InputNumber: withDefaultPlaceholder(ElInputNumber, 'input', {
- style: { width: '100%' },
- }),
+ InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
RadioGroup: (props, { attrs, slots }) => {
let defaultSlot;
if (Reflect.has(slots, 'default')) {
diff --git a/apps/web-ele/vite.config.ts b/apps/web-ele/vite.config.ts
index 9f1e7235..c50186cc 100644
--- a/apps/web-ele/vite.config.ts
+++ b/apps/web-ele/vite.config.ts
@@ -1,4 +1,4 @@
-import { defineConfig } from '@vben/vite-config';
+import { defineConfig, viteCssLayerPlugin } from '@vben/vite-config';
import ElementPlus from 'unplugin-element-plus/vite';
@@ -7,9 +7,9 @@ export default defineConfig(async () => {
application: {},
vite: {
plugins: [
- ElementPlus({
- format: 'esm',
- }),
+ // element-plus 的 css 包进 @layer el,使 Tailwind 工具类可覆盖组件样式
+ viteCssLayerPlugin({ layerName: 'el', packageName: 'element-plus' }),
+ ElementPlus({ format: 'esm' }),
],
server: {
proxy: {
diff --git a/apps/web-naive/src/adapter/component/index.ts b/apps/web-naive/src/adapter/component/index.ts
index 9ee677f8..98601656 100644
--- a/apps/web-naive/src/adapter/component/index.ts
+++ b/apps/web-naive/src/adapter/component/index.ts
@@ -225,9 +225,7 @@ async function initComponentAdapter() {
inputComponent: NInput,
}),
Input: withDefaultPlaceholder(NInput, 'input'),
- InputNumber: withDefaultPlaceholder(NInputNumber, 'input', {
- style: { width: '100%' },
- }),
+ InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
RadioGroup: (props, { attrs, slots }) => {
let defaultSlot;
if (Reflect.has(slots, 'default')) {
diff --git a/apps/web-tdesign/src/adapter/component/index.ts b/apps/web-tdesign/src/adapter/component/index.ts
index 37a3055e..c2f9634d 100644
--- a/apps/web-tdesign/src/adapter/component/index.ts
+++ b/apps/web-tdesign/src/adapter/component/index.ts
@@ -239,9 +239,7 @@ async function initComponentAdapter() {
modelValueProp: 'value',
}),
Input: withDefaultPlaceholder(Input, 'input'),
- InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
- style: { width: '100%' },
- }),
+ InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
// InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
// Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
diff --git a/apps/web-tdesign/vite.config.ts b/apps/web-tdesign/vite.config.ts
index b6360f1d..c79ec9a4 100644
--- a/apps/web-tdesign/vite.config.ts
+++ b/apps/web-tdesign/vite.config.ts
@@ -1,9 +1,16 @@
-import { defineConfig } from '@vben/vite-config';
+import { defineConfig, viteCssLayerPlugin } from '@vben/vite-config';
export default defineConfig(async () => {
return {
application: {},
vite: {
+ plugins: [
+ // tdesign 的 css 包进 @layer td,使 Tailwind 工具类可覆盖组件样式
+ viteCssLayerPlugin({
+ layerName: 'td',
+ packageName: 'tdesign-vue-next',
+ }),
+ ],
server: {
proxy: {
'/api': {
diff --git a/internal/tailwind-config/src/theme.css b/internal/tailwind-config/src/theme.css
index 076c1cfb..2bf31bbe 100644
--- a/internal/tailwind-config/src/theme.css
+++ b/internal/tailwind-config/src/theme.css
@@ -1,3 +1,12 @@
+/*
+ * 级联层顺序声明(必须先于 @import 'tailwindcss' 首次出现):
+ * preflight(base) < UI 库组件样式(antd / el / td) < Tailwind 工具类,
+ * 使组件库样式(antdv-next 经 StyleProvider layer 注入 @layer antd;
+ * element-plus / tdesign 经各自应用的 vite css-layer 插件包入对应层)
+ * 能被工具类覆盖
+ */
+@layer theme, base, ant, antd, el, td, components, utilities;
+
@import 'tailwindcss';
@import 'tw-animate-css';
@@ -301,7 +310,7 @@
}
html {
- @apply text-foreground bg-background font-sans;
+ @apply bg-background font-sans text-foreground;
scroll-behavior: smooth;
font-size: var(--font-size-base, 16px);
@@ -369,7 +378,7 @@
}
::-webkit-scrollbar-thumb {
- @apply bg-border rounded-sm border-none;
+ @apply rounded-sm border-none bg-border;
}
::-webkit-scrollbar-track {
@@ -407,7 +416,7 @@
* 见:https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layering */
@layer components {
.outline-box {
- @apply outline-border relative cursor-pointer rounded-md p-1 outline-1;
+ @apply relative cursor-pointer rounded-md p-1 outline-1 outline-border;
}
.outline-box::after {
@@ -415,7 +424,7 @@
}
.outline-box.outline-box-active {
- @apply outline-primary outline-2;
+ @apply outline-2 outline-primary;
}
.outline-box.outline-box-active::after {
@@ -423,15 +432,15 @@
}
.outline-box:not(.outline-box-active):hover::after {
- @apply outline-primary top-0 left-0 h-full w-full p-1 opacity-100;
+ @apply top-0 left-0 h-full w-full p-1 opacity-100 outline-primary;
}
.vben-link {
- @apply text-primary hover:text-primary-hover active:text-primary-active cursor-pointer;
+ @apply cursor-pointer text-primary hover:text-primary-hover active:text-primary-active;
}
.card-box {
- @apply bg-card text-card-foreground border-border rounded-xl border;
+ @apply rounded-xl border border-border bg-card text-card-foreground;
}
}
diff --git a/internal/vite-config/src/plugins/css-layer.ts b/internal/vite-config/src/plugins/css-layer.ts
new file mode 100644
index 00000000..d12e60cb
--- /dev/null
+++ b/internal/vite-config/src/plugins/css-layer.ts
@@ -0,0 +1,45 @@
+import type { Plugin } from 'vite';
+
+interface CssLayerRule {
+ /** 层名,需在 css 中先于 @import 'tailwindcss' 声明层顺序(见 internal/tailwind-config/theme.css) */
+ layerName: string;
+ /** 需要包层的包名(匹配该包在 node_modules 下的 css 模块 id) */
+ packageName: string;
+}
+
+function escapeRegExp(value: string): string {
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
+}
+
+/**
+ * 把指定包内的 css 包进 @layer:
+ * 组件库的 css 是无层样式,无层样式在级联中永远压过 @layer 内的 Tailwind 工具类;
+ * 包层后由 theme.css 的层声明决定顺序(utilities 排在组件库层之后),
+ * 使 Tailwind 工具类可以覆盖组件库样式。
+ *
+ * @example
+ * ```ts
+ * plugins: [viteCssLayerPlugin({ packageName: 'element-plus', layerName: 'el' })]
+ * ```
+ */
+export function viteCssLayerPlugin(
+ rules: CssLayerRule | CssLayerRule[],
+): Plugin {
+ const list = Array.isArray(rules) ? rules : [rules];
+ const matchers = list.map(({ layerName, packageName }) => ({
+ layerName,
+ regex: new RegExp(`${escapeRegExp(packageName)}[\\\\/].+\\.css$`, 'i'),
+ }));
+ return {
+ name: 'vite-plugin-css-layer',
+ enforce: 'pre',
+ transform(code, id) {
+ const [file] = id.split('?', 1);
+ if (!file) return;
+ const matched = matchers.find((m) => m.regex.test(file));
+ if (matched) {
+ return { code: `@layer ${matched.layerName} {\n${code}\n}`, map: null };
+ }
+ },
+ };
+}
diff --git a/internal/vite-config/src/plugins/index.ts b/internal/vite-config/src/plugins/index.ts
index 7488ab32..f7bdfd28 100644
--- a/internal/vite-config/src/plugins/index.ts
+++ b/internal/vite-config/src/plugins/index.ts
@@ -248,6 +248,8 @@ async function loadLibraryPlugins(
]);
}
+export { viteCssLayerPlugin } from './css-layer';
+
export {
loadApplicationPlugins,
loadLibraryPlugins,
diff --git a/playground/src/adapter/component/index.ts b/playground/src/adapter/component/index.ts
index 9a975a2d..bd41e361 100644
--- a/playground/src/adapter/component/index.ts
+++ b/playground/src/adapter/component/index.ts
@@ -726,9 +726,7 @@ async function initComponentAdapter() {
modelValueProp: 'value',
}),
Input: withDefaultPlaceholder(Input, 'input'),
- InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
- style: { width: '100%' },
- }),
+ InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
diff --git a/playground/src/app.vue b/playground/src/app.vue
index 518f5068..46366e91 100644
--- a/playground/src/app.vue
+++ b/playground/src/app.vue
@@ -4,7 +4,7 @@ import { computed, watch } from 'vue';
import { useAntdDesignTokens } from '@vben/hooks';
import { preferences, usePreferences } from '@vben/preferences';
-import { App, ConfigProvider, theme } from 'antdv-next';
+import { App, ConfigProvider, StyleProvider, theme } from 'antdv-next';
import { antdLocale } from '#/locales';
@@ -39,9 +39,12 @@ watch(
-
-
-
-
-
+
+
+
+
+
+
+
+