引入 Tailwind CSS 和 shadcn/ui

This commit is contained in:
雨霖铃
2026-05-21 22:57:23 +08:00
parent bb27577278
commit 3eb00d8a29
14 changed files with 1034 additions and 3 deletions
+20
View File
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
+48
View File
@@ -0,0 +1,48 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
);
},
);
Button.displayName = 'Button';
export { Button, buttonVariants };
+1
View File
@@ -1,6 +1,7 @@
import ReactDOM from 'react-dom/client';
import AppRoot from '@/providers/AppRoot';
import '@/i18n';
import '@/src/index.css';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
+4
View File
@@ -6,6 +6,7 @@ import { SnackbarProvider } from '@/components/GlobalSnackbar';
import { Box } from '@mui/material';
import { getEntryPointType } from '@/config/features';
import { useMemo } from 'react';
import { Button } from '@/components/ui/button';
export default function App() {
// 打开Chrome扩展选项页面,需确保manifest中已配置options_page或options_ui
@@ -63,6 +64,9 @@ export default function App() {
<ErrorBoundary>
<RouterContainer />
</ErrorBoundary>
<Button variant="outline" className="m-4">
Tailwind CSS
</Button>
</Box>
</SnackbarProvider>
</RouterProvider>
+1
View File
@@ -1,6 +1,7 @@
import ReactDOM from 'react-dom/client';
import AppRoot from '@/providers/AppRoot';
import '@/i18n';
import '@/src/index.css';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(
+1
View File
@@ -1,6 +1,7 @@
import ReactDOM from 'react-dom/client';
import AppRoot from '@/providers/AppRoot';
import '@/i18n';
import '@/src/index.css';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(
+6
View File
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+835 -1
View File
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -28,18 +28,23 @@
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.8",
"@mui/material": "^7.3.8",
"@radix-ui/react-slot": "^1.2.4",
"@testing-library/dom": "^10.4.1",
"@vitest/coverage-v8": "^4.1.7",
"@webext-core/messaging": "^3.0.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.20",
"i18next": "^26.2.0",
"i18next-browser-languagedetector": "^8.2.1",
"lucide-react": "^1.16.0",
"marked": "^18.0.4",
"qr-scanner": "^1.4.2",
"qrious": "^4.0.2",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-i18next": "^17.0.8"
"react-i18next": "^17.0.8",
"tailwind-merge": "^3.6.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1",
@@ -53,6 +58,7 @@
"@typescript-eslint/parser": "^8.59.4",
"@vitejs/plugin-react": "^6.0.2",
"@wxt-dev/module-react": "^1.2.2",
"autoprefixer": "^10.5.0",
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
@@ -60,7 +66,9 @@
"husky": "^9.1.7",
"jsdom": "^29.1.1",
"lint-staged": "^17.0.5",
"postcss": "^8.5.15",
"prettier": "^3.8.3",
"tailwindcss": "^3.4.19",
"terser": "^5.47.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.59.4",
+6
View File
@@ -0,0 +1,6 @@
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';
export default {
plugins: [tailwindcss, autoprefixer],
};
+77
View File
@@ -0,0 +1,77 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
@layer base {
* {
border-color: hsl(var(--border));
}
body {
background-color: hsl(var(--background));
color: hsl(var(--foreground));
}
}
+15
View File
@@ -0,0 +1,15 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./entrypoints/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./pages/**/*.{js,ts,jsx,tsx}',
'./providers/**/*.{js,ts,jsx,tsx}',
'./utils/**/*.{js,ts,jsx,tsx}',
'./config/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [],
};
+7 -1
View File
@@ -26,7 +26,11 @@
"target": "ESNext",
"types": ["chrome", "webextension-polyfill", "@testing-library/jest-dom", "vitest/globals"],
"noImplicitAny": true
"noImplicitAny": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
},
// 确保包含你的源代码目录
"include": [
@@ -39,6 +43,8 @@
"types/**/*",
"assets/**/*",
"hooks/**/*",
"lib/**/*",
"src/**/*",
".wxt/types/**/*.ts",
".wxt/types/*.d.ts",
"eslint.config.ts"
+4
View File
@@ -1,5 +1,6 @@
import { defineConfig } from 'wxt';
import type { Plugin } from 'vite';
import postcssConfig from './postcss.config.js';
/**
* 仅在 HTML 多入口构建(popup / options / sidepanel)中启用 manualChunks 拆分 vendor。
@@ -94,5 +95,8 @@ export default defineConfig({
},
chunkSizeWarningLimit: 600,
},
css: {
postcss: postcssConfig,
},
}),
});