feat: migrate to Tailwind CSS v4 (#7614)
* chore: update deps * feat: use jsonc/x language * chore: update eslint 10.0 * fix: no-useless-assignment * feat: add CLAUDE.md * chore: ignore * feat: claude * fix: lint * chore: suppot eslint v10 * fix: lint * fix: lint * fix: type check * fix: unit test * fix: Suggested fix * fix: unit test * chore: update stylelint v17 * chore: update all major deps * fix: echarts console warn * chore: update vitest v4 * feat: add skills ignores * chore: update deps * chore: update deps * fix: cspell * chore: update deps * chore: update tailwindcss v4 * chore: remove postcss config * fix: no use catalog * chore: tailwind v4 config * fix: tailwindcss v4 sort * feat: use eslint-plugin-better-tailwindcss * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix(lint): resolve prettier and better-tailwindcss formatting conflicts * fix(tailwind): update theme references and lint sources * style(format): normalize apps docs and playground vue files * style(format): normalize core ui-kit components * style(format): normalize effects ui and layout components
This commit is contained in:
@@ -32,12 +32,13 @@
|
||||
"eslint-plugin-import-x": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "catalog:",
|
||||
"@eslint/js": "catalog:",
|
||||
"@types/eslint": "catalog:",
|
||||
"@typescript-eslint/eslint-plugin": "catalog:",
|
||||
"@typescript-eslint/parser": "catalog:",
|
||||
"@vitest/eslint-plugin": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"eslint-plugin-eslint-comments": "catalog:",
|
||||
"eslint-plugin-better-tailwindcss": "catalog:",
|
||||
"eslint-plugin-jsdoc": "catalog:",
|
||||
"eslint-plugin-jsonc": "catalog:",
|
||||
"eslint-plugin-n": "catalog:",
|
||||
@@ -48,11 +49,9 @@
|
||||
"eslint-plugin-regexp": "catalog:",
|
||||
"eslint-plugin-unicorn": "catalog:",
|
||||
"eslint-plugin-unused-imports": "catalog:",
|
||||
"eslint-plugin-vitest": "catalog:",
|
||||
"eslint-plugin-vue": "catalog:",
|
||||
"eslint-plugin-yml": "catalog:",
|
||||
"globals": "catalog:",
|
||||
"jsonc-eslint-parser": "catalog:",
|
||||
"vue-eslint-parser": "catalog:",
|
||||
"yaml-eslint-parser": "catalog:"
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import { interopDefault } from '../util';
|
||||
|
||||
export async function comments(): Promise<Linter.Config[]> {
|
||||
const [pluginComments] = await Promise.all([
|
||||
// @ts-expect-error - no types
|
||||
interopDefault(import('eslint-plugin-eslint-comments')),
|
||||
interopDefault(import('@eslint-community/eslint-plugin-eslint-comments')),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
|
||||
@@ -48,6 +48,12 @@ export async function ignores(): Promise<Linter.Config[]> {
|
||||
'**/*.woff',
|
||||
'**/.github',
|
||||
'**/lefthook.yml',
|
||||
|
||||
'**/.agent/**',
|
||||
'**/.agents/**',
|
||||
'**/.codex/**',
|
||||
'**/.claude/**',
|
||||
'**/.cursor/**',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -11,6 +11,7 @@ export * from './perfectionist';
|
||||
export * from './pnpm';
|
||||
export * from './prettier';
|
||||
export * from './regexp';
|
||||
export * from './tailwindcss';
|
||||
export * from './test';
|
||||
export * from './turbo';
|
||||
export * from './typescript';
|
||||
|
||||
@@ -3,17 +3,12 @@ import type { Linter } from 'eslint';
|
||||
import { interopDefault } from '../util';
|
||||
|
||||
export async function jsonc(): Promise<Linter.Config[]> {
|
||||
const [pluginJsonc, parserJsonc] = await Promise.all([
|
||||
interopDefault(import('eslint-plugin-jsonc')),
|
||||
interopDefault(import('jsonc-eslint-parser')),
|
||||
] as const);
|
||||
const pluginJsonc = await interopDefault(import('eslint-plugin-jsonc'));
|
||||
|
||||
return [
|
||||
{
|
||||
files: ['**/*.json', '**/*.json5', '**/*.jsonc', '*.code-workspace'],
|
||||
languageOptions: {
|
||||
parser: parserJsonc as any,
|
||||
},
|
||||
language: 'jsonc/x',
|
||||
plugins: {
|
||||
jsonc: pluginJsonc as any,
|
||||
},
|
||||
|
||||
@@ -23,7 +23,6 @@ export async function node(): Promise<Linter.Config[]> {
|
||||
'vitest',
|
||||
'vite',
|
||||
'@vue/test-utils',
|
||||
'@vben/tailwind-config',
|
||||
'@playwright/test',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -21,41 +21,58 @@ export async function perfectionist(): Promise<Linter.Config[]> {
|
||||
'perfectionist/sort-imports': [
|
||||
'error',
|
||||
{
|
||||
customGroups: {
|
||||
type: {
|
||||
'vben-core-type': ['^@vben-core/.+'],
|
||||
'vben-type': ['^@vben/.+'],
|
||||
'vue-type': ['^vue$', '^vue-.+', '^@vue/.+'],
|
||||
customGroups: [
|
||||
{
|
||||
selector: 'type',
|
||||
groupName: 'vben-core-type',
|
||||
elementNamePattern: '^@vben-core/.+',
|
||||
},
|
||||
value: {
|
||||
vben: ['^@vben/.+'],
|
||||
'vben-core': ['^@vben-core/.+'],
|
||||
vue: ['^vue$', '^vue-.+', '^@vue/.+'],
|
||||
{
|
||||
selector: 'type',
|
||||
groupName: 'vben-type',
|
||||
elementNamePattern: '^@vben/.+',
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'type',
|
||||
groupName: 'vue-type',
|
||||
elementNamePattern: ['^vue$', '^vue-.+', '^@vue/.+'],
|
||||
},
|
||||
{
|
||||
groupName: 'vben',
|
||||
elementNamePattern: '^@vben/.+',
|
||||
},
|
||||
{
|
||||
groupName: 'vben-core',
|
||||
elementNamePattern: '^@vben-core/.+',
|
||||
},
|
||||
{
|
||||
groupName: 'vue',
|
||||
elementNamePattern: ['^vue$', '^vue-.+', '^@vue/.+'],
|
||||
},
|
||||
],
|
||||
environment: 'node',
|
||||
groups: [
|
||||
['external-type', 'builtin-type', 'type'],
|
||||
['type-external', 'type-builtin', 'type-import'],
|
||||
'vue-type',
|
||||
'vben-type',
|
||||
'vben-core-type',
|
||||
['parent-type', 'sibling-type', 'index-type'],
|
||||
['internal-type'],
|
||||
'builtin',
|
||||
['type-parent', 'type-sibling', 'type-index'],
|
||||
['type-internal'],
|
||||
'value-builtin',
|
||||
'vue',
|
||||
'vben',
|
||||
'vben-core',
|
||||
'external',
|
||||
'internal',
|
||||
['parent', 'sibling', 'index'],
|
||||
'value-external',
|
||||
'value-internal',
|
||||
['value-parent', 'value-sibling', 'value-index'],
|
||||
'side-effect',
|
||||
'side-effect-style',
|
||||
'style',
|
||||
'object',
|
||||
'ts-equals-import',
|
||||
'unknown',
|
||||
],
|
||||
internalPattern: ['^#/.+'],
|
||||
newlinesBetween: 'always',
|
||||
newlinesBetween: 1,
|
||||
order: 'asc',
|
||||
type: 'natural',
|
||||
},
|
||||
|
||||
@@ -3,18 +3,15 @@ import type { Linter } from 'eslint';
|
||||
import { interopDefault } from '../util';
|
||||
|
||||
export async function pnpm(): Promise<Linter.Config[]> {
|
||||
const [pluginPnpm, parserPnpm, parserJsonc] = await Promise.all([
|
||||
const [pluginPnpm, parserPnpm] = await Promise.all([
|
||||
interopDefault(import('eslint-plugin-pnpm')),
|
||||
interopDefault(import('yaml-eslint-parser')),
|
||||
interopDefault(import('jsonc-eslint-parser')),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
files: ['package.json', '**/package.json'],
|
||||
languageOptions: {
|
||||
parser: parserJsonc,
|
||||
},
|
||||
language: 'jsonc/x',
|
||||
plugins: {
|
||||
pnpm: pluginPnpm,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
import { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults';
|
||||
import { SelectorKind } from 'eslint-plugin-better-tailwindcss/types';
|
||||
|
||||
import { interopDefault } from '../util';
|
||||
|
||||
export async function tailwindcss(): Promise<Linter.Config[]> {
|
||||
const [pluginBetterTailwindcss] = await Promise.all([
|
||||
interopDefault(import('eslint-plugin-better-tailwindcss')),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
'better-tailwindcss': pluginBetterTailwindcss,
|
||||
},
|
||||
// shadcn-ui 内部组件是自动生成的,不做太多限制
|
||||
ignores: ['packages/@core/ui-kit/shadcn-ui/**/**'],
|
||||
settings: {
|
||||
'better-tailwindcss': {
|
||||
entryPoint: 'packages/@core/base/design/src/css/global.css',
|
||||
selectors: [
|
||||
...getDefaultSelectors(), // preserve default selectors
|
||||
{
|
||||
kind: SelectorKind.Attribute,
|
||||
match: [{ type: 'objectValues' }],
|
||||
name: '^classNames$',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...pluginBetterTailwindcss.configs.recommended.rules,
|
||||
'better-tailwindcss/enforce-consistent-class-order': [
|
||||
'error',
|
||||
{
|
||||
detectComponentClasses: true,
|
||||
unknownClassOrder: 'asc',
|
||||
unknownClassPosition: 'start',
|
||||
},
|
||||
],
|
||||
// Let Prettier own wrapping decisions to avoid ping-pong formatting.
|
||||
'better-tailwindcss/enforce-consistent-line-wrapping': 'off',
|
||||
'better-tailwindcss/no-unknown-classes': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { interopDefault } from '../util';
|
||||
|
||||
export async function test(): Promise<Linter.Config[]> {
|
||||
const [pluginTest, pluginNoOnlyTests] = await Promise.all([
|
||||
interopDefault(import('eslint-plugin-vitest')),
|
||||
interopDefault(import('@vitest/eslint-plugin')),
|
||||
// @ts-expect-error - no types
|
||||
interopDefault(import('eslint-plugin-no-only-tests')),
|
||||
] as const);
|
||||
|
||||
@@ -12,7 +12,7 @@ export async function yaml(): Promise<Linter.Config[]> {
|
||||
{
|
||||
files: ['**/*.y?(a)ml'],
|
||||
plugins: {
|
||||
yaml: pluginYaml as any,
|
||||
yaml: pluginYaml,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: parserYaml,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
const restrictedImportIgnores = [
|
||||
'**/vite.config.mts',
|
||||
'**/tailwind.config.mjs',
|
||||
'**/postcss.config.mjs',
|
||||
];
|
||||
const restrictedImportIgnores = ['**/vite.config.mts'];
|
||||
|
||||
const customConfig: Linter.Config[] = [
|
||||
// shadcn-ui 内部组件是自动生成的,不做太多限制
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
pnpm,
|
||||
prettier,
|
||||
regexp,
|
||||
tailwindcss,
|
||||
test,
|
||||
turbo,
|
||||
typescript,
|
||||
@@ -45,6 +46,7 @@ async function defineConfig(config: FlatConfig[] = []) {
|
||||
perfectionist(),
|
||||
comments(),
|
||||
jsdoc(),
|
||||
tailwindcss(),
|
||||
unicorn(),
|
||||
test(),
|
||||
regexp(),
|
||||
|
||||
@@ -9,7 +9,6 @@ export default {
|
||||
},
|
||||
},
|
||||
],
|
||||
plugins: ['prettier-plugin-tailwindcss'],
|
||||
printWidth: 80,
|
||||
proseWrap: 'never',
|
||||
semi: true,
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": "catalog:",
|
||||
"prettier-plugin-tailwindcss": "catalog:"
|
||||
"prettier": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,12 @@ export default {
|
||||
'use',
|
||||
'forward',
|
||||
'return',
|
||||
'reference',
|
||||
'plugin',
|
||||
'source',
|
||||
'theme',
|
||||
'utility',
|
||||
'custom-variant',
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -130,12 +136,18 @@ export default {
|
||||
'use',
|
||||
'forward',
|
||||
'return',
|
||||
'reference',
|
||||
'plugin',
|
||||
'source',
|
||||
'theme',
|
||||
'utility',
|
||||
'custom-variant',
|
||||
],
|
||||
},
|
||||
],
|
||||
'scss/operator-no-newline-after': null,
|
||||
'selector-class-pattern':
|
||||
'^(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:[.+])?$',
|
||||
'^-?(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:[.+])?$',
|
||||
|
||||
'selector-not-notation': null,
|
||||
},
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: ['src/index', './src/postcss.config'],
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"name": "@vben/tailwind-config",
|
||||
"version": "5.6.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/tailwind-config"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"./dist/*",
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./postcss": {
|
||||
"types": "./src/postcss.config.ts",
|
||||
"import": "./dist/postcss.config.mjs",
|
||||
"require": "./dist/postcss.config.cjs",
|
||||
"default": "./dist/postcss.config.mjs"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": "^3.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/json": "catalog:",
|
||||
"@iconify/tailwind": "catalog:",
|
||||
"@manypkg/get-packages": "catalog:",
|
||||
"@tailwindcss/nesting": "catalog:",
|
||||
"@tailwindcss/typography": "catalog:",
|
||||
"autoprefixer": "catalog:",
|
||||
"cssnano": "catalog:",
|
||||
"jiti": "catalog:",
|
||||
"postcss": "catalog:",
|
||||
"postcss-antd-fixes": "catalog:",
|
||||
"postcss-import": "catalog:",
|
||||
"postcss-preset-env": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
"tailwindcss-animate": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/postcss-import": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
import type { Config } from 'tailwindcss';
|
||||
|
||||
import path from 'node:path';
|
||||
|
||||
import { addDynamicIconSelectors } from '@iconify/tailwind';
|
||||
import { getPackagesSync } from '@manypkg/get-packages';
|
||||
import typographyPlugin from '@tailwindcss/typography';
|
||||
import animate from 'tailwindcss-animate';
|
||||
|
||||
import { enterAnimationPlugin } from './plugins/entry';
|
||||
|
||||
// import defaultTheme from 'tailwindcss/defaultTheme';
|
||||
|
||||
const { packages } = getPackagesSync(process.cwd());
|
||||
|
||||
const tailwindPackages: string[] = [];
|
||||
|
||||
packages.forEach((pkg) => {
|
||||
// apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui
|
||||
// if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
|
||||
tailwindPackages.push(pkg.dir);
|
||||
// }
|
||||
});
|
||||
|
||||
const shadcnUiColors = {
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
hover: 'hsl(var(--accent-hover))',
|
||||
lighter: 'has(val(--accent-lighter))',
|
||||
},
|
||||
background: {
|
||||
deep: 'hsl(var(--background-deep))',
|
||||
DEFAULT: 'hsl(var(--background))',
|
||||
},
|
||||
border: {
|
||||
DEFAULT: 'hsl(var(--border))',
|
||||
},
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
...createColorsPalette('destructive'),
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
},
|
||||
|
||||
foreground: {
|
||||
DEFAULT: 'hsl(var(--foreground))',
|
||||
},
|
||||
|
||||
input: {
|
||||
background: 'hsl(var(--input-background))',
|
||||
DEFAULT: 'hsl(var(--input))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
},
|
||||
primary: {
|
||||
...createColorsPalette('primary'),
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
},
|
||||
|
||||
ring: 'hsl(var(--ring))',
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
desc: 'hsl(var(--secondary-desc))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
};
|
||||
|
||||
const customColors = {
|
||||
green: {
|
||||
...createColorsPalette('green'),
|
||||
foreground: 'hsl(var(--success-foreground))',
|
||||
},
|
||||
header: {
|
||||
DEFAULT: 'hsl(var(--header))',
|
||||
},
|
||||
heavy: {
|
||||
DEFAULT: 'hsl(var(--heavy))',
|
||||
foreground: 'hsl(var(--heavy-foreground))',
|
||||
},
|
||||
main: {
|
||||
DEFAULT: 'hsl(var(--main))',
|
||||
},
|
||||
overlay: {
|
||||
content: 'hsl(var(--overlay-content))',
|
||||
DEFAULT: 'hsl(var(--overlay))',
|
||||
},
|
||||
red: {
|
||||
...createColorsPalette('red'),
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
sidebar: {
|
||||
deep: 'hsl(var(--sidebar-deep))',
|
||||
DEFAULT: 'hsl(var(--sidebar))',
|
||||
},
|
||||
success: {
|
||||
...createColorsPalette('success'),
|
||||
DEFAULT: 'hsl(var(--success))',
|
||||
},
|
||||
warning: {
|
||||
...createColorsPalette('warning'),
|
||||
DEFAULT: 'hsl(var(--warning))',
|
||||
},
|
||||
yellow: {
|
||||
...createColorsPalette('yellow'),
|
||||
foreground: 'hsl(var(--warning-foreground))',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
content: [
|
||||
'./index.html',
|
||||
...tailwindPackages.map((item) =>
|
||||
path.join(item, 'src/**/*.{vue,js,ts,jsx,tsx,svelte,astro,html}'),
|
||||
),
|
||||
],
|
||||
darkMode: 'selector',
|
||||
plugins: [
|
||||
animate,
|
||||
typographyPlugin,
|
||||
addDynamicIconSelectors(),
|
||||
enterAnimationPlugin,
|
||||
],
|
||||
prefix: '',
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
screens: {
|
||||
'2xl': '1400px',
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
animation: {
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
|
||||
'collapsible-up': 'collapsible-up 0.2s ease-in-out',
|
||||
float: 'float 5s linear 0ms infinite',
|
||||
},
|
||||
|
||||
animationDuration: {
|
||||
'2000': '2000ms',
|
||||
'3000': '3000ms',
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
xl: 'calc(var(--radius) + 4px)',
|
||||
},
|
||||
boxShadow: {
|
||||
float: `0 6px 16px 0 rgb(0 0 0 / 8%),
|
||||
0 3px 6px -4px rgb(0 0 0 / 12%),
|
||||
0 9px 28px 8px rgb(0 0 0 / 5%)`,
|
||||
},
|
||||
colors: {
|
||||
...customColors,
|
||||
...shadcnUiColors,
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [
|
||||
'var(--font-family)',
|
||||
// ...defaultTheme.fontFamily.sans
|
||||
],
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--reka-accordion-content-height)' },
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--reka-accordion-content-height)' },
|
||||
to: { height: '0' },
|
||||
},
|
||||
'collapsible-down': {
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--reka-collapsible-content-height)' },
|
||||
},
|
||||
'collapsible-up': {
|
||||
from: { height: 'var(--reka-collapsible-content-height)' },
|
||||
to: { height: '0' },
|
||||
},
|
||||
float: {
|
||||
'0%': { transform: 'translateY(0)' },
|
||||
'50%': { transform: 'translateY(-20px)' },
|
||||
'100%': { transform: 'translateY(0)' },
|
||||
},
|
||||
},
|
||||
zIndex: {
|
||||
'100': '100',
|
||||
'1000': '1000',
|
||||
},
|
||||
},
|
||||
},
|
||||
safelist: ['dark'],
|
||||
} as Config;
|
||||
|
||||
function createColorsPalette(name: string) {
|
||||
// backgroundLightest: '#EFF6FF', // Tailwind CSS 默认的 `blue-50`
|
||||
// backgroundLighter: '#DBEAFE', // Tailwind CSS 默认的 `blue-100`
|
||||
// backgroundLight: '#BFDBFE', // Tailwind CSS 默认的 `blue-200`
|
||||
// borderLight: '#93C5FD', // Tailwind CSS 默认的 `blue-300`
|
||||
// border: '#60A5FA', // Tailwind CSS 默认的 `blue-400`
|
||||
// main: '#3B82F6', // Tailwind CSS 默认的 `blue-500`
|
||||
// hover: '#2563EB', // Tailwind CSS 默认的 `blue-600`
|
||||
// active: '#1D4ED8', // Tailwind CSS 默认的 `blue-700`
|
||||
// backgroundDark: '#1E40AF', // Tailwind CSS 默认的 `blue-800`
|
||||
// backgroundDarker: '#1E3A8A', // Tailwind CSS 默认的 `blue-900`
|
||||
// backgroundDarkest: '#172554', // Tailwind CSS 默认的 `blue-950`
|
||||
|
||||
// • backgroundLightest (#EFF6FF): 适用于最浅的背景色,可能用于非常轻微的阴影或卡片的背景。
|
||||
// • backgroundLighter (#DBEAFE): 适用于略浅的背景色,通常用于次要背景或略浅的区域。
|
||||
// • backgroundLight (#BFDBFE): 适用于浅色背景,可能用于输入框或表单区域的背景。
|
||||
// • borderLight (#93C5FD): 适用于浅色边框,可能用于输入框或卡片的边框。
|
||||
// • border (#60A5FA): 适用于普通边框,可能用于按钮或卡片的边框。
|
||||
// • main (#3B82F6): 适用于主要的主题色,通常用于按钮、链接或主要的强调色。
|
||||
// • hover (#2563EB): 适用于鼠标悬停状态下的颜色,例如按钮悬停时的背景色或边框色。
|
||||
// • active (#1D4ED8): 适用于激活状态下的颜色,例如按钮按下时的背景色或边框色。
|
||||
// • backgroundDark (#1E40AF): 适用于深色背景,可能用于主要按钮或深色卡片背景。
|
||||
// • backgroundDarker (#1E3A8A): 适用于更深的背景,通常用于头部导航栏或页脚。
|
||||
// • backgroundDarkest (#172554): 适用于最深的背景,可能用于非常深色的区域或极端对比色。
|
||||
|
||||
return {
|
||||
50: `hsl(var(--${name}-50))`,
|
||||
100: `hsl(var(--${name}-100))`,
|
||||
200: `hsl(var(--${name}-200))`,
|
||||
300: `hsl(var(--${name}-300))`,
|
||||
400: `hsl(var(--${name}-400))`,
|
||||
500: `hsl(var(--${name}-500))`,
|
||||
600: `hsl(var(--${name}-600))`,
|
||||
700: `hsl(var(--${name}-700))`,
|
||||
// 800: `hsl(var(--${name}-800))`,
|
||||
// 900: `hsl(var(--${name}-900))`,
|
||||
// 950: `hsl(var(--${name}-950))`,
|
||||
// 激活状态下的颜色,适用于按钮按下时的背景色或边框色。
|
||||
active: `hsl(var(--${name}-700))`,
|
||||
// 浅色背景,适用于输入框或表单区域的背景。
|
||||
'background-light': `hsl(var(--${name}-200))`,
|
||||
// 适用于略浅的背景色,通常用于次要背景或略浅的区域。
|
||||
'background-lighter': `hsl(var(--${name}-100))`,
|
||||
// 最浅的背景色,适用于非常轻微的阴影或卡片的背景。
|
||||
'background-lightest': `hsl(var(--${name}-50))`,
|
||||
// 适用于普通边框,可能用于按钮或卡片的边框。
|
||||
border: `hsl(var(--${name}-400))`,
|
||||
// 浅色边框,适用于输入框或卡片的边框。
|
||||
'border-light': `hsl(var(--${name}-300))`,
|
||||
foreground: `hsl(var(--${name}-foreground))`,
|
||||
// 鼠标悬停状态下的颜色,适用于按钮悬停时的背景色或边框色。
|
||||
hover: `hsl(var(--${name}-600))`,
|
||||
// 主色文本
|
||||
text: `hsl(var(--${name}-500))`,
|
||||
// 主色文本激活态
|
||||
'text-active': `hsl(var(--${name}-700))`,
|
||||
// 主色文本悬浮态
|
||||
'text-hover': `hsl(var(--${name}-600))`,
|
||||
};
|
||||
}
|
||||
3
internal/tailwind-config/src/module.d.ts
vendored
3
internal/tailwind-config/src/module.d.ts
vendored
@@ -1,3 +0,0 @@
|
||||
declare module '@tailwindcss/nesting' {
|
||||
export default any;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import plugin from 'tailwindcss/plugin.js';
|
||||
|
||||
const enterAnimationPlugin = plugin(({ addUtilities }) => {
|
||||
const maxChild = 5;
|
||||
const utilities: Record<string, any> = {};
|
||||
for (let i = 1; i <= maxChild; i++) {
|
||||
const baseDelay = 0.1;
|
||||
const delay = `${baseDelay * i}s`;
|
||||
|
||||
utilities[`.enter-x:nth-child(${i})`] = {
|
||||
animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: '0',
|
||||
transform: `translateX(50px)`,
|
||||
};
|
||||
|
||||
utilities[`.enter-y:nth-child(${i})`] = {
|
||||
animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: '0',
|
||||
transform: `translateY(50px)`,
|
||||
};
|
||||
|
||||
utilities[`.-enter-x:nth-child(${i})`] = {
|
||||
animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: '0',
|
||||
transform: `translateX(-50px)`,
|
||||
};
|
||||
|
||||
utilities[`.-enter-y:nth-child(${i})`] = {
|
||||
animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: '0',
|
||||
transform: `translateY(-50px)`,
|
||||
};
|
||||
}
|
||||
|
||||
// 添加动画关键帧
|
||||
addUtilities(utilities);
|
||||
addUtilities({
|
||||
'@keyframes enter-x-animation': {
|
||||
to: {
|
||||
opacity: '1',
|
||||
transform: 'translateX(0)',
|
||||
},
|
||||
},
|
||||
'@keyframes enter-y-animation': {
|
||||
to: {
|
||||
opacity: '1',
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
export { enterAnimationPlugin };
|
||||
@@ -1,15 +0,0 @@
|
||||
import config from '.';
|
||||
|
||||
export default {
|
||||
plugins: {
|
||||
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
|
||||
// Specifying the config is not necessary in most cases, but it is included
|
||||
autoprefixer: {},
|
||||
// 修复 element-plus 和 ant-design-vue 的样式和tailwindcss冲突问题
|
||||
'postcss-antd-fixes': { prefixes: ['ant', 'el'] },
|
||||
'postcss-import': {},
|
||||
'postcss-preset-env': {},
|
||||
tailwindcss: { config },
|
||||
'tailwindcss/nesting': {},
|
||||
},
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/node.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
"dependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "catalog:",
|
||||
"@jspm/generator": "catalog:",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"archiver": "catalog:",
|
||||
"cheerio": "catalog:",
|
||||
"get-port": "catalog:",
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
} from '../typing';
|
||||
|
||||
import viteVueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import viteVue from '@vitejs/plugin-vue';
|
||||
import viteVueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import { visualizer as viteVisualizerPlugin } from 'rollup-plugin-visualizer';
|
||||
@@ -25,6 +26,7 @@ import { viteMetadataPlugin } from './inject-metadata';
|
||||
import { viteLicensePlugin } from './license';
|
||||
import { viteNitroMockPlugin } from './nitro-mock';
|
||||
import { vitePrintPlugin } from './print';
|
||||
import { viteTailwindReferencePlugin } from './tailwind-reference';
|
||||
import { viteVxeTableImportsPlugin } from './vxe-table';
|
||||
|
||||
/**
|
||||
@@ -60,6 +62,8 @@ async function loadCommonPlugins(
|
||||
},
|
||||
}),
|
||||
viteVueJsx(),
|
||||
viteTailwindReferencePlugin(),
|
||||
tailwindcss(),
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
40
internal/vite-config/src/plugins/tailwind-reference.ts
Normal file
40
internal/vite-config/src/plugins/tailwind-reference.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
const REFERENCE_LINE = '@reference "@vben-core/design/theme";\n';
|
||||
|
||||
/**
|
||||
* Auto-inject @reference into Vue SFC <style> blocks that use @apply.
|
||||
*
|
||||
* In Tailwind CSS v4, each Vue SFC <style scoped> block is processed as an
|
||||
* independent CSS module. If a style block uses @apply with custom theme
|
||||
* utilities (e.g. bg-primary, text-foreground), it needs access to the
|
||||
* @theme definition via @reference. This plugin auto-injects it so
|
||||
* individual components don't need to add it manually.
|
||||
*/
|
||||
export function viteTailwindReferencePlugin(): Plugin {
|
||||
return {
|
||||
enforce: 'pre',
|
||||
name: 'vite:tailwind-reference',
|
||||
transform(code, id) {
|
||||
// Only process Vue SFC style blocks
|
||||
if (!id.includes('.vue')) {
|
||||
return null;
|
||||
}
|
||||
if (!id.includes('type=style')) {
|
||||
return null;
|
||||
}
|
||||
// Skip if already has @reference
|
||||
if (code.includes('@reference')) {
|
||||
return null;
|
||||
}
|
||||
// Only inject if the style block uses @apply
|
||||
if (!code.includes('@apply')) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
code: REFERENCE_LINE + code,
|
||||
map: null,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user