feat!: complete visual redesign → 1.0.0
BREAKING: version bump to 1.0.0 New design system: - Emerald/teal color palette with semantic tokens (success, warning, destructive) - Light/dark mode toggle with localStorage persistence and anti-FOUC - CSS custom properties via Tailwind darkMode: 'class' - Zero new dependencies — all primitives hand-rolled Foundation: - 70+ inline SVG icons (no external icon library) - ThemeProvider + useTheme hook - cn() utility for conditional classes UI primitives (web/src/components/ui/): - Button (6 variants, 4 sizes) - Input, Label, Card with slots, Badge (6 variants + sizes) - Switch (animated), Checkbox, Separator - Skeleton, Spinner, EmptyState - Toast system with auto-dismiss (success/error/warning/info) - Dialog with focus trap + escape/click-outside - ConfirmDialog replacing native confirm() - DropdownMenu with keyboard support Layout & navigation: - Sidebar with icon+label nav, active indicator, responsive drawer on mobile - Topbar for <lg viewports with hamburger - PageHeader with title/description/actions pattern - UserMenu with avatar + dropdown (change password, logout) - DirtyBanner redesigned with semantic styling Pages redesigned: - Login: centered card with gradient background - Dashboard: 4 KPI tiles, disk usage bars, services grid, recent activity - Users: table with search, empty state, edit modal - Files: breadcrumbs, drag-drop upload zone, SVG file icons, rename/delete - Samba: card list with badges, edit modal with PathField - NFS: card list with client chips, client editor - Storage: tabs (Mover/SnapRAID/Jobs), real-time job log streaming - Log: timeline grouped by date with filters - Settings: watched mounts, import actions, system info - NotFound: friendly 404 page Polish: - Toast notifications replace all native alert()/confirm() - Tailwind animations (dialog-enter, toast-enter/leave, dropdown-enter) - Custom scrollbar styling - Focus-visible rings - Skeleton loading states across all pages
This commit is contained in:
+103
-6
@@ -1,17 +1,114 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: {
|
||||
50: "#eff6ff",
|
||||
500: "#3b82f6",
|
||||
600: "#2563eb",
|
||||
700: "#1d4ed8",
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
success: {
|
||||
DEFAULT: "hsl(var(--success))",
|
||||
foreground: "hsl(var(--success-foreground))",
|
||||
},
|
||||
warning: {
|
||||
DEFAULT: "hsl(var(--warning))",
|
||||
foreground: "hsl(var(--warning-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
brand: {
|
||||
50: "hsl(var(--brand-50))",
|
||||
100: "hsl(var(--brand-100))",
|
||||
200: "hsl(var(--brand-200))",
|
||||
300: "hsl(var(--brand-300))",
|
||||
400: "hsl(var(--brand-400))",
|
||||
500: "hsl(var(--brand-500))",
|
||||
600: "hsl(var(--brand-600))",
|
||||
700: "hsl(var(--brand-700))",
|
||||
800: "hsl(var(--brand-800))",
|
||||
900: "hsl(var(--brand-900))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" },
|
||||
},
|
||||
"toast-enter": {
|
||||
from: { opacity: "0", transform: "translateX(100%)" },
|
||||
to: { opacity: "1", transform: "translateX(0)" },
|
||||
},
|
||||
"toast-leave": {
|
||||
from: { opacity: "1", transform: "translateX(0)" },
|
||||
to: { opacity: "0", transform: "translateX(100%)" },
|
||||
},
|
||||
"dialog-enter": {
|
||||
from: { opacity: "0", transform: "scale(0.95)" },
|
||||
to: { opacity: "1", transform: "scale(1)" },
|
||||
},
|
||||
"dropdown-enter": {
|
||||
from: { opacity: "0", transform: "translateY(-4px)" },
|
||||
to: { opacity: "1", transform: "translateY(0)" },
|
||||
},
|
||||
"skeleton-pulse": {
|
||||
"0%, 100%": { opacity: "1" },
|
||||
"50%": { opacity: "0.4" },
|
||||
},
|
||||
spin: {
|
||||
from: { transform: "rotate(0deg)" },
|
||||
to: { transform: "rotate(360deg)" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"toast-enter": "toast-enter 0.3s ease-out",
|
||||
"toast-leave": "toast-leave 0.2s ease-in forwards",
|
||||
"dialog-enter": "dialog-enter 0.2s ease-out",
|
||||
"dropdown-enter": "dropdown-enter 0.15s ease-out",
|
||||
"skeleton-pulse": "skeleton-pulse 1.5s ease-in-out infinite",
|
||||
spin: "spin 1s linear infinite",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user