From fbec0cc08aa9d9f6da733d8a02c1b8485ea6cfd9 Mon Sep 17 00:00:00 2001 From: Sergey Karmanov Date: Thu, 14 May 2026 02:44:44 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=91=D0=BB=20=D1=86=D0=B2=D0=B5=D1=82=D0=B0=20=D0=BD=D0=B0=20?= =?UTF-8?q?CSS-=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/assets/icons/coin.svg | 2 +- frontend/src/assets/main.css | 202 ++++++++++++++---- .../src/components/layout/AppBottomNav.vue | 2 +- frontend/src/components/layout/AppSidebar.vue | 8 +- frontend/src/components/layout/AppTopbar.vue | 34 +-- .../src/components/ui/AchievementBadge.vue | 8 +- frontend/src/components/ui/CoinChip.vue | 12 +- frontend/src/components/ui/DataTable.vue | 4 +- frontend/src/components/ui/GlassCard.vue | 2 +- frontend/src/components/ui/LectureCard.vue | 14 +- frontend/src/components/ui/LoadingSpinner.vue | 2 +- frontend/src/components/ui/ModalDialog.vue | 4 +- frontend/src/components/ui/ProgressBar.vue | 6 +- frontend/src/components/ui/SearchInput.vue | 4 +- frontend/src/components/ui/StatsWidget.vue | 8 +- frontend/src/components/ui/StatusBadge.vue | 8 +- .../src/components/ui/ToastNotification.vue | 8 +- .../src/views/admin/AdminDashboardView.vue | 6 +- .../src/views/admin/AdminLecturesView.vue | 18 +- frontend/src/views/student/ProfileView.vue | 2 +- 20 files changed, 240 insertions(+), 114 deletions(-) diff --git a/frontend/src/assets/icons/coin.svg b/frontend/src/assets/icons/coin.svg index d236fe0..60eb83b 100644 --- a/frontend/src/assets/icons/coin.svg +++ b/frontend/src/assets/icons/coin.svg @@ -5,5 +5,5 @@ version: "1.3" unicode: "eb82" --> - + \ No newline at end of file diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 011f161..c1d9853 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -1,28 +1,154 @@ -/* UniVerse – Aero Green Design System */ :root { + --color-white: #FFFFFF; + --color-black: #000000; + --color-primary: #22C55E; --color-primary-dark: #16A34A; --color-primary-light: #86EFAC; + --color-primary-bright: #4ADE80; + --color-primary-border: #15803D; + --color-aqua: #06B6D4; + --color-aqua-dark: #0E7490; + --color-aqua-light: #67E8F9; + --color-sky: #7DD3FC; + --color-sky-light: #BAE6FD; + --color-white-glass: rgba(255, 255, 255, 0.75); --color-surface: rgba(255, 255, 255, 0.85); --color-border-glass: rgba(255, 255, 255, 0.8); + --color-text: #1E293B; --color-text-secondary: #64748B; + --color-bg-start: #E0F2FE; --color-bg-mid: #DCFCE7; + --color-error: #EF4444; + --color-error-dark: #DC2626; + --color-error-border: #B91C1C; + --color-success: #22C55E; + --color-success-text: #166534; + --color-warning: #F59E0B; - --gradient-bg: linear-gradient(135deg, #E0F2FE 0%, #DCFCE7 50%, #E0F2FE 100%); - --gradient-brand: linear-gradient(135deg, #22C55E 0%, #06B6D4 60%, #7DD3FC 100%); + --color-warning-text: #92400E; + --color-warning-border: #FDE68A; + + --color-brown-dark: #78350F; + --color-danger-text: #991B1B; + --color-danger-light: #FCA5A5; + --color-danger-pale: #FECACA; + --color-info-text: #1E40AF; + --color-info-border: #93C5FD; + + --color-orange: #FB923C; + --color-orange-deep: #EA580C; + --color-orange-dark: #C2410C; + --color-yellow: #FCD34D; + + --color-purple: #A78BFA; + --color-purple-light: #C4B5FD; + --color-purple-dark: #6D28D9; + + --color-gray-400: #9CA3AF; + --color-star: #FBBF24; + + --color-white-a10: rgba(255,255,255,0.1); + --color-white-a30: rgba(255,255,255,0.3); + --color-white-a40: rgba(255,255,255,0.4); + --color-white-a50: rgba(255,255,255,0.5); + --color-white-a60: rgba(255,255,255,0.6); + --color-white-a70: rgba(255,255,255,0.7); + --color-white-a72: rgba(255,255,255,0.72); + --color-white-a80: rgba(255,255,255,0.8); + --color-white-a82: rgba(255,255,255,0.82); + --color-white-a86: rgba(255,255,255,0.86); + --color-white-a90: rgba(255,255,255,0.9); + --color-white-a96: rgba(255,255,255,0.96); + + --color-black-a04: rgba(0,0,0,0.04); + --color-black-a05: rgba(0,0,0,0.05); + --color-black-a06: rgba(0,0,0,0.06); + --color-black-a08: rgba(0,0,0,0.08); + --color-black-a12: rgba(0,0,0,0.12); + --color-black-a15: rgba(0,0,0,0.15); + --color-black-a20: rgba(0,0,0,0.2); + --color-black-a35: rgba(0,0,0,0.35); + + --color-slate-900-a08: rgba(15,23,42,0.08); + --color-slate-900-a14: rgba(15,23,42,0.14); + --color-slate-500-a10: rgba(100,116,139,0.1); + --color-slate-500-a20: rgba(100,116,139,0.2); + + --color-primary-a05: rgba(34,197,94,0.05); + --color-primary-a08: rgba(34,197,94,0.08); + --color-primary-a10: rgba(34,197,94,0.1); + --color-primary-a12: rgba(34,197,94,0.12); + --color-primary-a15: rgba(34,197,94,0.15); + --color-primary-a18: rgba(34,197,94,0.18); + --color-primary-a20: rgba(34,197,94,0.2); + --color-primary-a25: rgba(34,197,94,0.25); + --color-primary-a30: rgba(34,197,94,0.3); + --color-primary-a40: rgba(34,197,94,0.4); + --color-primary-a45: rgba(34,197,94,0.45); + --color-primary-a50: rgba(34,197,94,0.5); + --color-primary-light-a12: rgba(134,239,172,0.12); + + --color-error-a10: rgba(239,68,68,0.1); + --color-error-a12: rgba(239,68,68,0.12); + --color-error-a20: rgba(239,68,68,0.2); + --color-error-a24: rgba(239,68,68,0.24); + --color-error-a30: rgba(239,68,68,0.3); + --color-error-a40: rgba(239,68,68,0.4); + + --color-aqua-a15: rgba(6,182,212,0.15); + --color-aqua-a30: rgba(6,182,212,0.3); + --color-orange-a15: rgba(251,146,60,0.15); + --color-orange-a30: rgba(251,146,60,0.3); + --color-purple-a12: rgba(139,92,246,0.12); + --color-purple-a20: rgba(139,92,246,0.2); + --color-star-a15: rgba(251,191,36,0.15); + --color-star-a20: rgba(251,191,36,0.2); + --color-star-a30: rgba(251,191,36,0.3); + --color-warning-a15: rgba(245,158,11,0.15); + --color-warning-a25: rgba(245,158,11,0.25); + --color-warning-a40: rgba(245,158,11,0.4); + + --color-success-bg-a90: rgba(220,252,231,0.9); + --color-success-bg-a95: rgba(220,252,231,0.95); + --color-info-bg-a90: rgba(224,242,254,0.9); + --color-info-bg-a95: rgba(224,242,254,0.95); + --color-danger-bg-a68: rgba(254,242,242,0.68); + --color-danger-bg-a90: rgba(254,226,226,0.9); + --color-danger-bg-a95: rgba(254,226,226,0.95); + --color-warning-bg-a90: rgba(254,243,199,0.9); + + --gradient-bg: linear-gradient(135deg, var(--color-bg-start) 0%, var(--color-bg-mid) 50%, var(--color-bg-start) 100%); + --gradient-brand: linear-gradient(135deg, var(--color-primary) 0%, var(--color-aqua) 60%, var(--color-sky) 100%); + --gradient-progress-success: linear-gradient(90deg, var(--color-primary), var(--color-primary-light)); + --gradient-progress-neutral: linear-gradient(90deg, var(--color-sky), var(--color-sky-light)); + --gradient-progress-danger: linear-gradient(90deg, var(--color-danger-light), var(--color-danger-pale)); + --gradient-bar-success-vertical: linear-gradient(180deg, var(--color-primary), var(--color-primary-light)); + --gradient-bar-neutral-vertical: linear-gradient(180deg, var(--color-sky), var(--color-sky-light)); + --gradient-nav-active: linear-gradient(135deg, var(--color-primary-a18), var(--color-primary-light-a12)); + --gradient-btn-primary: linear-gradient(180deg, var(--color-primary-bright) 0%, var(--color-primary) 50%, var(--color-primary-dark) 100%); + --gradient-btn-danger: linear-gradient(180deg, var(--color-danger-light) 0%, var(--color-error) 50%, var(--color-error-dark) 100%); + --gradient-btn-shine: linear-gradient(180deg, var(--color-white-a40) 0%, var(--color-white-a10) 100%); + --gradient-stats-green: linear-gradient(90deg, var(--color-primary), var(--color-primary-light)); + --gradient-stats-aqua: linear-gradient(90deg, var(--color-aqua), var(--color-aqua-light)); + --gradient-stats-orange: linear-gradient(90deg, var(--color-orange), var(--color-yellow)); + --gradient-stats-purple: linear-gradient(90deg, var(--color-purple), var(--color-purple-light)); + --gradient-coin-chip: linear-gradient(135deg, var(--color-star-a20), var(--color-warning-a15)); + --gradient-coin-chip-hover: linear-gradient(135deg, var(--color-star-a30), var(--color-warning-a25)); + --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; - --shadow-glass: 0 8px 32px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.9); - --shadow-card: 0 4px 16px rgba(0, 0, 0, 0.06); + --shadow-glass: 0 8px 32px var(--color-black-a08), inset 0 1px 0 var(--color-white-a90); + --shadow-card: 0 4px 16px var(--color-black-a06); --sidebar-width: 240px; --topbar-height: 60px; } @@ -67,9 +193,9 @@ input, textarea, select { /* Scrollbar */ ::-webkit-scrollbar { width: 6px; height: 6px; } -::-webkit-scrollbar-track { background: rgba(0,0,0,0.04); border-radius: 3px; } -::-webkit-scrollbar-thumb { background: rgba(34,197,94,0.3); border-radius: 3px; } -::-webkit-scrollbar-thumb:hover { background: rgba(34,197,94,0.5); } +::-webkit-scrollbar-track { background: var(--color-black-a04); border-radius: 3px; } +::-webkit-scrollbar-thumb { background: var(--color-primary-a30); border-radius: 3px; } +::-webkit-scrollbar-thumb:hover { background: var(--color-primary-a50); } /* Glass panel utility */ .glass-panel { @@ -83,10 +209,10 @@ input, textarea, select { /* Primary glossy button */ .btn-primary { - background: linear-gradient(180deg, #4ADE80 0%, #22C55E 50%, #16A34A 100%); - border: 1px solid #15803D; + background: var(--gradient-btn-primary); + border: 1px solid var(--color-primary-border); border-radius: var(--radius-sm); - color: white; + color: var(--color-white); padding: 10px 20px; font-weight: 600; font-size: 14px; @@ -103,13 +229,13 @@ input, textarea, select { position: absolute; top: 0; left: 0; right: 0; height: 50%; - background: linear-gradient(180deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0.1) 100%); + background: var(--gradient-btn-shine); border-radius: 8px 8px 0 0; pointer-events: none; } .btn-primary:hover { transform: translateY(-1px); - box-shadow: 0 6px 20px rgba(34,197,94,0.4); + box-shadow: 0 6px 20px var(--color-primary-a40); } .btn-primary:active { transform: translateY(0); @@ -127,8 +253,8 @@ input, textarea, select { /* Secondary button */ .btn-secondary { - background: rgba(255,255,255,0.7); - border: 1px solid rgba(34,197,94,0.4); + background: var(--color-white-a70); + border: 1px solid var(--color-primary-a40); border-radius: var(--radius-sm); color: var(--color-primary-dark); padding: 10px 20px; @@ -142,16 +268,16 @@ input, textarea, select { backdrop-filter: blur(8px); } .btn-secondary:hover { - background: rgba(34,197,94,0.1); + background: var(--color-primary-a10); border-color: var(--color-primary); } /* Danger button */ .btn-danger { - background: linear-gradient(180deg, #FCA5A5 0%, #EF4444 50%, #DC2626 100%); - border: 1px solid #B91C1C; + background: var(--gradient-btn-danger); + border: 1px solid var(--color-error-border); border-radius: var(--radius-sm); - color: white; + color: var(--color-white); padding: 10px 20px; font-weight: 600; font-size: 14px; @@ -163,7 +289,7 @@ input, textarea, select { } .btn-danger:hover { transform: translateY(-1px); - box-shadow: 0 6px 20px rgba(239,68,68,0.4); + box-shadow: 0 6px 20px var(--color-error-a40); } /* Ghost button */ @@ -182,14 +308,14 @@ input, textarea, select { gap: 6px; } .btn-ghost:hover { - background: rgba(0,0,0,0.05); + background: var(--color-black-a05); color: var(--color-text); } /* Glass input */ .glass-input { - background: rgba(255,255,255,0.6); - border: 1px solid rgba(255,255,255,0.8); + background: var(--color-white-a60); + border: 1px solid var(--color-border-glass); border-radius: var(--radius-sm); padding: 10px 14px; font-size: 14px; @@ -199,9 +325,9 @@ input, textarea, select { backdrop-filter: blur(8px); } .glass-input:focus { - background: rgba(255,255,255,0.85); + background: var(--color-surface); border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(34,197,94,0.15); + box-shadow: 0 0 0 3px var(--color-primary-a15); } .glass-input::placeholder { color: var(--color-text-secondary); @@ -217,20 +343,20 @@ input, textarea, select { font-weight: 600; white-space: nowrap; } -.badge-green { color: #15803D; border: 1.3px solid rgba(34,197,94,0.3); } -.badge-blue { background: rgba(6,182,212,0.15); color: #0E7490; border: 1px solid rgba(6,182,212,0.3); } -.badge-orange { background: rgba(251,146,60,0.15); color: #C2410C; border: 1px solid rgba(251,146,60,0.3); } -.badge-gray { background: rgba(100,116,139,0.1); color: #64748B; border: 1px solid rgba(100,116,139,0.2); } -.badge-red { background: rgba(239,68,68,0.12); color: #B91C1C; border: 1px solid rgba(239,68,68,0.2); } -.badge-purple { background: rgba(139,92,246,0.12); color: #6D28D9; border: 1px solid rgba(139,92,246,0.2); } +.badge-green { color: var(--color-primary-border); border: 1.3px solid var(--color-primary-a30); } +.badge-blue { background: var(--color-aqua-a15); color: var(--color-aqua-dark); border: 1px solid var(--color-aqua-a30); } +.badge-orange { background: var(--color-orange-a15); color: var(--color-orange-dark); border: 1px solid var(--color-orange-a30); } +.badge-gray { background: var(--color-slate-500-a10); color: var(--color-text-secondary); border: 1px solid var(--color-slate-500-a20); } +.badge-red { background: var(--color-error-a12); color: var(--color-error-border); border: 1px solid var(--color-error-a20); } +.badge-purple { background: var(--color-purple-a12); color: var(--color-purple-dark); border: 1px solid var(--color-purple-a20); } /* Tag chip */ .tag-chip { display: inline-flex; align-items: center; padding: 3px 10px; - background: rgba(34,197,94,0.1); - border: 1px solid rgba(34,197,94,0.25); + background: var(--color-primary-a10); + border: 1px solid var(--color-primary-a25); border-radius: 20px; font-size: 12px; color: var(--color-primary-dark); @@ -241,7 +367,7 @@ input, textarea, select { } .tag-chip:hover, .tag-chip.active { - background: rgba(34,197,94,0.2); + background: var(--color-primary-a20); border-color: var(--color-primary); } @@ -292,7 +418,7 @@ input, textarea, select { .font-semibold { font-weight: 600; } /* Stars rating */ -.stars { color: #FBBF24; font-size: 14px; letter-spacing: 1px; } +.stars { color: var(--color-star); font-size: 14px; letter-spacing: 1px; } /* Animations */ @keyframes fadeIn { @@ -306,14 +432,14 @@ input, textarea, select { } .spinner { width: 20px; height: 20px; - border: 2px solid rgba(255,255,255,0.3); - border-top-color: white; + border: 2px solid var(--color-white-a30); + border-top-color: var(--color-white); border-radius: 50%; animation: spin 0.8s linear infinite; display: inline-block; } .spinner-green { - border-color: rgba(34,197,94,0.2); + border-color: var(--color-primary-a20); border-top-color: var(--color-primary); } diff --git a/frontend/src/components/layout/AppBottomNav.vue b/frontend/src/components/layout/AppBottomNav.vue index 2dd3153..3f900f3 100644 --- a/frontend/src/components/layout/AppBottomNav.vue +++ b/frontend/src/components/layout/AppBottomNav.vue @@ -57,7 +57,7 @@ function isActive(to: string) { left: 0; right: 0; height: 60px; - background: rgba(255,255,255,0.9); + background: var(--color-white-a90); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-top: 1px solid var(--color-border-glass); diff --git a/frontend/src/components/layout/AppSidebar.vue b/frontend/src/components/layout/AppSidebar.vue index 8cc1a81..0dd5933 100644 --- a/frontend/src/components/layout/AppSidebar.vue +++ b/frontend/src/components/layout/AppSidebar.vue @@ -66,7 +66,7 @@ function isActive(to: string) { left: 0; bottom: 0; width: var(--sidebar-width); - background: rgba(255,255,255,0.75); + background: var(--color-white-glass); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-right: 1px solid var(--color-border-glass); @@ -96,14 +96,14 @@ function isActive(to: string) { transition: all 0.2s; } .nav-item:hover { - background: rgba(34,197,94,0.1); + background: var(--color-primary-a10); color: var(--color-primary-dark); } .nav-item.active { - background: linear-gradient(135deg, rgba(34,197,94,0.18), rgba(134,239,172,0.12)); + background: var(--gradient-nav-active); color: var(--color-primary-dark); font-weight: 700; - box-shadow: 0 2px 8px rgba(34,197,94,0.12); + box-shadow: 0 2px 8px var(--color-primary-a12); } .nav-icon { flex-shrink: 0; color: currentColor; } .sidebar-footer { diff --git a/frontend/src/components/layout/AppTopbar.vue b/frontend/src/components/layout/AppTopbar.vue index 606536d..ef271f4 100644 --- a/frontend/src/components/layout/AppTopbar.vue +++ b/frontend/src/components/layout/AppTopbar.vue @@ -153,11 +153,11 @@ onBeforeUnmount(() => { left: 0; right: 0; height: var(--topbar-height); - background: rgba(255,255,255,0.8); + background: var(--color-white-a80); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-bottom: 1px solid var(--color-border-glass); - box-shadow: 0 2px 20px rgba(0,0,0,0.06); + box-shadow: 0 2px 20px var(--color-black-a06); display: flex; align-items: center; justify-content: space-between; @@ -198,13 +198,13 @@ onBeforeUnmount(() => { border-radius: 50%; transition: background 0.2s; } -.notif-btn:hover { background: rgba(0,0,0,0.05); } +.notif-btn:hover { background: var(--color-black-a05); } .notif-dot { position: absolute; top: -2px; right: -2px; background: var(--color-error); - color: #fff; + color: var(--color-white); font-size: 9px; font-weight: 700; width: 16px; @@ -225,20 +225,20 @@ onBeforeUnmount(() => { padding: 5px 10px; border-radius: 20px; border: 1px solid var(--color-border-glass); - background: rgba(255,255,255,0.5); + background: var(--color-white-a50); color: var(--color-text); font: inherit; transition: all 0.2s; } .avatar:hover, .avatar[aria-expanded="true"] { - border-color: rgba(34,197,94,0.5); - background: rgba(255,255,255,0.8); - box-shadow: 0 0 0 3px rgba(34,197,94,0.12); + border-color: var(--color-primary-a50); + background: var(--color-white-a80); + box-shadow: 0 0 0 3px var(--color-primary-a12); } .avatar:focus-visible, .profile-menu-item:focus-visible { - outline: 2px solid rgba(34,197,94,0.45); + outline: 2px solid var(--color-primary-a45); outline-offset: 2px; } .avatar-icon { color: var(--color-text-secondary); } @@ -251,8 +251,8 @@ onBeforeUnmount(() => { padding: 8px; border: 1px solid var(--color-border-glass); border-radius: var(--radius-sm); - background: rgba(255,255,255,0.96); - box-shadow: 0 18px 38px rgba(15,23,42,0.14); + background: var(--color-white-a96); + box-shadow: 0 18px 38px var(--color-slate-900-a14); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); display: flex; @@ -261,8 +261,8 @@ onBeforeUnmount(() => { z-index: 120; } .profile-menu-section { - border-top: 1px solid rgba(15,23,42,0.08); - border-bottom: 1px solid rgba(15,23,42,0.08); + border-top: 1px solid var(--color-slate-900-a08); + border-bottom: 1px solid var(--color-slate-900-a08); margin: 4px 0; padding: 6px 0; } @@ -292,15 +292,15 @@ onBeforeUnmount(() => { transition: background 0.2s, color 0.2s; } .profile-menu-item:hover { - background: rgba(34,197,94,0.1); + background: var(--color-primary-a10); color: var(--color-primary-dark); } .profile-menu-item.danger { - color: #991B1B; + color: var(--color-danger-text); } .profile-menu-item.danger:hover { - background: rgba(239,68,68,0.1); - color: #991B1B; + background: var(--color-error-a10); + color: var(--color-danger-text); } @media (max-width: 640px) { diff --git a/frontend/src/components/ui/AchievementBadge.vue b/frontend/src/components/ui/AchievementBadge.vue index 55ab68f..6fab288 100644 --- a/frontend/src/components/ui/AchievementBadge.vue +++ b/frontend/src/components/ui/AchievementBadge.vue @@ -56,17 +56,17 @@ defineProps<{ .badge-desc { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 6px; } .badge-meta { font-size: 11px; color: var(--color-text-secondary); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .meta-icon { color: var(--color-text-secondary); } -.locked-msg { color: #9CA3AF; } +.locked-msg { color: var(--color-gray-400); } .coins-tag { margin-left: 6px; - background: rgba(251,191,36,0.15); + background: var(--color-star-a15); border-radius: 10px; padding: 1px 8px; - color: #92400E; + color: var(--color-warning-text); font-weight: 600; display: inline-flex; align-items: center; gap: 4px; } -.coin-icon { color: #92400E; } +.coin-icon { color: var(--color-warning-text); } diff --git a/frontend/src/components/ui/CoinChip.vue b/frontend/src/components/ui/CoinChip.vue index 3928408..0ae0c68 100644 --- a/frontend/src/components/ui/CoinChip.vue +++ b/frontend/src/components/ui/CoinChip.vue @@ -17,8 +17,8 @@ defineProps<{ amount: number }>() display: inline-flex; align-items: center; gap: 5px; - background: linear-gradient(135deg, rgba(251,191,36,0.2), rgba(245,158,11,0.15)); - border: 1px solid rgba(245,158,11,0.4); + background: var(--gradient-coin-chip); + border: 1px solid var(--color-warning-a40); border-radius: 20px; padding: 5px 12px; cursor: default; @@ -26,9 +26,9 @@ defineProps<{ amount: number }>() white-space: nowrap; } .coin-chip:hover { - background: linear-gradient(135deg, rgba(251,191,36,0.3), rgba(245,158,11,0.25)); + background: var(--gradient-coin-chip-hover); } -.coin-icon { color: #78350F; } -.coin-amount { font-weight: 800; font-size: 14px; color: #78350F; } -.coin-label { font-size: 12px; color: #92400E; } +.coin-icon { color: var(--color-brown-dark); } +.coin-amount { font-weight: 800; font-size: 14px; color: var(--color-brown-dark); } +.coin-label { font-size: 12px; color: var(--color-warning-text); } diff --git a/frontend/src/components/ui/DataTable.vue b/frontend/src/components/ui/DataTable.vue index 7317a96..cc1b615 100644 --- a/frontend/src/components/ui/DataTable.vue +++ b/frontend/src/components/ui/DataTable.vue @@ -45,7 +45,7 @@ defineProps<{ font-size: 14px; } .data-table th { - background: rgba(255,255,255,0.6); + background: var(--color-white-a60); border-bottom: 2px solid var(--color-border-glass); padding: 10px 14px; font-weight: 600; @@ -59,7 +59,7 @@ defineProps<{ } .data-table tbody tr:last-child td { border-bottom: none; } .data-table tbody tr:hover td { - background: rgba(34,197,94,0.05); + background: var(--color-primary-a05); } .align-left { text-align: left; } .align-center { text-align: center; } diff --git a/frontend/src/components/ui/GlassCard.vue b/frontend/src/components/ui/GlassCard.vue index 8db1116..04285b0 100644 --- a/frontend/src/components/ui/GlassCard.vue +++ b/frontend/src/components/ui/GlassCard.vue @@ -33,6 +33,6 @@ const { padding, hoverable } = toRefs(props) } .hoverable:hover { transform: translateY(-2px); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12); + box-shadow: 0 12px 40px var(--color-black-a12); } diff --git a/frontend/src/components/ui/LectureCard.vue b/frontend/src/components/ui/LectureCard.vue index 562afa1..d85ed0b 100644 --- a/frontend/src/components/ui/LectureCard.vue +++ b/frontend/src/components/ui/LectureCard.vue @@ -130,7 +130,7 @@ function goDetail() { } .lecture-card:hover { transform: translateY(-3px); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(34,197,94,0.2); + box-shadow: 0 12px 40px var(--color-black-a12), 0 0 0 1px var(--color-primary-a20); } .card-header { display: flex; @@ -145,8 +145,8 @@ function goDetail() { font-weight: 600; color: var(--color-primary-dark); } -.seats-low { color: #EA580C; } -.seats-zero { color: #DC2626; } +.seats-low { color: var(--color-orange-deep); } +.seats-zero { color: var(--color-error-dark); } .card-title { font-size: 15px; font-weight: 700; @@ -203,8 +203,8 @@ function goDetail() { font-size: 13px; } .btn-registered { - background: rgba(34,197,94,0.12); - border: 1px solid rgba(34,197,94,0.3); + background: var(--color-primary-a12); + border: 1px solid var(--color-primary-a30); border-radius: var(--radius-sm); color: var(--color-primary-dark); padding: 7px 14px; @@ -213,8 +213,8 @@ function goDetail() { cursor: default; } .btn-disabled { - background: rgba(100,116,139,0.1); - border: 1px solid rgba(100,116,139,0.2); + background: var(--color-slate-500-a10); + border: 1px solid var(--color-slate-500-a20); border-radius: var(--radius-sm); color: var(--color-text-secondary); padding: 7px 14px; diff --git a/frontend/src/components/ui/LoadingSpinner.vue b/frontend/src/components/ui/LoadingSpinner.vue index 947f283..b27b0ce 100644 --- a/frontend/src/components/ui/LoadingSpinner.vue +++ b/frontend/src/components/ui/LoadingSpinner.vue @@ -12,7 +12,7 @@ defineProps<{ size?: 'sm' | 'md' | 'lg' }>() .spinner-wrap { display: flex; align-items: center; justify-content: center; padding: 24px; } .spinner { border-radius: 50%; - border: 3px solid rgba(34,197,94,0.2); + border: 3px solid var(--color-primary-a20); border-top-color: var(--color-primary); animation: spin 0.8s linear infinite; } diff --git a/frontend/src/components/ui/ModalDialog.vue b/frontend/src/components/ui/ModalDialog.vue index 8a4a3b2..997ebc5 100644 --- a/frontend/src/components/ui/ModalDialog.vue +++ b/frontend/src/components/ui/ModalDialog.vue @@ -31,7 +31,7 @@ const emit = defineEmits<{ 'update:modelValue': [v: boolean] }>() .modal-overlay { position: fixed; inset: 0; - background: rgba(0,0,0,0.35); + background: var(--color-black-a35); backdrop-filter: blur(4px); display: flex; align-items: center; @@ -43,7 +43,7 @@ const emit = defineEmits<{ 'update:modelValue': [v: boolean] }>() background: var(--color-surface); border: 1px solid var(--color-border-glass); border-radius: var(--radius-lg); - box-shadow: 0 24px 64px rgba(0,0,0,0.2); + box-shadow: 0 24px 64px var(--color-black-a20); width: 100%; max-width: 520px; max-height: 90vh; diff --git a/frontend/src/components/ui/ProgressBar.vue b/frontend/src/components/ui/ProgressBar.vue index 3b2a807..ae0d67c 100644 --- a/frontend/src/components/ui/ProgressBar.vue +++ b/frontend/src/components/ui/ProgressBar.vue @@ -15,7 +15,7 @@ defineProps<{ class="progress-fill" :style="{ width: `${Math.min(100, (value / (max ?? 100)) * 100)}%`, - background: color ?? 'linear-gradient(90deg, #22C55E, #86EFAC)' + background: color ?? 'var(--gradient-progress-success)' }" /> @@ -28,7 +28,7 @@ defineProps<{ .progress-label { font-size: 12px; color: var(--color-text-secondary); font-weight: 500; } .progress-bar { height: 8px; - background: rgba(0,0,0,0.08); + background: var(--color-black-a08); border-radius: 4px; overflow: hidden; } @@ -43,7 +43,7 @@ defineProps<{ position: absolute; top: 0; left: 0; right: 0; height: 50%; - background: rgba(255,255,255,0.4); + background: var(--color-white-a40); border-radius: 4px 4px 0 0; } .progress-text { font-size: 11px; color: var(--color-text-secondary); } diff --git a/frontend/src/components/ui/SearchInput.vue b/frontend/src/components/ui/SearchInput.vue index 07b83ef..0ffe02d 100644 --- a/frontend/src/components/ui/SearchInput.vue +++ b/frontend/src/components/ui/SearchInput.vue @@ -38,7 +38,7 @@ const emit = defineEmits<{ 'update:modelValue': [value: string] }>() padding: 10px 16px 10px 38px; border-radius: var(--radius-sm); border: 1px solid var(--color-border-glass); - background: rgba(255,255,255,0.7); + background: var(--color-white-a70); backdrop-filter: blur(8px); font-size: 14px; color: var(--color-text); @@ -47,7 +47,7 @@ const emit = defineEmits<{ 'update:modelValue': [value: string] }>() } .search-input:focus { border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(34,197,94,0.15); + box-shadow: 0 0 0 3px var(--color-primary-a15); } .search-input::placeholder { color: var(--color-text-secondary); } diff --git a/frontend/src/components/ui/StatsWidget.vue b/frontend/src/components/ui/StatsWidget.vue index 3023c35..0bbf2f1 100644 --- a/frontend/src/components/ui/StatsWidget.vue +++ b/frontend/src/components/ui/StatsWidget.vue @@ -43,10 +43,10 @@ defineProps<{ height: 3px; border-radius: var(--radius-md) var(--radius-md) 0 0; } -.color-green::after { background: linear-gradient(90deg, #22C55E, #86EFAC); } -.color-aqua::after { background: linear-gradient(90deg, #06B6D4, #67E8F9); } -.color-orange::after { background: linear-gradient(90deg, #FB923C, #FCD34D); } -.color-purple::after { background: linear-gradient(90deg, #A78BFA, #C4B5FD); } +.color-green::after { background: var(--gradient-stats-green); } +.color-aqua::after { background: var(--gradient-stats-aqua); } +.color-orange::after { background: var(--gradient-stats-orange); } +.color-purple::after { background: var(--gradient-stats-purple); } .widget-icon { flex-shrink: 0; diff --git a/frontend/src/components/ui/StatusBadge.vue b/frontend/src/components/ui/StatusBadge.vue index 84a84df..668160e 100644 --- a/frontend/src/components/ui/StatusBadge.vue +++ b/frontend/src/components/ui/StatusBadge.vue @@ -36,8 +36,8 @@ const statusMap: Record = { font-size: 12px; font-weight: 600; } -.success { background: rgba(220,252,231,0.9); color: #166534; border: 1px solid #86EFAC; } -.warning { background: rgba(254,243,199,0.9); color: #92400E; border: 1px solid #FDE68A; } -.danger { background: rgba(254,226,226,0.9); color: #991B1B; border: 1px solid #FCA5A5; } -.info { background: rgba(224,242,254,0.9); color: #1E40AF; border: 1px solid #93C5FD; } +.success { background: var(--color-success-bg-a90); color: var(--color-success-text); border: 1px solid var(--color-primary-light); } +.warning { background: var(--color-warning-bg-a90); color: var(--color-warning-text); border: 1px solid var(--color-warning-border); } +.danger { background: var(--color-danger-bg-a90); color: var(--color-danger-text); border: 1px solid var(--color-danger-light); } +.info { background: var(--color-info-bg-a90); color: var(--color-info-text); border: 1px solid var(--color-info-border); } diff --git a/frontend/src/components/ui/ToastNotification.vue b/frontend/src/components/ui/ToastNotification.vue index 75abdd4..2b4456e 100644 --- a/frontend/src/components/ui/ToastNotification.vue +++ b/frontend/src/components/ui/ToastNotification.vue @@ -37,13 +37,13 @@ const iconNameMap = { success: 'circle-check', error: 'circle-x', info: 'info-ci border-radius: var(--radius-md); font-size: 14px; font-weight: 500; - box-shadow: 0 8px 32px rgba(0,0,0,0.15); + box-shadow: 0 8px 32px var(--color-black-a15); pointer-events: all; max-width: 340px; } -.success { background: rgba(220,252,231,0.95); border: 1px solid #86EFAC; color: #166534; } -.error { background: rgba(254,226,226,0.95); border: 1px solid #FCA5A5; color: #991B1B; } -.info { background: rgba(224,242,254,0.95); border: 1px solid #93C5FD; color: #1E40AF; } +.success { background: var(--color-success-bg-a95); border: 1px solid var(--color-primary-light); color: var(--color-success-text); } +.error { background: var(--color-danger-bg-a95); border: 1px solid var(--color-danger-light); color: var(--color-danger-text); } +.info { background: var(--color-info-bg-a95); border: 1px solid var(--color-info-border); color: var(--color-info-text); } .toast-close { margin-left: auto; background: none; border: none; font-size: 18px; cursor: pointer; opacity: 0.6; } .toast-close:hover { opacity: 1; } .toast-enter-active, .toast-leave-active { transition: all 0.35s ease; } diff --git a/frontend/src/views/admin/AdminDashboardView.vue b/frontend/src/views/admin/AdminDashboardView.vue index 1d11afb..e1fe7f4 100644 --- a/frontend/src/views/admin/AdminDashboardView.vue +++ b/frontend/src/views/admin/AdminDashboardView.vue @@ -101,13 +101,13 @@ onMounted(async () => { .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; } .bars { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; } .bar-row { display: grid; grid-template-columns: 1fr 2fr auto; align-items: center; gap: 8px; font-size: 13px; } -.bar { background: rgba(0,0,0,0.08); border-radius: 6px; height: 8px; overflow: hidden; } -.bar-fill { background: linear-gradient(90deg, #22C55E, #86EFAC); height: 100%; } +.bar { background: var(--color-black-a08); border-radius: 6px; height: 8px; overflow: hidden; } +.bar-fill { background: var(--gradient-progress-success); height: 100%; } .percent { color: var(--color-text-secondary); font-size: 12px; } .metric { margin-bottom: 10px; color: var(--color-text-secondary); } .activity { display: flex; gap: 10px; margin-top: 12px; align-items: flex-end; } .day { display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: var(--color-text-secondary); } -.day-bar { width: 16px; background: linear-gradient(180deg, #7DD3FC, #BAE6FD); border-radius: 6px 6px 0 0; } +.day-bar { width: 16px; background: var(--gradient-bar-neutral-vertical); border-radius: 6px 6px 0 0; } .sync-meta { font-size: 12px; color: var(--color-text-secondary); margin-top: 6px; } .sync-error { font-size: 12px; color: var(--color-error); margin-top: 8px; } .queue-meta { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 8px; } diff --git a/frontend/src/views/admin/AdminLecturesView.vue b/frontend/src/views/admin/AdminLecturesView.vue index 872e4df..d722bea 100644 --- a/frontend/src/views/admin/AdminLecturesView.vue +++ b/frontend/src/views/admin/AdminLecturesView.vue @@ -340,8 +340,8 @@ onMounted(() => { .admin-lectures { display: flex; flex-direction: column; gap: 16px; } .header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; } .tabs { display: inline-flex; border: 1px solid var(--color-border-glass); border-radius: 12px; overflow: hidden; } -.tabs button { background: rgba(255,255,255,0.7); border: none; padding: 8px 18px; font-size: 13px; cursor: pointer; color: var(--color-text-secondary); } -.tabs button.active { background: rgba(34,197,94,0.18); color: var(--color-primary-dark); font-weight: 600; } +.tabs button { background: var(--color-white-a70); border: none; padding: 8px 18px; font-size: 13px; cursor: pointer; color: var(--color-text-secondary); } +.tabs button.active { background: var(--color-primary-a18); color: var(--color-primary-dark); font-weight: 600; } .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; } .section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; } .form { display: flex; flex-direction: column; gap: 10px; } @@ -356,8 +356,8 @@ onMounted(() => { padding: 8px 10px; border: 1px solid var(--color-border-glass); border-radius: var(--radius-sm); - background: rgba(255,255,255,0.72); - color: var(--color-text-primary); + background: var(--color-white-a72); + color: var(--color-text); cursor: pointer; } .type-option input { flex: 0 0 auto; } @@ -366,10 +366,10 @@ onMounted(() => { .sync-result { font-size: 13px; color: var(--color-text-secondary); } .sync-error { font-size: 13px; color: var(--color-error); } .sync-details { - border: 1px solid rgba(239,68,68,0.24); + border: 1px solid var(--color-error-a24); border-radius: var(--radius-sm); padding: 8px 10px; - background: rgba(254,242,242,0.68); + background: var(--color-danger-bg-a68); color: var(--color-text-secondary); font-size: 12px; } @@ -383,8 +383,8 @@ onMounted(() => { padding: 4px 10px; font-size: 12px; color: var(--color-text-secondary); - background: rgba(255,255,255,0.72); + background: var(--color-white-a72); } -.sync-status.completed { color: #166534; background: rgba(220,252,231,0.9); border-color: #86EFAC; } -.sync-status.failed { color: #991B1B; background: rgba(254,226,226,0.9); border-color: #FCA5A5; } +.sync-status.completed { color: var(--color-success-text); background: var(--color-success-bg-a90); border-color: var(--color-primary-light); } +.sync-status.failed { color: var(--color-danger-text); background: var(--color-danger-bg-a90); border-color: var(--color-danger-light); } diff --git a/frontend/src/views/student/ProfileView.vue b/frontend/src/views/student/ProfileView.vue index ef329c7..592008d 100644 --- a/frontend/src/views/student/ProfileView.vue +++ b/frontend/src/views/student/ProfileView.vue @@ -21,7 +21,7 @@ const userMetaLine = computed(() => { }) const userYearLine = computed(() => { - const year = user.value.year + const year = user.value.year ?? 0 return Number.isFinite(year) && year > 0 ? `${year} курс` : '' }) const interestTags = ref([