refactor: 重构 Tailwind 配置以支持模块化和共享主题
- 更新 components.json,修改 CSS 文件路径为 src/styles/shell.css。 - 移除 tailwind.config.js 中的冗余配置,改为导入 tailwind.shell.config.js。 - 新增 tailwind.pages.config.js 和 tailwind.shared.js,集中管理主题和样式配置。 - 更新多个组件以使用新的样式类和配置,提升代码一致性和可维护性。
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
"tsx": true,
|
"tsx": true,
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.js",
|
"config": "tailwind.config.js",
|
||||||
"css": "src/index.css",
|
"css": "src/styles/shell.css",
|
||||||
"baseColor": "slate",
|
"baseColor": "slate",
|
||||||
"cssVariables": true,
|
"cssVariables": true,
|
||||||
"prefix": ""
|
"prefix": ""
|
||||||
|
|||||||
@@ -57,11 +57,7 @@ export const CopyButton: React.FC<CopyButtonProps> = ({
|
|||||||
className={cn(className, copied && 'text-emerald-500')}
|
className={cn(className, copied && 'text-emerald-500')}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{copied ? (
|
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||||
<Check className="h-[1.2em] w-[1.2em]" />
|
|
||||||
) : (
|
|
||||||
<Copy className="h-[1.2em] w-[1.2em]" />
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ export function ErrorFallback({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-col items-center justify-center',
|
'flex flex-col items-center justify-center',
|
||||||
isApp
|
isApp ? 'mt-16 mx-auto max-w-md' : 'flex-1 p-6 min-h-[300px] fade-in-zoom-95',
|
||||||
? 'mt-16 mx-auto max-w-md'
|
|
||||||
: 'flex-1 p-6 min-h-[300px] animate-in fade-in zoom-in-95 duration-200',
|
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ function ToolPageSkeleton() {
|
|||||||
<div className="w-24 h-9 bg-muted rounded-lg animate-pulse" />
|
<div className="w-24 h-9 bg-muted rounded-lg animate-pulse" />
|
||||||
<div className="w-20 h-9 bg-muted rounded-lg animate-pulse" />
|
<div className="w-20 h-9 bg-muted rounded-lg animate-pulse" />
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
<div className="w-22 h-9 bg-muted rounded-lg animate-pulse" />
|
<div className="w-24 h-9 bg-muted rounded-lg animate-pulse" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 结果区域 */}
|
{/* 结果区域 */}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default function RouterContainer() {
|
|||||||
key={currentPage}
|
key={currentPage}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex-1 flex flex-col overflow-x-hidden overflow-y-auto',
|
'flex-1 flex flex-col overflow-x-hidden overflow-y-auto',
|
||||||
'scrollbar-gutter-stable motion-reduce:transition-none',
|
'motion-reduce:transition-none',
|
||||||
animationClass,
|
animationClass,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -57,7 +57,7 @@ export default function RouterContainer() {
|
|||||||
{currentFeature ? (
|
{currentFeature ? (
|
||||||
<LoadedPage key={currentPage} pageKey={currentPage} />
|
<LoadedPage key={currentPage} pageKey={currentPage} />
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center animate-in fade-in duration-300">
|
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center fade-in-300">
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-destructive/10 text-destructive mb-4">
|
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-destructive/10 text-destructive mb-4">
|
||||||
<AlertTriangle className="h-6 w-6" />
|
<AlertTriangle className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ const SIZE_CLASSES = {
|
|||||||
large: 'text-base h-11 px-4 py-2 rounded-lg',
|
large: 'text-base h-11 px-4 py-2 rounded-lg',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const SELECTED_CLASSES =
|
const SELECTED_CLASSES = 'bg-background text-foreground shadow-sm font-semibold fade-in-zoom-95';
|
||||||
'bg-background text-foreground shadow-sm font-semibold animate-in fade-in-50 zoom-in-95 duration-150';
|
|
||||||
const UNSELECTED_CLASSES = 'hover:bg-background/50 hover:text-foreground/80';
|
const UNSELECTED_CLASSES = 'hover:bg-background/50 hover:text-foreground/80';
|
||||||
|
|
||||||
export default function SwitchButtonGroup<T extends string | number = string>({
|
export default function SwitchButtonGroup<T extends string | number = string>({
|
||||||
|
|||||||
@@ -267,7 +267,13 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
|
|||||||
|
|
||||||
<div className="flex items-center gap-1.5 ml-auto shrink-0">
|
<div className="flex items-center gap-1.5 ml-auto shrink-0">
|
||||||
{allowCopy && value && (
|
{allowCopy && value && (
|
||||||
<CopyButton text={value} tooltip="复制内容" size="sm" className="h-7 w-7 p-1" />
|
<CopyButton
|
||||||
|
text={value}
|
||||||
|
tooltip="复制内容"
|
||||||
|
variant="ghost"
|
||||||
|
size="iconSm"
|
||||||
|
className="text-muted-foreground hover:text-foreground"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{showClear && value && !disabled && !readOnly && (
|
{showClear && value && !disabled && !readOnly && (
|
||||||
<Button
|
<Button
|
||||||
@@ -275,8 +281,8 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
|
|||||||
onClick={handleClear}
|
onClick={handleClear}
|
||||||
aria-label="清空"
|
aria-label="清空"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="iconSm"
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
|
className="text-muted-foreground hover:text-destructive hover:bg-destructive/10"
|
||||||
>
|
>
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -287,7 +293,7 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{displayError && (
|
{displayError && (
|
||||||
<p className="text-xs font-medium text-destructive px-0.5 animate-in fade-in slide-in-from-top-1 duration-150">
|
<p className="text-xs font-medium text-destructive px-0.5 fade-in-slide-top-1">
|
||||||
{displayError}
|
{displayError}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const buttonVariants = cva(
|
|||||||
sm: 'h-9 rounded-md px-3',
|
sm: 'h-9 rounded-md px-3',
|
||||||
lg: 'h-11 rounded-md px-8',
|
lg: 'h-11 rounded-md px-8',
|
||||||
icon: 'h-10 w-10',
|
icon: 'h-10 w-10',
|
||||||
|
iconSm: 'h-7 w-7',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const DialogOverlay = React.forwardRef<
|
|||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed inset-0 z-50 bg-black/80 dark:bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
'fixed inset-0 z-50 bg-black/80 dark:bg-black/60 transition-opacity duration-200 data-[state=closed]:opacity-0 data-[state=open]:opacity-100',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -41,7 +41,7 @@ const DialogContent = React.forwardRef<
|
|||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-white dark:bg-gray-900 p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-white dark:bg-gray-900 p-6 shadow-lg transition-all duration-200 data-[state=closed]:scale-95 data-[state=open]:scale-100 data-[state=closed]:opacity-0 data-[state=open]:opacity-100 sm:rounded-lg',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const SelectContent = React.forwardRef<
|
|||||||
<SelectPrimitive.Content
|
<SelectPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md transition-all duration-200 data-[state=closed]:scale-95 data-[state=open]:scale-100 data-[state=closed]:opacity-0 data-[state=open]:opacity-100',
|
||||||
position === 'popper' &&
|
position === 'popper' &&
|
||||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||||
className,
|
className,
|
||||||
|
|||||||
@@ -3,31 +3,43 @@ import type { PageType } from '@/types/storage';
|
|||||||
|
|
||||||
type PageModule = { default: ComponentType };
|
type PageModule = { default: ComponentType };
|
||||||
|
|
||||||
export function loadPage(key: PageType): Promise<PageModule> {
|
let pagesStylesLoaded = false;
|
||||||
switch (key) {
|
|
||||||
case 'dashboard':
|
function ensurePagesStyles(): Promise<unknown> {
|
||||||
return import('@/config/pageLoaders/dashboard').then((m) => m.default());
|
if (pagesStylesLoaded) {
|
||||||
case 'timestamp':
|
return Promise.resolve();
|
||||||
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)}`));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
pagesStylesLoaded = true;
|
||||||
|
return import('@/styles/pages.css');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadPage(key: PageType): Promise<PageModule> {
|
||||||
|
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)}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default function App() {
|
|||||||
visiblePagesKey={routerConfig.visiblePagesKey}
|
visiblePagesKey={routerConfig.visiblePagesKey}
|
||||||
pageOrderKey={routerConfig.pageOrderKey}
|
pageOrderKey={routerConfig.pageOrderKey}
|
||||||
>
|
>
|
||||||
<div className="app flex flex-col w-[400px] max-w-[400px] min-w-[400px] h-[600px] min-h-[600px] overflow-hidden bg-background sm:w-screen sm:max-w-none sm:min-w-0 sm:h-screen sm:min-h-0">
|
<div className="flex flex-col w-[400px] max-w-[400px] min-w-[400px] h-[600px] min-h-[600px] overflow-hidden bg-background sm:w-screen sm:max-w-none sm:min-w-0 sm:h-screen sm:min-h-0">
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<RouterContainer />
|
<RouterContainer />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import AppRoot from '@/providers/AppRoot';
|
import AppRoot from '@/providers/AppRoot';
|
||||||
import '@/index.css';
|
import '@/styles/shell.css';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RouterProvider defaultRoute="dashboard" syncKey="app/sidepanelRoute">
|
<RouterProvider defaultRoute="dashboard" syncKey="app/sidepanelRoute">
|
||||||
<div className="app flex flex-col h-screen w-full overflow-hidden">
|
<div className="flex flex-col h-screen w-full overflow-hidden">
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<RouterContainer />
|
<RouterContainer />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import AppRoot from '@/providers/AppRoot';
|
import AppRoot from '@/providers/AppRoot';
|
||||||
import '@/index.css';
|
import '@/styles/shell.css';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function SearchDropdown({
|
|||||||
const items = isSearching ? searchResults : recentFeatures;
|
const items = isSearching ? searchResults : recentFeatures;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute left-0 right-0 top-full z-50 mt-1.5 max-h-80 overflow-y-auto rounded-lg border border-border bg-popover text-popover-foreground shadow-lg animate-in fade-in slide-in-from-top-2 duration-150">
|
<div className="absolute left-0 right-0 top-full z-50 mt-1.5 max-h-80 overflow-y-auto rounded-lg border border-border bg-popover text-popover-foreground shadow-lg fade-in-slide-top-2">
|
||||||
<ul role="listbox" className="p-1.5">
|
<ul role="listbox" className="p-1.5">
|
||||||
{!isSearching && items.length > 0 && (
|
{!isSearching && items.length > 0 && (
|
||||||
<li className="px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">
|
<li className="px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const QrCodePreview = ({
|
|||||||
<img
|
<img
|
||||||
src={qrCodeDataUrl}
|
src={qrCodeDataUrl}
|
||||||
alt="QR Code Preview"
|
alt="QR Code Preview"
|
||||||
className="w-48 h-48 max-w-full object-contain block animate-in fade-in duration-300"
|
className="w-48 h-48 max-w-full object-contain block fade-in-300"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@config "../../tailwind.pages.config.js";
|
||||||
|
|
||||||
|
@tailwind utilities;
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@config "../../tailwind.shell.config.js";
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
@@ -90,3 +92,91 @@
|
|||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
display: none;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
declare module '*.css' {
|
||||||
|
const css: string;
|
||||||
|
export default css;
|
||||||
|
}
|
||||||
+1
-34
@@ -1,34 +1 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
export { default } from './tailwind.shell.config.js';
|
||||||
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: [],
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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: [],
|
||||||
|
};
|
||||||
@@ -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)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -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: [],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user