From 6975e38f79e65caffe046dc64a4e3dc70bbcd7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= <2712495353@qq.com> Date: Tue, 30 Jun 2026 23:15:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20Tailwind=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BB=A5=E6=94=AF=E6=8C=81=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E5=85=B1=E4=BA=AB=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新 components.json,修改 CSS 文件路径为 src/styles/shell.css。 - 移除 tailwind.config.js 中的冗余配置,改为导入 tailwind.shell.config.js。 - 新增 tailwind.pages.config.js 和 tailwind.shared.js,集中管理主题和样式配置。 - 更新多个组件以使用新的样式类和配置,提升代码一致性和可维护性。 --- components.json | 2 +- src/components/CopyButton.tsx | 6 +- src/components/ErrorFallback.tsx | 4 +- src/components/PageSkeleton.tsx | 2 +- src/components/RouterContainer.tsx | 4 +- src/components/SwitchButtonGroup.tsx | 3 +- src/components/TextInputArea.tsx | 14 ++- src/components/ui/button.tsx | 1 + src/components/ui/dialog.tsx | 4 +- src/components/ui/select.tsx | 2 +- src/config/pageLoaders/index.ts | 64 +++++++------ src/entrypoints/popup/App.tsx | 2 +- src/entrypoints/popup/main.tsx | 2 +- src/entrypoints/sidepanel/App.tsx | 2 +- src/entrypoints/sidepanel/main.tsx | 2 +- src/layout/TopBar/SearchDropdown.tsx | 2 +- src/pages/QrCode/components/QrCodePreview.tsx | 2 +- src/styles/pages.css | 3 + src/{index.css => styles/shell.css} | 90 +++++++++++++++++++ src/types/css.d.ts | 4 + tailwind.config.js | 35 +------- tailwind.pages.config.js | 22 +++++ tailwind.shared.js | 29 ++++++ tailwind.shell.config.js | 22 +++++ 24 files changed, 236 insertions(+), 87 deletions(-) create mode 100644 src/styles/pages.css rename src/{index.css => styles/shell.css} (57%) create mode 100644 src/types/css.d.ts create mode 100644 tailwind.pages.config.js create mode 100644 tailwind.shared.js create mode 100644 tailwind.shell.config.js diff --git a/components.json b/components.json index e2c49ef..fbd95c2 100644 --- a/components.json +++ b/components.json @@ -5,7 +5,7 @@ "tsx": true, "tailwind": { "config": "tailwind.config.js", - "css": "src/index.css", + "css": "src/styles/shell.css", "baseColor": "slate", "cssVariables": true, "prefix": "" diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx index 971e6c2..f70c40d 100644 --- a/src/components/CopyButton.tsx +++ b/src/components/CopyButton.tsx @@ -57,11 +57,7 @@ export const CopyButton: React.FC = ({ className={cn(className, copied && 'text-emerald-500')} {...props} > - {copied ? ( - - ) : ( - - )} + {copied ? : } ); }; diff --git a/src/components/ErrorFallback.tsx b/src/components/ErrorFallback.tsx index d7fae12..277bede 100644 --- a/src/components/ErrorFallback.tsx +++ b/src/components/ErrorFallback.tsx @@ -30,9 +30,7 @@ export function ErrorFallback({
diff --git a/src/components/PageSkeleton.tsx b/src/components/PageSkeleton.tsx index 2814850..cf7ed82 100644 --- a/src/components/PageSkeleton.tsx +++ b/src/components/PageSkeleton.tsx @@ -46,7 +46,7 @@ function ToolPageSkeleton() {
-
+
{/* 结果区域 */} diff --git a/src/components/RouterContainer.tsx b/src/components/RouterContainer.tsx index f0465c1..ef29af1 100644 --- a/src/components/RouterContainer.tsx +++ b/src/components/RouterContainer.tsx @@ -49,7 +49,7 @@ export default function RouterContainer() { key={currentPage} className={cn( 'flex-1 flex flex-col overflow-x-hidden overflow-y-auto', - 'scrollbar-gutter-stable motion-reduce:transition-none', + 'motion-reduce:transition-none', animationClass, )} > @@ -57,7 +57,7 @@ export default function RouterContainer() { {currentFeature ? ( ) : ( -
+
diff --git a/src/components/SwitchButtonGroup.tsx b/src/components/SwitchButtonGroup.tsx index b0e425d..5035b14 100644 --- a/src/components/SwitchButtonGroup.tsx +++ b/src/components/SwitchButtonGroup.tsx @@ -24,8 +24,7 @@ const SIZE_CLASSES = { large: 'text-base h-11 px-4 py-2 rounded-lg', } as const; -const SELECTED_CLASSES = - 'bg-background text-foreground shadow-sm font-semibold animate-in fade-in-50 zoom-in-95 duration-150'; +const SELECTED_CLASSES = 'bg-background text-foreground shadow-sm font-semibold fade-in-zoom-95'; const UNSELECTED_CLASSES = 'hover:bg-background/50 hover:text-foreground/80'; export default function SwitchButtonGroup({ diff --git a/src/components/TextInputArea.tsx b/src/components/TextInputArea.tsx index f1fcaff..3af1d16 100644 --- a/src/components/TextInputArea.tsx +++ b/src/components/TextInputArea.tsx @@ -267,7 +267,13 @@ const TextInputArea = forwardRef((props
{allowCopy && value && ( - + )} {showClear && value && !disabled && !readOnly && ( @@ -287,7 +293,7 @@ const TextInputArea = forwardRef((props
{displayError && ( -

+

{displayError}

)} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 0390bf6..6cec5ff 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -21,6 +21,7 @@ const buttonVariants = cva( sm: 'h-9 rounded-md px-3', lg: 'h-11 rounded-md px-8', icon: 'h-10 w-10', + iconSm: 'h-7 w-7', }, }, defaultVariants: { diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 68fa678..7a4d0e4 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -18,7 +18,7 @@ const DialogOverlay = React.forwardRef< { - switch (key) { - case 'dashboard': - return import('@/config/pageLoaders/dashboard').then((m) => m.default()); - case 'timestamp': - return import('@/config/pageLoaders/timestamp').then((m) => m.default()); - case 'storageCleaner': - return import('@/config/pageLoaders/storageCleaner').then((m) => m.default()); - case 'qrCode': - return import('@/config/pageLoaders/qrCode').then((m) => m.default()); - case 'textStatistics': - return import('@/config/pageLoaders/textStatistics').then((m) => m.default()); - case 'jwt': - return import('@/config/pageLoaders/jwt').then((m) => m.default()); - case 'jsonTools': - return import('@/config/pageLoaders/jsonTools').then((m) => m.default()); - case 'base64Converter': - return import('@/config/pageLoaders/base64Converter').then((m) => m.default()); - case 'rightClickRestorer': - return import('@/config/pageLoaders/rightClickRestorer').then((m) => m.default()); - case 'testDataGenerator': - return import('@/config/pageLoaders/testDataGenerator').then((m) => m.default()); - default: { - const _exhaustive: never = key; - return Promise.reject(new Error(`Unknown page: ${String(_exhaustive)}`)); - } +let pagesStylesLoaded = false; + +function ensurePagesStyles(): Promise { + if (pagesStylesLoaded) { + return Promise.resolve(); } + pagesStylesLoaded = true; + return import('@/styles/pages.css'); +} + +export function loadPage(key: PageType): Promise { + return ensurePagesStyles().then(() => { + switch (key) { + case 'dashboard': + return import('@/config/pageLoaders/dashboard').then((m) => m.default()); + case 'timestamp': + return import('@/config/pageLoaders/timestamp').then((m) => m.default()); + case 'storageCleaner': + return import('@/config/pageLoaders/storageCleaner').then((m) => m.default()); + case 'qrCode': + return import('@/config/pageLoaders/qrCode').then((m) => m.default()); + case 'textStatistics': + return import('@/config/pageLoaders/textStatistics').then((m) => m.default()); + case 'jwt': + return import('@/config/pageLoaders/jwt').then((m) => m.default()); + case 'jsonTools': + return import('@/config/pageLoaders/jsonTools').then((m) => m.default()); + case 'base64Converter': + return import('@/config/pageLoaders/base64Converter').then((m) => m.default()); + case 'rightClickRestorer': + return import('@/config/pageLoaders/rightClickRestorer').then((m) => m.default()); + case 'testDataGenerator': + return import('@/config/pageLoaders/testDataGenerator').then((m) => m.default()); + default: { + const _exhaustive: never = key; + return Promise.reject(new Error(`Unknown page: ${String(_exhaustive)}`)); + } + } + }); } diff --git a/src/entrypoints/popup/App.tsx b/src/entrypoints/popup/App.tsx index 9bda4d4..e7a33ba 100644 --- a/src/entrypoints/popup/App.tsx +++ b/src/entrypoints/popup/App.tsx @@ -25,7 +25,7 @@ export default function App() { visiblePagesKey={routerConfig.visiblePagesKey} pageOrderKey={routerConfig.pageOrderKey} > -
+
diff --git a/src/entrypoints/popup/main.tsx b/src/entrypoints/popup/main.tsx index 6f602b5..7f2f97c 100644 --- a/src/entrypoints/popup/main.tsx +++ b/src/entrypoints/popup/main.tsx @@ -1,6 +1,6 @@ import ReactDOM from 'react-dom/client'; import AppRoot from '@/providers/AppRoot'; -import '@/index.css'; +import '@/styles/shell.css'; import App from './App.tsx'; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/entrypoints/sidepanel/App.tsx b/src/entrypoints/sidepanel/App.tsx index 0475e7a..3cd8491 100644 --- a/src/entrypoints/sidepanel/App.tsx +++ b/src/entrypoints/sidepanel/App.tsx @@ -15,7 +15,7 @@ export default function App() { return ( -
+
diff --git a/src/entrypoints/sidepanel/main.tsx b/src/entrypoints/sidepanel/main.tsx index 6f602b5..7f2f97c 100644 --- a/src/entrypoints/sidepanel/main.tsx +++ b/src/entrypoints/sidepanel/main.tsx @@ -1,6 +1,6 @@ import ReactDOM from 'react-dom/client'; import AppRoot from '@/providers/AppRoot'; -import '@/index.css'; +import '@/styles/shell.css'; import App from './App.tsx'; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/layout/TopBar/SearchDropdown.tsx b/src/layout/TopBar/SearchDropdown.tsx index cd194ce..b43e84b 100644 --- a/src/layout/TopBar/SearchDropdown.tsx +++ b/src/layout/TopBar/SearchDropdown.tsx @@ -20,7 +20,7 @@ export default function SearchDropdown({ const items = isSearching ? searchResults : recentFeatures; return ( -
+
    {!isSearching && items.length > 0 && (
  • diff --git a/src/pages/QrCode/components/QrCodePreview.tsx b/src/pages/QrCode/components/QrCodePreview.tsx index 68cb8a3..ca95d42 100644 --- a/src/pages/QrCode/components/QrCodePreview.tsx +++ b/src/pages/QrCode/components/QrCodePreview.tsx @@ -44,7 +44,7 @@ const QrCodePreview = ({ QR Code Preview
diff --git a/src/styles/pages.css b/src/styles/pages.css new file mode 100644 index 0000000..fb7a340 --- /dev/null +++ b/src/styles/pages.css @@ -0,0 +1,3 @@ +@config "../../tailwind.pages.config.js"; + +@tailwind utilities; diff --git a/src/index.css b/src/styles/shell.css similarity index 57% rename from src/index.css rename to src/styles/shell.css index a92c1bc..7a1b002 100644 --- a/src/index.css +++ b/src/styles/shell.css @@ -1,3 +1,5 @@ +@config "../../tailwind.shell.config.js"; + @tailwind base; @tailwind components; @tailwind utilities; @@ -90,3 +92,91 @@ ::-webkit-scrollbar { display: none; } + +@layer components { + .page-transition-dashboard { + @apply motion-reduce:transition-none; + } + + .page-transition-enter { + @apply motion-reduce:transition-none; + animation: tt-page-enter 200ms ease-out; + } +} + +@layer utilities { + .fade-in-150 { + animation: tt-fade-in 150ms ease-out forwards; + } + + .fade-in-200 { + animation: tt-fade-in 200ms ease-out forwards; + } + + .fade-in-300 { + animation: tt-fade-in 300ms ease-out forwards; + } + + .fade-in-zoom-95 { + animation: tt-fade-in-zoom-95 150ms ease-out forwards; + } + + .fade-in-slide-top-1 { + animation: tt-fade-in-slide-top-1 150ms ease-out forwards; + } + + .fade-in-slide-top-2 { + animation: tt-fade-in-slide-top-2 150ms ease-out forwards; + } +} + +@keyframes tt-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes tt-fade-in-zoom-95 { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes tt-fade-in-slide-top-1 { + from { + opacity: 0; + transform: translateY(-0.25rem); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes tt-fade-in-slide-top-2 { + from { + opacity: 0; + transform: translateY(-0.5rem); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes tt-page-enter { + from { + opacity: 0.92; + } + to { + opacity: 1; + } +} diff --git a/src/types/css.d.ts b/src/types/css.d.ts new file mode 100644 index 0000000..ed6ca81 --- /dev/null +++ b/src/types/css.d.ts @@ -0,0 +1,4 @@ +declare module '*.css' { + const css: string; + export default css; +} diff --git a/tailwind.config.js b/tailwind.config.js index ed91354..42a1f61 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,34 +1 @@ -/** @type {import('tailwindcss').Config} */ -export default { - darkMode: 'class', - content: ['./entrypoints/**/*.{js,ts,jsx,tsx}', './src/**/*.{js,ts,jsx,tsx}'], - theme: { - extend: { - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: 'hsl(var(--card))', - 'card-foreground': 'hsl(var(--card-foreground))', - popover: 'hsl(var(--popover))', - 'popover-foreground': 'hsl(var(--popover-foreground))', - primary: 'hsl(var(--primary))', - 'primary-foreground': 'hsl(var(--primary-foreground))', - secondary: 'hsl(var(--secondary))', - 'secondary-foreground': 'hsl(var(--secondary-foreground))', - muted: 'hsl(var(--muted))', - 'muted-foreground': 'hsl(var(--muted-foreground))', - accent: 'hsl(var(--accent))', - 'accent-foreground': 'hsl(var(--accent-foreground))', - destructive: 'hsl(var(--destructive))', - 'destructive-foreground': 'hsl(var(--destructive-foreground))', - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - }, - borderRadius: { - DEFAULT: 'var(--radius)', - }, - }, - }, - plugins: [], -}; +export { default } from './tailwind.shell.config.js'; diff --git a/tailwind.pages.config.js b/tailwind.pages.config.js new file mode 100644 index 0000000..2466e8d --- /dev/null +++ b/tailwind.pages.config.js @@ -0,0 +1,22 @@ +import { sharedTheme } from './tailwind.shared.js'; + +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: 'class', + content: [ + './src/pages/**/*.{js,ts,jsx,tsx}', + './src/components/CopyButton.tsx', + './src/components/TextInputArea.tsx', + './src/components/SwitchButtonGroup.tsx', + './src/components/EmptyPlaceholder.tsx', + './src/components/ui/badge.tsx', + './src/components/ui/checkbox.tsx', + './src/components/ui/dialog.tsx', + './src/components/ui/label.tsx', + './src/components/ui/select.tsx', + './src/components/ui/switch.tsx', + './src/styles/pages.css', + ], + theme: sharedTheme, + plugins: [], +}; diff --git a/tailwind.shared.js b/tailwind.shared.js new file mode 100644 index 0000000..f873bba --- /dev/null +++ b/tailwind.shared.js @@ -0,0 +1,29 @@ +/** @type {import('tailwindcss').Config['theme']} */ +export const sharedTheme = { + extend: { + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: 'hsl(var(--card))', + 'card-foreground': 'hsl(var(--card-foreground))', + popover: 'hsl(var(--popover))', + 'popover-foreground': 'hsl(var(--popover-foreground))', + primary: 'hsl(var(--primary))', + 'primary-foreground': 'hsl(var(--primary-foreground))', + secondary: 'hsl(var(--secondary))', + 'secondary-foreground': 'hsl(var(--secondary-foreground))', + muted: 'hsl(var(--muted))', + 'muted-foreground': 'hsl(var(--muted-foreground))', + accent: 'hsl(var(--accent))', + 'accent-foreground': 'hsl(var(--accent-foreground))', + destructive: 'hsl(var(--destructive))', + 'destructive-foreground': 'hsl(var(--destructive-foreground))', + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + }, + borderRadius: { + DEFAULT: 'var(--radius)', + }, + }, +}; diff --git a/tailwind.shell.config.js b/tailwind.shell.config.js new file mode 100644 index 0000000..9acb3f0 --- /dev/null +++ b/tailwind.shell.config.js @@ -0,0 +1,22 @@ +import { sharedTheme } from './tailwind.shared.js'; + +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: 'class', + content: [ + './src/entrypoints/**/*.{js,ts,jsx,tsx}', + './src/layout/**/*.{js,ts,jsx,tsx}', + './src/providers/**/*.{js,ts,jsx,tsx}', + './src/components/RouterContainer.tsx', + './src/components/ErrorBoundary.tsx', + './src/components/ErrorFallback.tsx', + './src/components/PageErrorBoundary.tsx', + './src/components/PageSkeleton.tsx', + './src/components/ui/button.tsx', + './src/components/ui/input.tsx', + './src/components/ui/sonner.tsx', + './src/styles/shell.css', + ], + theme: sharedTheme, + plugins: [], +};