From 0e994a572ef719e814c5b4e713dd8a3be3cb9d48 Mon Sep 17 00:00:00 2001 From: host Date: Sun, 9 Aug 2026 18:04:36 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20=D1=82=D1=91?= =?UTF-8?q?=D0=BC=D0=BD=D0=B0=D1=8F=20=D1=82=D0=B5=D0=BC=D0=B0=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/styles.css | 910 ++++------------------------------------ 1 file changed, 83 insertions(+), 827 deletions(-) diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 9929688..9f7fc91 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1,889 +1,145 @@ /* ============================================================ - Proxmox VPS Panel — UI - Linear / Vercel-style: чистые формы, тонкие тени, Inter, - один акцент, без рамок, много воздуха. - Dashboard-стиль с боковой навигацией. + Тёмная тема ============================================================ */ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); - :root { - /* Базовые цвета — нейтральная светлая серая шкала */ - --bg: #fafafa; - --surface: #ffffff; - --surface-2: #f5f5f5; - --surface-hover: #fafafa; + --bg: #101217; + --surface: #181b22; + --surface-2: #222631; + --surface-hover: #292e3a; - /* Текст */ - --text: #0a0a0a; - --text-muted: #6b7280; - --text-dim: #9ca3af; + --text: #f1f5f9; + --text-muted: #a7b0c0; + --text-dim: #70798a; - /* Границы */ - --border: rgba(0, 0, 0, 0.06); - --border-soft: rgba(0, 0, 0, 0.04); + --border: rgba(255, 255, 255, 0.10); + --border-soft: rgba(255, 255, 255, 0.07); - /* Один акцент */ - --accent: #0070f3; - --accent-hover: #005ad1; - --accent-soft: rgba(0, 112, 243, 0.08); + --accent: #3b82f6; + --accent-hover: #60a5fa; + --accent-soft: rgba(59, 130, 246, 0.18); --accent-text: #ffffff; - /* Семантические цвета */ - --success: #10b981; - --success-soft: rgba(16, 185, 129, 0.08); - --warn: #f59e0b; - --warn-soft: rgba(245, 158, 11, 0.08); - --danger: #ef4444; - --danger-soft: rgba(239, 68, 68, 0.08); + --success: #34d399; + --success-soft: rgba(52, 211, 153, 0.16); + --warn: #fbbf24; + --warn-soft: rgba(251, 191, 36, 0.16); + --danger: #f87171; + --danger-soft: rgba(248, 113, 113, 0.16); - /* Тени */ - --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04); - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.03); - --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.06), 0 2px 4px -1px rgba(0, 0, 0, 0.04); - --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.06), 0 4px 6px -2px rgba(0, 0, 0, 0.04); - - /* Радиусы */ - --radius-sm: 6px; - --radius: 8px; - --radius-lg: 12px; - --radius-xl: 16px; - - --font-body: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif; + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.25); + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.24); + --shadow-md: 0 6px 16px rgba(0, 0, 0, 0.30); + --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.36); } -* { - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -html, body { - margin: 0; - padding: 0; +body { background: var(--bg); color: var(--text); - font-family: var(--font-body); - font-size: 14px; - line-height: 1.5; - font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; } -button, input, select, textarea { - font-family: inherit; - font-size: inherit; - color: inherit; +.sidebar, +.main { + background: var(--bg); } -a { - color: var(--accent); - text-decoration: none; - transition: color 0.15s ease; -} -a:hover { color: var(--accent-hover); } - -/* ============================================================ - Layout shell — горизонтальный: sidebar + main - ============================================================ */ - -.shell { - min-height: 100vh; - display: flex; - flex-direction: row; - align-items: stretch; -} - -/* ============================================================ - Sidebar — боковая навигация - ============================================================ */ - .sidebar { - width: 240px; - background: var(--surface); - display: flex; - flex-direction: column; - flex-shrink: 0; - border-right: 1px solid var(--border-soft); - height: 100vh; - position: sticky; - top: 0; -} - -.sidebar-brand { - display: flex; - align-items: center; - gap: 10px; - padding: 22px 20px; - border-bottom: 1px solid var(--border-soft); -} - -.sidebar-brand-mark { - width: 32px; - height: 32px; - border-radius: 8px; - background: var(--accent); - color: var(--accent-text); - display: flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 13px; - box-shadow: 0 1px 3px rgba(0, 112, 243, 0.3); -} - -.sidebar-brand-text { - font-weight: 600; - font-size: 15px; - letter-spacing: -0.01em; - color: var(--text); -} - -.sidebar-nav { - display: flex; - flex-direction: column; - padding: 12px 12px; - gap: 2px; - flex: 1; -} - -.sidebar-nav .nav-link { - display: flex; - align-items: center; - gap: 10px; - padding: 9px 12px; - border-radius: var(--radius-sm); - color: var(--text-muted); - font-size: 14px; - font-weight: 500; - transition: all 0.15s ease; - text-decoration: none; -} - -.sidebar-nav .nav-link:hover { - background: var(--surface-2); - color: var(--text); -} - -.sidebar-nav .nav-link.active { - background: var(--accent-soft); - color: var(--accent); - font-weight: 600; -} - -.sidebar-nav .nav-link svg { - flex-shrink: 0; + border-right-color: var(--border-soft); } +.sidebar-brand, .sidebar-footer { - padding: 14px; - border-top: 1px solid var(--border-soft); - display: flex; - flex-direction: column; - gap: 10px; + border-color: var(--border-soft); } -.sidebar-user { - display: flex; - align-items: center; - gap: 10px; - padding: 8px; - border-radius: var(--radius-sm); -} - -.sidebar-user-avatar { - width: 32px; - height: 32px; - border-radius: 50%; - background: var(--surface-2); - color: var(--text); - display: flex; - align-items: center; - justify-content: center; - font-weight: 600; - font-size: 13px; - flex-shrink: 0; -} - -.sidebar-user-info { - min-width: 0; - flex: 1; -} - -.sidebar-user-email { - font-size: 13px; - font-weight: 500; - color: var(--text); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.sidebar-user-role { - font-size: 11px; - color: var(--text-muted); -} - -.btn-logout { - width: 100%; - background: transparent; - color: var(--text-muted); - box-shadow: none; - font-weight: 500; -} - -.btn-logout:hover { - background: var(--danger-soft); - color: var(--danger); - box-shadow: none; -} - -/* ============================================================ - Main + container - ============================================================ */ - -.main { - flex: 1; - min-width: 0; - background: var(--bg); -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 40px 32px 80px; - width: 100%; -} - -.dashboard-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 28px; - flex-wrap: wrap; - gap: 16px; -} - -.page-title { - font-size: 28px; - font-weight: 600; - letter-spacing: -0.02em; - margin: 0 0 6px; +.sidebar-brand-text, +.sidebar-user-email, +.page-title, +.stat-card-value, +.os-name, +.instance-name, +.instance-name a, +.empty-title { color: var(--text); } -.page-sub { - color: var(--text-muted); - margin: 0; - font-size: 14px; -} - -/* ============================================================ - Stats cards — виджеты на дашборде - ============================================================ */ - -.stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 16px; - margin-bottom: 28px; -} - -.stat-card { - display: flex; - align-items: center; - gap: 14px; - padding: 18px 20px; +.card, +.stat-card, +.instance-card, +.empty-state, +table.data-table { background: var(--surface); - border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); - transition: box-shadow 0.15s ease; } -.stat-card:hover { - box-shadow: var(--shadow-md); -} - -.stat-card-icon { - width: 42px; - height: 42px; - border-radius: var(--radius); - background: var(--surface-2); - color: var(--text-muted); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.stat-success .stat-card-icon { - background: var(--success-soft); - color: var(--success); -} - -.stat-muted .stat-card-icon { - background: var(--surface-2); - color: var(--text-dim); -} - -.stat-danger .stat-card-icon { - background: var(--danger-soft); - color: var(--danger); -} - -.stat-card-body { min-width: 0; } - -.stat-card-label { - font-size: 12px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.04em; - font-weight: 500; -} - -.stat-card-value { - font-size: 24px; - font-weight: 600; - color: var(--text); - letter-spacing: -0.02em; - line-height: 1.2; - margin-top: 2px; -} - -/* ============================================================ - Buttons — мягкие тени, без рамок - ============================================================ */ - +.field input, +.field select, +.field textarea, +.preset-select, +.os-card, +.preset-btn, .btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - padding: 10px 16px; - border-radius: var(--radius-sm); - border: none; - background: var(--surface); + background: var(--surface-2); color: var(--text); - cursor: pointer; - font-size: 14px; - font-weight: 500; - box-shadow: var(--shadow-xs); - transition: all 0.15s ease; - user-select: none; + border-color: var(--border); } + +.field input:hover, +.field select:hover, +.field textarea:hover, +.os-card:hover:not(.disabled), +.preset-btn:hover:not(.disabled), .btn:hover { background: var(--surface-hover); - box-shadow: var(--shadow-sm); -} -.btn:active { transform: scale(0.98); } -.btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.btn:disabled { - opacity: 0.5; - cursor: not-allowed; } -.btn-primary { - background: var(--accent); - color: var(--accent-text); - box-shadow: var(--shadow-sm); -} -.btn-primary:hover { - background: var(--accent-hover); - box-shadow: var(--shadow-md); -} - -.btn-danger { - background: var(--danger-soft); - color: var(--danger); -} -.btn-danger:hover { - background: var(--danger); - color: white; -} - -.btn-sm { - padding: 6px 12px; - font-size: 13px; -} - -/* ============================================================ - Cards — без рамок, только тени - ============================================================ */ - -.card { +.field input:focus, +.field select:focus, +.field textarea:focus { background: var(--surface); - border-radius: var(--radius-lg); - padding: 24px; - box-shadow: var(--shadow-sm); } -.card + .card { margin-top: 16px; } - -/* ============================================================ - Create form - ============================================================ */ - -.create-form { - margin-bottom: 24px; -} - -.form-section { - margin-bottom: 22px; -} - -.form-label { - display: block; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--text-muted); - margin-bottom: 10px; -} - -.form-row { - display: grid; - grid-template-columns: 2fr 1fr 1fr; - gap: 14px; - margin-bottom: 18px; -} - -.os-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 10px; -} - -.os-card { - display: flex; - align-items: center; - gap: 12px; - padding: 14px 16px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - cursor: pointer; - transition: all 0.15s ease; - text-align: left; - font-family: inherit; - color: var(--text); -} - -.os-card:hover:not(.disabled) { +table.data-table th { background: var(--surface-2); - border-color: var(--accent-dim); -} - -.os-card.active { - background: var(--accent-soft); - border-color: var(--accent); -} - -.os-card.disabled { - opacity: 0.4; - cursor: not-allowed; -} - -.os-name { - font-weight: 600; - font-size: 14px; - color: var(--text); -} - -.os-desc { - font-size: 12px; color: var(--text-muted); - margin-top: 1px; + border-bottom-color: var(--border); } -.preset-row { - display: flex; - gap: 12px; - flex-wrap: wrap; - align-items: stretch; +table.data-table td { + border-bottom-color: var(--border-soft); } -.preset-select { - flex: 1; - min-width: 220px; - padding: 11px 14px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--surface); - font-size: 13px; - color: var(--text); - cursor: pointer; - transition: border-color 0.15s ease; -} - -.preset-select:hover { border-color: var(--accent-dim); } -.preset-select:focus { outline: none; border-color: var(--accent); } - -.preset-buttons { - display: flex; - gap: 8px; -} - -.preset-btn { - min-width: 60px; - padding: 8px 14px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - text-align: center; - font-family: inherit; - color: var(--text); - transition: all 0.15s ease; -} - -.preset-btn:hover:not(.disabled) { - background: var(--surface-2); - border-color: var(--accent-dim); -} - -.preset-btn.active { - background: var(--accent); - border-color: var(--accent); - color: var(--accent-text); -} - -.preset-btn.disabled { - opacity: 0.35; - cursor: not-allowed; -} - -.preset-label { - font-weight: 700; - font-size: 15px; -} - -.preset-status { - font-size: 11px; - color: var(--text-muted); - margin-top: 1px; -} - -.preset-btn.active .preset-status { color: var(--accent-text); } - -.submit-btn { - width: 100%; - padding: 12px; - font-size: 15px; - font-weight: 600; -} - -/* ============================================================ - Forms — старые стили (для совместимости) - ============================================================ */ - -.field { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 18px; -} -.field label { - font-size: 13px; - color: var(--text-muted); - font-weight: 500; -} -.field input, .field select, .field textarea { - background: var(--surface-2); - border: none; - border-radius: var(--radius-sm); - padding: 11px 14px; - color: var(--text); - font-size: 14px; - transition: all 0.15s ease; -} -.field input:hover, .field select:hover { +table.data-table tr:hover td { background: var(--surface-hover); } -.field input:focus, .field select:focus, .field textarea:focus { - outline: none; - background: var(--surface); - box-shadow: 0 0 0 3px var(--accent-soft); -} -.field input::placeholder { color: var(--text-dim); } -/* ============================================================ - Banners - ============================================================ */ - -.error-box { - background: var(--danger-soft); - color: var(--danger); - padding: 12px 16px; - border-radius: var(--radius); - font-size: 14px; - margin-bottom: 16px; -} - -.success-box { - background: var(--success-soft); - color: var(--success); - padding: 12px 16px; - border-radius: var(--radius); - font-size: 14px; - margin-bottom: 16px; -} - -/* ============================================================ - Auth - ============================================================ */ - -.auth-wrap { - max-width: 400px; - margin: 100px auto; -} -.auth-wrap .card { padding: 36px; } -.auth-wrap .page-title { text-align: center; } -.auth-wrap .page-sub { text-align: center; margin-bottom: 28px; } -.auth-wrap .btn { width: 100%; padding: 12px; font-size: 15px; } - -/* ============================================================ - Instance grid - ============================================================ */ - -.instance-grid { +.template-results { display: grid; - grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); - gap: 20px; + gap: 24px; + margin-top: 18px; } -.instance-card { - background: var(--surface); - border-radius: var(--radius-lg); - padding: 22px; - display: flex; - flex-direction: column; - gap: 14px; - box-shadow: var(--shadow-sm); - transition: box-shadow 0.2s ease; -} -.instance-card:hover { - box-shadow: var(--shadow-md); -} - -/* ============================================================ - Статусы — цветовая кодировка - ============================================================ */ - -.status-row { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - font-weight: 500; - color: var(--text-muted); -} - -.dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--text-dim); - flex-shrink: 0; -} -.dot.running { - background: var(--success); - box-shadow: 0 0 0 3px var(--success-soft); -} -.dot.stopped { background: var(--text-dim); } -.dot.creating { - background: var(--warn); - animation: pulse 1.4s infinite; - box-shadow: 0 0 0 3px var(--warn-soft); -} -.dot.error { - background: var(--danger); - box-shadow: 0 0 0 3px var(--danger-soft); -} - -@keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.3; } -} - -.instance-name { - font-size: 17px; - font-weight: 600; +.template-results section h4 { color: var(--text); - letter-spacing: -0.01em; + margin: 0 0 10px; } -.instance-name a { - color: var(--text); - transition: color 0.15s ease; -} -.instance-name a:hover { color: var(--accent); } -.instance-meta { - font-size: 13px; +.template-form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; +} + +.template-form { + margin: 24px 0 32px; +} + +.muted { color: var(--text-muted); - line-height: 1.7; } -.instance-actions { - display: flex; - gap: 8px; - flex-wrap: wrap; - margin-top: 8px; -} - -/* ============================================================ - Empty state - ============================================================ */ - -.empty-state { - text-align: center; - padding: 80px 20px; - color: var(--text-muted); - background: var(--surface); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-xs); -} - -.empty-icon { font-size: 48px; margin-bottom: 12px; opacity: 0.7; } -.empty-title { font-size: 17px; font-weight: 600; color: var(--text); margin-bottom: 6px; } -.empty-sub { font-size: 14px; color: var(--text-muted); } - -/* ============================================================ - Tabs - ============================================================ */ - -.nav-tabs { - display: flex; - gap: 8px; - margin-bottom: 28px; -} -.nav-tab { - padding: 10px 16px; - font-size: 14px; - font-weight: 500; - color: var(--text-muted); - cursor: pointer; - border-radius: var(--radius-sm); - transition: all 0.15s ease; -} -.nav-tab:hover { - background: var(--surface); - color: var(--text); -} -.nav-tab.active { - background: var(--accent); - color: var(--accent-text); -} - -/* ============================================================ - Tables - ============================================================ */ - -table.data-table { - width: 100%; - border-collapse: separate; - border-spacing: 0; - font-size: 14px; - background: var(--surface); - border-radius: var(--radius-lg); - overflow: hidden; - box-shadow: var(--shadow-sm); -} -table.data-table th, table.data-table td { - text-align: left; - padding: 14px 18px; -} -table.data-table th { - color: var(--text-muted); - font-weight: 500; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.04em; - background: var(--surface-2); - border-bottom: 1px solid var(--border-soft); -} -table.data-table td { - border-bottom: 1px solid var(--border-soft); -} -table.data-table tr:last-child td { border-bottom: none; } -table.data-table tr:hover td { background: var(--surface-2); } - -/* ============================================================ - Inline stats - ============================================================ */ - -.metric { - display: inline-flex; - align-items: center; - gap: 8px; - font-size: 13px; - color: var(--text); - font-weight: 500; -} -.metric-bar { - width: 70px; - height: 6px; - background: var(--surface-2); - border-radius: 3px; - overflow: hidden; -} -.metric-bar > span { - display: block; - height: 100%; - border-radius: 3px; - transition: width 0.5s ease; -} -.metric-bar > span.good { background: var(--success); } -.metric-bar > span.warn { background: var(--warn); } -.metric-bar > span.bad { background: var(--danger); } - -/* ============================================================ - Badges - ============================================================ */ - -.badge { - display: inline-flex; - align-items: center; - font-size: 12px; - font-weight: 500; - padding: 3px 10px; - border-radius: 100px; - color: var(--text-muted); - background: var(--surface-2); -} - -/* ============================================================ - Mobile — sidebar превращается в top-bar - ============================================================ */ - @media (max-width: 768px) { - .shell { flex-direction: column; } - .sidebar { - width: 100%; - height: auto; - position: relative; - flex-direction: row; - border-right: none; - border-bottom: 1px solid var(--border-soft); - overflow-x: auto; + .template-form-grid { + grid-template-columns: 1fr; } - .sidebar-brand { display: none; } - .sidebar-nav { - flex-direction: row; - padding: 8px; - flex-shrink: 0; - } - .sidebar-footer { - flex-direction: row; - border-top: none; - border-left: 1px solid var(--border-soft); - flex-shrink: 0; - } - .sidebar-user { padding: 4px 8px; } - .container { padding: 24px 20px 64px; } - .form-row { grid-template-columns: 1fr; } - .stats-grid { grid-template-columns: repeat(2, 1fr); } - .page-title { font-size: 22px; } }