feat(docker-watcher): phase 14 - frontend polish & modern UI
Design system with CSS custom properties (light/dark themes). 38 Lucide SVG icon components. Dark mode with system preference. EN/RU localization with i18n store. Skeleton loaders, empty states, toggle switches, micro-interactions. Responsive sidebar with mobile hamburger menu. All pages polished with consistent styling.
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
/* ── Design Tokens ─────────────────────────────────────────────────────
|
||||
CSS custom properties for Docker Watcher design system.
|
||||
Task 1: Color palette, spacing scale, typography, border radius tokens.
|
||||
Task 12: Dark mode support via [data-theme="dark"] selector.
|
||||
───────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ── Brand Colors ───────────────────────────────────── */
|
||||
--color-brand-50: #eef2ff;
|
||||
--color-brand-100: #e0e7ff;
|
||||
--color-brand-200: #c7d2fe;
|
||||
--color-brand-300: #a5b4fc;
|
||||
--color-brand-400: #818cf8;
|
||||
--color-brand-500: #6366f1;
|
||||
--color-brand-600: #4f46e5;
|
||||
--color-brand-700: #4338ca;
|
||||
--color-brand-800: #3730a3;
|
||||
--color-brand-900: #312e81;
|
||||
|
||||
/* ── Semantic Colors ────────────────────────────────── */
|
||||
--color-success: #16a34a;
|
||||
--color-success-light: #dcfce7;
|
||||
--color-success-dark: #15803d;
|
||||
--color-warning: #d97706;
|
||||
--color-warning-light: #fef3c7;
|
||||
--color-warning-dark: #b45309;
|
||||
--color-danger: #dc2626;
|
||||
--color-danger-light: #fee2e2;
|
||||
--color-danger-dark: #b91c1c;
|
||||
--color-info: #2563eb;
|
||||
--color-info-light: #dbeafe;
|
||||
--color-info-dark: #1d4ed8;
|
||||
|
||||
/* ── Surface Colors (Light Mode) ────────────────────── */
|
||||
--surface-page: #f8fafc;
|
||||
--surface-card: #ffffff;
|
||||
--surface-card-hover: #f8fafc;
|
||||
--surface-sidebar: #ffffff;
|
||||
--surface-overlay: rgba(0, 0, 0, 0.3);
|
||||
--surface-input: #ffffff;
|
||||
|
||||
/* ── Border Colors ──────────────────────────────────── */
|
||||
--border-primary: #e2e8f0;
|
||||
--border-secondary: #f1f5f9;
|
||||
--border-focus: var(--color-brand-500);
|
||||
--border-input: #cbd5e1;
|
||||
|
||||
/* ── Text Colors ────────────────────────────────────── */
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-tertiary: #94a3b8;
|
||||
--text-inverse: #ffffff;
|
||||
--text-link: var(--color-brand-600);
|
||||
--text-link-hover: var(--color-brand-700);
|
||||
|
||||
/* ── Spacing Scale (4px base) ───────────────────────── */
|
||||
--space-0: 0;
|
||||
--space-1: 0.25rem; /* 4px */
|
||||
--space-2: 0.5rem; /* 8px */
|
||||
--space-3: 0.75rem; /* 12px */
|
||||
--space-4: 1rem; /* 16px */
|
||||
--space-5: 1.25rem; /* 20px */
|
||||
--space-6: 1.5rem; /* 24px */
|
||||
--space-8: 2rem; /* 32px */
|
||||
--space-10: 2.5rem; /* 40px */
|
||||
--space-12: 3rem; /* 48px */
|
||||
--space-16: 4rem; /* 64px */
|
||||
|
||||
/* ── Typography Scale ───────────────────────────────── */
|
||||
--font-family-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
--font-family-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;
|
||||
|
||||
--text-xs: 0.75rem; /* 12px */
|
||||
--text-sm: 0.875rem; /* 14px */
|
||||
--text-base: 1rem; /* 16px */
|
||||
--text-lg: 1.125rem; /* 18px */
|
||||
--text-xl: 1.25rem; /* 20px */
|
||||
--text-2xl: 1.5rem; /* 24px */
|
||||
--text-3xl: 1.875rem; /* 30px */
|
||||
|
||||
--leading-tight: 1.25;
|
||||
--leading-normal: 1.5;
|
||||
--leading-relaxed: 1.625;
|
||||
|
||||
--weight-normal: 400;
|
||||
--weight-medium: 500;
|
||||
--weight-semibold: 600;
|
||||
--weight-bold: 700;
|
||||
|
||||
/* ── Border Radius ──────────────────────────────────── */
|
||||
--radius-sm: 0.25rem; /* 4px */
|
||||
--radius-md: 0.375rem; /* 6px */
|
||||
--radius-lg: 0.5rem; /* 8px */
|
||||
--radius-xl: 0.75rem; /* 12px */
|
||||
--radius-2xl: 1rem; /* 16px */
|
||||
--radius-full: 9999px;
|
||||
|
||||
/* ── Shadows ────────────────────────────────────────── */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
/* ── Transitions ────────────────────────────────────── */
|
||||
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* ── Sidebar ────────────────────────────────────────── */
|
||||
--sidebar-width: 16rem; /* 256px */
|
||||
--sidebar-collapsed-width: 0; /* mobile collapsed */
|
||||
--topbar-height: 4rem; /* 64px */
|
||||
}
|
||||
|
||||
/* ── Dark Mode Tokens ─────────────────────────────────────────────── */
|
||||
|
||||
[data-theme="dark"] {
|
||||
--surface-page: #0f172a;
|
||||
--surface-card: #1e293b;
|
||||
--surface-card-hover: #334155;
|
||||
--surface-sidebar: #1e293b;
|
||||
--surface-overlay: rgba(0, 0, 0, 0.6);
|
||||
--surface-input: #1e293b;
|
||||
|
||||
--border-primary: #334155;
|
||||
--border-secondary: #1e293b;
|
||||
--border-focus: var(--color-brand-400);
|
||||
--border-input: #475569;
|
||||
|
||||
--text-primary: #f1f5f9;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-tertiary: #64748b;
|
||||
--text-inverse: #0f172a;
|
||||
--text-link: var(--color-brand-400);
|
||||
--text-link-hover: var(--color-brand-300);
|
||||
|
||||
--color-success: #22c55e;
|
||||
--color-success-light: #14532d;
|
||||
--color-warning: #f59e0b;
|
||||
--color-warning-light: #451a03;
|
||||
--color-danger: #ef4444;
|
||||
--color-danger-light: #450a0a;
|
||||
--color-info: #3b82f6;
|
||||
--color-info-light: #172554;
|
||||
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -4px rgba(0, 0, 0, 0.3);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 8px 10px -6px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* ── Animations ───────────────────────────────────────────────────── */
|
||||
|
||||
@keyframes pulse-status {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
@keyframes slide-in-right {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slide-out-right {
|
||||
from { transform: translateX(0); opacity: 1; }
|
||||
to { transform: translateX(100%); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes skeleton-shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes scale-in {
|
||||
from { transform: scale(0.95); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes button-press {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(0.97); }
|
||||
}
|
||||
|
||||
.animate-pulse-status {
|
||||
animation: pulse-status 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
.animate-slide-in {
|
||||
animation: slide-in-right var(--transition-slow) forwards;
|
||||
}
|
||||
|
||||
.animate-slide-out {
|
||||
animation: slide-out-right var(--transition-slow) forwards;
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in var(--transition-normal) forwards;
|
||||
}
|
||||
|
||||
.animate-scale-in {
|
||||
animation: scale-in var(--transition-normal) forwards;
|
||||
}
|
||||
|
||||
.active\:animate-press:active {
|
||||
animation: button-press 150ms ease-in-out;
|
||||
}
|
||||
|
||||
/* ── Skeleton Loader ──────────────────────────────────────────────── */
|
||||
|
||||
.skeleton {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--border-secondary) 25%,
|
||||
var(--border-primary) 50%,
|
||||
var(--border-secondary) 75%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
/* ── Toggle Switch ────────────────────────────────────────────────── */
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
width: 2.75rem;
|
||||
height: 1.5rem;
|
||||
background-color: var(--border-primary);
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.toggle-switch[aria-checked="true"] {
|
||||
background-color: var(--color-brand-600);
|
||||
}
|
||||
|
||||
.toggle-switch::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0.125rem;
|
||||
left: 0.125rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
background-color: white;
|
||||
border-radius: var(--radius-full);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform var(--transition-fast);
|
||||
}
|
||||
|
||||
.toggle-switch[aria-checked="true"]::after {
|
||||
transform: translateX(1.25rem);
|
||||
}
|
||||
Reference in New Issue
Block a user