diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index 703fd78..a08f19b 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -1,93 +1,28 @@ -/* @import 'variables.css'; - Loaded via HTML */ +/* variables.css loaded separately via HTML */ -/* Reset & Basics */ -.container { - max-width: 1600px; - margin: 0 auto; - padding: 0 2.5rem; -} +/* ===== Reset & Basics ===== */ +.container { max-width: 1600px; margin: 0 auto; padding: 0 2.5rem; } +a { text-decoration: none; color: inherit; } +ul { list-style: none; padding: 0; margin: 0; } -a { - text-decoration: none; - color: inherit; -} +/* ===== Utility Classes ===== */ +.flex { display: flex; } +.flex-col { flex-direction: column; } +.items-center { align-items: center; } +.justify-between { justify-content: space-between; } +.gap-2 { gap: var(--space-2); } +.gap-4 { gap: 1rem; } +.w-full { width: 100%; } +.text-sm { font-size: 0.875rem; } +.text-lg { font-size: 1.125rem; font-weight: 600; } +.text-xl { font-size: 1.5rem; font-weight: 700; } +.text-muted { color: var(--text-muted); } +.font-bold { font-weight: 700; } +.mt-4 { margin-top: 1rem; } +.mb-4 { margin-bottom: 1rem; } +.p-4 { padding: 1rem; } +.p-6 { padding: 1.5rem; } -ul { - list-style: none; - padding: 0; - margin: 0; -} - -/* Utility Classes */ -.flex { - display: flex; -} - -.flex-col { - flex-direction: column; -} - -.items-center { - align-items: center; -} - -.justify-between { - justify-content: space-between; -} - -.gap-2 { - gap: var(--space-2); -} - -.gap-4 { - gap: 1rem; -} - -.w-full { - width: 100%; -} - -.text-sm { - font-size: 0.875rem; -} - -.text-lg { - font-size: 1.125rem; - font-weight: 600; -} - -.text-xl { - font-size: 1.5rem; - font-weight: 700; -} - -.text-muted { - color: var(--text-muted); -} - -.font-bold { - font-weight: 700; -} - -.mt-4 { - margin-top: 1rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.p-4 { - padding: 1rem; -} - -.p-6 { - padding: 1.5rem; -} - -/* ===== Utility backfill (classes already used in templates, previously undefined) ===== */ - -/* Spacing — padding */ .p-0 { padding: 0; } .p-2 { padding: var(--space-2); } .p-3 { padding: var(--space-3); } @@ -98,7 +33,6 @@ ul { .py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); } .pr-4 { padding-right: var(--space-4); } -/* Spacing — margin */ .mt-1 { margin-top: var(--space-1); } .mt-2 { margin-top: var(--space-2); } .mb-1 { margin-bottom: var(--space-1); } @@ -107,11 +41,9 @@ ul { .mb-6 { margin-bottom: var(--space-6); } .ml-2 { margin-left: var(--space-2); } -/* Spacing — gap */ .gap-1 { gap: var(--space-1); } .gap-3 { gap: var(--space-3); } -/* Fl/grid helpers */ .flex-wrap { flex-wrap: wrap; } .flex-shrink-0 { flex-shrink: 0; } .justify-center { justify-content: center; } @@ -135,141 +67,96 @@ ul { .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .text-center { text-align: center; } -/* Typography */ .text-xs { font-size: 0.75rem; } .text-2xl { font-size: 1.875rem; font-weight: 700; } .font-medium { font-weight: 500; } .font-semibold { font-weight: 600; } .font-normal { font-weight: 400; } -/* Colors */ .text-white { color: #fff; } .text-secondary { color: var(--text-secondary); } .text-danger { color: var(--danger); } .text-success { color: var(--success); } -.text-blue-400 { color: #60a5fa; } -.text-cyan-400 { color: #22d3ee; } -.text-pink-400 { color: #f472b6; } -.text-purple-400 { color: #c084fc; } -.text-orange-400 { color: #fb923c; } -.text-yellow-400 { color: #facc15; } -.text-emerald-400 { color: #34d399; } +.text-blue-400 { color: var(--accent); } +.text-cyan-400 { color: var(--sky); } +.text-pink-400 { color: var(--red); } +.text-purple-400 { color: var(--purple); } +.text-orange-400 { color: var(--amber); } +.text-yellow-400 { color: var(--amber); } +.text-emerald-400 { color: var(--green); } -/* Transitions */ .transition-colors { transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease; } .transition-opacity { transition: opacity 0.2s ease; } .transition-all { transition: all 0.2s ease; } -/* Hover utilities (escaped selectors) */ -.hover\:text-white:hover { color: #fff; } +.hover\:text-white:hover { color: var(--text); } .hover\:underline:hover { text-decoration: underline; } .hover\:text-\[var\(--primary\)\]:hover { color: var(--primary); } .hover\:border-\[var\(--primary\)\]:hover { border-color: var(--primary); } -.border-\[var\(--glass-border\)\] { border: 1px solid var(--glass-border); } -.hover\:bg-blue-400\/10:hover { background: rgba(96, 165, 250, 0.1); } -.hover\:bg-cyan-400\/10:hover { background: rgba(34, 211, 238, 0.1); } -.hover\:bg-pink-400\/10:hover { background: rgba(244, 114, 182, 0.1); } -.hover\:bg-purple-400\/10:hover { background: rgba(192, 132, 252, 0.1); } -.hover\:bg-orange-400\/10:hover { background: rgba(251, 146, 60, 0.1); } -.hover\:bg-emerald-400\/10:hover { background: rgba(52, 211, 153, 0.1); } -.hover\:bg-yellow-400\/10:hover { background: rgba(250, 204, 21, 0.1); } +.border-\[var\(--glass-border\)\] { border: 1px solid var(--border); } +.hover\:bg-blue-400\/10:hover { background: rgba(79, 124, 255, 0.10); } +.hover\:bg-cyan-400\/10:hover { background: rgba(14, 165, 233, 0.10); } +.hover\:bg-pink-400\/10:hover { background: rgba(244, 63, 94, 0.10); } +.hover\:bg-purple-400\/10:hover { background: rgba(139, 92, 246, 0.10); } +.hover\:bg-orange-400\/10:hover { background: rgba(245, 158, 11, 0.10); } +.hover\:bg-emerald-400\/10:hover { background: rgba(16, 185, 129, 0.10); } +.hover\:bg-yellow-400\/10:hover { background: rgba(245, 158, 11, 0.10); } -/* Group hover reveal */ .group:hover .group-hover\:opacity-100 { opacity: 1; } -/* Spin animation */ @keyframes spin { to { transform: rotate(360deg); } } .animate-spin { animation: spin 1s linear infinite; } -@media (prefers-reduced-motion: reduce) { - .animate-spin { animation: none; } -} +@media (prefers-reduced-motion: reduce) { .animate-spin { animation: none; } } -/* Components */ +/* ===== Components ===== */ -/* GLASS CARD */ +/* CARD — clean surface, subtle border + shadow */ .card { - background: var(--bg-glass); - backdrop-filter: blur(var(--backdrop-blur)); - -webkit-backdrop-filter: blur(var(--backdrop-blur)); - border: 1px solid var(--glass-border); - border-radius: var(--radius-lg); - padding: 1.5rem; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); - transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--r); + padding: 1.25rem 1.4rem; + box-shadow: var(--shadow); + transition: border-color 0.2s ease, box-shadow 0.2s ease; position: relative; - overflow: hidden; -} - -.card:hover { - transform: translateY(-4px); - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2); - border-color: var(--glass-highlight); } +.card:hover { border-color: var(--border-strong); } /* BUTTON */ .btn { display: inline-flex; align-items: center; justify-content: center; - padding: 0.6rem 1.25rem; - border-radius: var(--radius-md); + gap: 0.45rem; + padding: 0.55rem 1rem; + border-radius: var(--r-sm); font-weight: 600; - transition: all 0.2s; + font-size: 0.875rem; + transition: all 0.18s ease; cursor: pointer; border: 1px solid transparent; + line-height: 1.2; } - .btn-primary { background: var(--primary-gradient); - color: white; - box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3); + color: #fff; + box-shadow: 0 2px 8px rgba(79, 99, 235, 0.25); } - -.btn-primary:hover { - box-shadow: 0 6px 20px rgba(37, 99, 235, 0.5); - transform: translateY(-1px); -} - -.btn-ghost { - background: transparent; - color: var(--text-secondary); -} - -.btn-ghost:hover { - background: rgba(255, 255, 255, 0.05); - color: var(--text-primary); - border-color: var(--glass-border); -} - +.btn-primary:hover { filter: brightness(1.05); box-shadow: 0 4px 14px rgba(79, 99, 235, 0.35); } +.btn-ghost { background: transparent; color: var(--text-2); } +.btn-ghost:hover { background: var(--hover); color: var(--text); } .btn-secondary { - background: rgba(255, 255, 255, 0.06); - color: var(--text-primary); - border: 1px solid var(--glass-border); + background: var(--surface); + color: var(--text); + border: 1px solid var(--border-strong); + box-shadow: var(--shadow); } +.btn-secondary:hover { background: var(--surface-2); } +.btn-outline { background: transparent; color: var(--text-2); border: 1px solid var(--border-strong); } +.btn-outline:hover { color: var(--text); border-color: var(--primary); } -.btn-secondary:hover { - background: rgba(255, 255, 255, 0.1); - border-color: var(--glass-highlight); - transform: translateY(-1px); -} - -.btn-outline { - background: transparent; - color: var(--text-secondary); - border: 1px solid var(--glass-border); -} - -.btn-outline:hover { - color: var(--text-primary); - border-color: var(--primary); -} - -/* SIDEBAR SPECIFIC */ -:root { - --sidebar-width: 260px; - --sidebar-collapsed-width: 80px; - --header-height: 64px; -} +/* ===== Sidebar ===== */ +:root { --sidebar-width: 244px; --sidebar-collapsed-width: 76px; --header-height: 64px; } .sidebar { width: var(--sidebar-width); @@ -279,468 +166,211 @@ ul { top: 0; display: flex; flex-direction: column; - background-color: var(--bg-glass); - backdrop-filter: blur(20px); - border-right: 1px solid var(--glass-border); + background: var(--side-bg); + border-right: 1px solid var(--border); z-index: 50; transition: width 0.3s ease; overflow-x: hidden; - /* Prevent horizontal scroll during transition */ } -/* User Profile Section */ +.nav-section { + font-size: 10.5px; + text-transform: uppercase; + letter-spacing: .08em; + color: var(--text-3); + font-weight: 600; + padding: 14px 1.25rem 6px; +} + .user-profile-container { - padding: 1rem; - margin: 0 1rem 1rem 1rem; - border-radius: 12px; - border: 1px solid var(--glass-border); - background: linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0) 100%); - transition: all 0.3s ease; + padding: 0.85rem 1rem; + margin-top: auto; + border-top: 1px solid var(--border); } +.user-profile-content { display: flex; align-items: center; gap: 0.7rem; overflow: hidden; } -.user-profile-content { - display: flex; - align-items: center; - gap: 0.75rem; - margin-bottom: 0.75rem; - overflow: hidden; -} - -/* Toggle Button */ .sidebar-toggle-btn { - display: flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; + display: flex; align-items: center; justify-content: center; + width: 28px; height: 28px; border-radius: 6px; - color: var(--text-muted); + color: var(--text-3); cursor: pointer; transition: all 0.2s; border: 1px solid transparent; background: transparent; } +.sidebar-toggle-btn:hover { color: var(--text); background: var(--hover); } -.sidebar-toggle-btn:hover { - color: var(--text-primary); - background: rgba(255, 255, 255, 0.05); - border-color: var(--glass-border); -} - -/* Collapsed State */ -.sidebar.collapsed { - width: var(--sidebar-collapsed-width); +/* Theme toggle */ +.theme-toggle { + display: flex; align-items: center; gap: 0.5rem; + width: 100%; + padding: 0.55rem 0.75rem; + margin-bottom: 0.5rem; + border-radius: var(--r-sm); + background: transparent; + border: 1px solid var(--border); + color: var(--text-2); + font-size: 0.8rem; font-weight: 500; + cursor: pointer; transition: all 0.18s ease; } +.theme-toggle:hover { background: var(--hover); color: var(--text); } +.theme-toggle .sun { display: none; } +.theme-toggle .moon { display: inline-flex; } +:root[data-theme="dark"] .theme-toggle .sun { display: inline-flex; } +:root[data-theme="dark"] .theme-toggle .moon { display: none; } +/* Collapsed state */ +.sidebar.collapsed { width: var(--sidebar-collapsed-width); } .sidebar.collapsed .brand-text, .sidebar.collapsed .nav-text, -.sidebar.collapsed .user-info { - display: none; - opacity: 0; -} - -/* Header in collapsed menu */ -.sidebar.collapsed .p-6 { - /* Target the header div which has p-6 */ - padding: 1rem; - flex-direction: column; - gap: 1rem; - justify-content: center; -} - -.sidebar.collapsed .sidebar-toggle-btn { - transform: rotate(180deg); - /* Rotate chevron */ -} - -.sidebar.collapsed .nav-item { - justify-content: center; - padding: 0.85rem 0; -} - -.sidebar.collapsed .nav-icon { - margin-right: 0; -} - -.sidebar.collapsed .user-profile-container { - padding: 0.75rem; - margin: 0 0.5rem 1rem 0.5rem; - border: none; - background: transparent; -} - -.sidebar.collapsed .user-profile-content { - justify-content: center; - margin-bottom: 0; -} - -.sidebar.collapsed .sign-out-btn span { - display: none; -} - -.sidebar.collapsed .sign-out-btn { - padding: 0.5rem; -} - +.sidebar.collapsed .nav-section, +.sidebar.collapsed .user-info, +.sidebar.collapsed .theme-toggle span { display: none; } +.sidebar.collapsed .nav-item { justify-content: center; padding: 0.7rem 0; } +.sidebar.collapsed .nav-icon { margin-right: 0; } +.sidebar.collapsed .sidebar-toggle-btn { transform: rotate(180deg); } +.sidebar.collapsed .theme-toggle { justify-content: center; } /* NAV ITEM */ .nav-item { display: flex; align-items: center; - padding: 0.85rem 1rem; - border-radius: 0 var(--radius-md) var(--radius-md) 0; - transition: all 0.2s ease; - color: var(--text-secondary); - border-left: 3px solid transparent; + padding: 0.6rem 0.75rem; + margin: 1px 0; + border-radius: var(--r-sm); + transition: all 0.15s ease; + color: var(--text-2); font-weight: 500; + font-size: 0.875rem; white-space: nowrap; - /* Keep text on one line */ overflow: hidden; + position: relative; } - -.nav-item:hover { - color: var(--text-primary); - background: rgba(255, 255, 255, 0.03); +.nav-item:hover { color: var(--text); background: var(--surface-2); } +.nav-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; } +.nav-item.active::before { + content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); + width: 3px; height: 18px; border-radius: 0 3px 3px 0; background: var(--accent); } +.nav-icon { margin-right: 0.7rem; color: currentColor; min-width: 18px; width: 18px; height: 18px; } -.nav-item.active { - background: linear-gradient(90deg, rgba(59, 130, 246, 0.15), transparent); - border-left: 3px solid var(--primary); - color: var(--text-primary); - box-shadow: 0 4px 12px -4px rgba(59, 130, 246, 0.2); - font-weight: 600; -} +/* Main content offset */ +#mainContent { transition: margin-left 0.3s ease; margin-left: var(--sidebar-width); } +body.sidebar-collapsed #mainContent { margin-left: var(--sidebar-collapsed-width); } -.nav-icon { - margin-right: 0.875rem; - color: currentColor; - min-width: 24px; - /* Fix icon size */ -} +/* ===== Page header (shared) ===== */ +.page-header { display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 1rem; margin-bottom: 1.5rem; } +.page-header h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.4px; } +.page-header .sub { color: var(--text-3); font-size: 0.85rem; margin-top: 0.3rem; } +.page-header .actions { display: flex; gap: 0.6rem; align-items: center; } -.nav-item.active .nav-icon { - color: var(--primary); - filter: drop-shadow(0 0 8px var(--primary-glow)); -} - -/* Main Content Adjustment based on sidebar */ -#mainContent { - transition: margin-left 0.3s ease; - margin-left: var(--sidebar-width); -} - -body.sidebar-collapsed #mainContent { - margin-left: var(--sidebar-collapsed-width); -} - -/* Responsive */ +/* ===== Responsive ===== */ @media (max-width: 1024px) { - .sidebar { - width: var(--sidebar-collapsed-width); - } - - .sidebar .brand-text, - .sidebar .nav-text, - .sidebar .user-info { - display: none; - } - - .sidebar .nav-item { - justify-content: center; - padding: 0.85rem 0; - } - - .sidebar .nav-icon { - margin-right: 0; - } - - /* Auto-collapse helpers handled via JS class, but default CSS fallback */ - #mainContent { - margin-left: var(--sidebar-collapsed-width); - } + .sidebar { width: var(--sidebar-collapsed-width); } + .sidebar .brand-text, .sidebar .nav-text, .sidebar .nav-section, + .sidebar .user-info, .sidebar .theme-toggle span { display: none; } + .sidebar .nav-item { justify-content: center; padding: 0.7rem 0; } + .sidebar .nav-icon { margin-right: 0; } + #mainContent { margin-left: var(--sidebar-collapsed-width); } } -/* Mobile Responsiveness Improvements */ @media (max-width: 768px) { + .mobile-col { flex-direction: column !important; } + .mobile-w-full { width: 100% !important; } + .mobile-items-start { align-items: flex-start !important; } + .mobile-gap-4 { gap: 1rem !important; } + .mobile-border-0 { border: none !important; } + .container { padding: 0 1rem; } - /* Layout Helpers */ - .mobile-col { - flex-direction: column !important; - } + :root { --sidebar-collapsed-width: 0px; } + .sidebar { transform: translateX(-100%); } + .sidebar:not(.collapsed) { transform: translateX(0); width: 100%; background: var(--surface); } + body.sidebar-collapsed #mainContent { margin-left: 0; } + #mainContent { margin-left: 0 !important; } - .mobile-w-full { - width: 100% !important; - } - - .mobile-items-start { - align-items: flex-start !important; - } - - .mobile-gap-4 { - gap: 1rem !important; - } - - .mobile-border-0 { - border: none !important; - } - - /* Container adjustments */ - .container { - padding: 0 1rem; - } - - /* Sidebar Overrides for Mobile */ - :root { - --sidebar-collapsed-width: 0px; - /* Hide completely on mobile if collapsed */ - } - - .sidebar { - transform: translateX(-100%); - } - - .sidebar:not(.collapsed) { - transform: translateX(0); - width: 100%; - /* Full width sidebar on mobile */ - background: rgba(10, 10, 10, 0.95); - /* Darker opaque bg */ - } - - body.sidebar-collapsed #mainContent { - margin-left: 0; - } - - #mainContent { - margin-left: 0 !important; - /* Always 0 on mobile unless overlay logic added, but for now stack */ - } - - /* Video Detail Specifics */ - .video-header-image { - width: 100% !important; - height: auto !important; - aspect-ratio: 16/9; - margin-bottom: 1rem; - } - - /* Stats Grid */ - .stats-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 1rem; - width: 100%; - } - - /* Tabs */ - .tabs-container { - overflow-x: auto; - padding-bottom: 0.5rem; - margin-bottom: 1rem; - -webkit-overflow-scrolling: touch; - } - - .tab-btn { - white-space: nowrap; - } - - /* Mobile Menu Toggle */ + .video-header-image { width: 100% !important; height: auto !important; aspect-ratio: 16/9; margin-bottom: 1rem; } + .stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; width: 100%; } + .tabs-container { overflow-x: auto; padding-bottom: 0.5rem; margin-bottom: 1rem; -webkit-overflow-scrolling: touch; } + .tab-btn { white-space: nowrap; } .mobile-menu-btn { - display: flex !important; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - margin-bottom: 1rem; - background: transparent; - color: white; - border: 1px solid var(--glass-border); - border-radius: var(--radius-md); - cursor: pointer; + display: flex !important; align-items: center; justify-content: center; + width: 40px; height: 40px; margin-bottom: 1rem; + background: var(--surface); color: var(--text); + border: 1px solid var(--border); border-radius: var(--radius-md); cursor: pointer; } } +.mobile-menu-btn { display: none; } -/* Default hidden for desktop */ -.mobile-menu-btn { - display: none; -} - -/* Mobile Responsive Tables (Card View) */ +/* Mobile responsive tables (card view) */ @media (max-width: 768px) { - - table, - thead, - tbody, - th, - td, - tr { - display: block; - } - - /* Hide table headers (but not display:none for accessibility, usually, but here simplicity) */ - thead tr { - position: absolute; - top: -9999px; - left: -9999px; - } - - tr { - margin-bottom: 1rem; - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--glass-border); - border-radius: var(--radius-md); - padding: 1rem; - } - - td { - border: none; - position: relative; - padding-left: 50% !important; - /* Make space for label */ - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - min-height: 2rem; - display: flex; - align-items: center; - text-align: right; - justify-content: flex-end; - } - - td::before { - content: attr(data-label); - position: absolute; - left: 1rem; - width: 45%; - padding-right: 10px; - white-space: nowrap; - text-align: left; - font-weight: bold; - color: var(--text-muted); - } - - /* Specific adjustments for certain columns if needed */ - td[data-label="Video"] { - padding-left: 0 !important; - display: block; - text-align: left; - } - - td[data-label="Video"]::before { - display: none; - /* Hide label for video title/thumb block to give it full width */ - } - - td[data-label="Rank"] { - display: inline-block; - padding: 0 !important; - margin-bottom: 0.5rem; - width: auto; - } - - td[data-label="Rank"]::before { - display: none; - } - - td[data-label="Rank"] span { - font-size: 0.875rem !important; - background: rgba(var(--primary-rgb), 0.1); - /* fallback if var not ready */ - padding: 2px 8px; - border-radius: 4px; - color: var(--text-muted); - border: 1px solid var(--glass-border); - } - - /* Customize Rank appearance to look like a badge */ + table, thead, tbody, th, td, tr { display: block; } + thead tr { position: absolute; top: -9999px; left: -9999px; } + tr { margin-bottom: 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 1rem; } + td { border: none; position: relative; padding-left: 50% !important; padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; min-height: 2rem; display: flex; align-items: center; text-align: right; justify-content: flex-end; } + td::before { content: attr(data-label); position: absolute; left: 1rem; width: 45%; padding-right: 10px; white-space: nowrap; text-align: left; font-weight: bold; color: var(--text-muted); } + td[data-label="Video"] { padding-left: 0 !important; display: block; text-align: left; } + td[data-label="Video"]::before { display: none; } + td[data-label="Rank"] { display: inline-block; padding: 0 !important; margin-bottom: 0.5rem; width: auto; } + td[data-label="Rank"]::before { display: none; } + td[data-label="Rank"] span { font-size: 0.875rem !important; background: var(--accent-soft); padding: 2px 8px; border-radius: 4px; color: var(--text-muted); border: 1px solid var(--border); } } -/* ===== Shared components (added) ===== */ +/* ===== Shared components ===== */ -/* Progress bar (dashboard funnel, distributions, …) */ -.bar-track { - flex: 1; - height: 8px; - background: rgba(255, 255, 255, 0.06); - border-radius: var(--radius-full); - overflow: hidden; -} -.bar-fill { - height: 100%; - border-radius: var(--radius-full); - transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1); -} -@media (prefers-reduced-motion: reduce) { - .bar-fill { transition: none; } -} +/* Progress bar */ +.bar-track { flex: 1; height: 8px; background: var(--inset); border-radius: var(--radius-full); overflow: hidden; } +.bar-fill { height: 100%; border-radius: var(--radius-full); transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1); } +@media (prefers-reduced-motion: reduce) { .bar-fill { transition: none; } } /* Badge */ .badge { - display: inline-flex; - align-items: center; - gap: 4px; - font-size: 0.7rem; - font-weight: 600; - padding: 2px 8px; - border-radius: var(--radius-full); - border: 1px solid transparent; - line-height: 1.4; + display: inline-flex; align-items: center; gap: 4px; + font-size: 0.7rem; font-weight: 700; + padding: 2px 8px; border-radius: 6px; + border: 1px solid transparent; line-height: 1.4; } -.badge-muted { background: rgba(255,255,255,0.06); color: var(--text-secondary); } -.badge-primary { background: rgba(var(--primary-rgb), 0.15); color: var(--primary); } -.badge-success { background: rgba(var(--success-rgb), 0.15); color: var(--success); } -.badge-warning { background: rgba(245, 158, 11, 0.15); color: var(--warning); } -.badge-danger { background: rgba(var(--danger-rgb), 0.15); color: var(--danger); } +.badge-muted { background: var(--surface-2); color: var(--text-secondary); } +.badge-primary { background: var(--accent-soft); color: var(--accent); } +.badge-success { background: rgba(var(--success-rgb), 0.14); color: var(--success); } +.badge-warning { background: rgba(245, 158, 11, 0.14); color: var(--warning); } +.badge-danger { background: rgba(var(--danger-rgb), 0.14); color: var(--danger); } /* Skeleton shimmer */ -.skeleton { - position: relative; - overflow: hidden; - background: rgba(255, 255, 255, 0.05); - border-radius: var(--radius-md); -} +.skeleton { position: relative; overflow: hidden; background: var(--surface-2); border-radius: var(--radius-md); } .skeleton::after { - content: ""; - position: absolute; - inset: 0; - transform: translateX(-100%); - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent); + content: ""; position: absolute; inset: 0; transform: translateX(-100%); + background: linear-gradient(90deg, transparent, var(--hover-strong), transparent); animation: skeleton-shimmer 1.4s infinite; } @keyframes skeleton-shimmer { 100% { transform: translateX(100%); } } -@media (prefers-reduced-motion: reduce) { - .skeleton::after { animation: none; } -} +@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } } -/* Dark form controls (unify select/input across pages) */ +/* Form controls */ select, input[type="text"], input[type="search"], input[type="number"], input[type="date"], input[type="datetime-local"], textarea { - background: rgba(255, 255, 255, 0.04); - color: var(--text-primary); - border: 1px solid var(--glass-border); - border-radius: var(--radius-md); - padding: 0.5rem 0.75rem; + background: var(--surface); + color: var(--text); + border: 1px solid var(--border-strong); + border-radius: var(--r-sm); + padding: 0.5rem 0.7rem; font-family: inherit; font-size: 0.875rem; - transition: border-color 0.2s ease, background 0.2s ease; + transition: border-color 0.2s ease, box-shadow 0.2s ease; } select:focus, input[type="text"]:focus, input[type="search"]:focus, input[type="number"]:focus, input[type="date"]:focus, input[type="datetime-local"]:focus, textarea:focus { outline: none; - border-color: var(--primary); - background: rgba(255, 255, 255, 0.06); + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); } -select option { background: #0f172a; color: var(--text-primary); } +select option { background: var(--surface); color: var(--text); } -/* Table polish */ +/* Table */ table { width: 100%; border-collapse: collapse; } thead th { - text-align: left; - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--text-muted); - padding: 0.6rem 0.75rem; - border-bottom: 1px solid var(--glass-border); + text-align: left; font-size: 0.75rem; font-weight: 600; + text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); + padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--border); } -tbody td { padding: 0.6rem 0.75rem; border-bottom: 1px solid rgba(255, 255, 255, 0.04); } +tbody td { padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--border); } tbody tr { transition: background 0.15s ease; } -tbody tr:hover { background: rgba(255, 255, 255, 0.03); } \ No newline at end of file +tbody tr:hover { background: var(--hover); } diff --git a/src/main/resources/static/css/variables.css b/src/main/resources/static/css/variables.css index 1dee211..938a789 100644 --- a/src/main/resources/static/css/variables.css +++ b/src/main/resources/static/css/variables.css @@ -1,60 +1,104 @@ +/* =========================================================================== + Design tokens — Refined SaaS, LIGHT default + DARK via [data-theme="dark"]. + Old variable names are aliased to theme tokens so existing markup adapts. + =========================================================================== */ :root { - /* Premium Dark Theme Palette (Cyber/Space) */ + /* Surfaces (light) */ + --bg: #f6f7f9; + --surface: #ffffff; + --surface-2: #f1f3f6; + --inset: #eef0f3; + --border: #e6e8ec; + --border-strong: #d9dce1; - /* Backgrounds */ - --bg-primary: #050508; - /* Ultra dark, almost black */ - --bg-glass: rgba(15, 23, 42, 0.6); + /* Text (light) */ + --text: #15181d; + --text-2: #5a626d; + --text-3: #9aa1ad; - /* Glassmorphism */ - --glass-border: rgba(255, 255, 255, 0.08); - --glass-highlight: rgba(255, 255, 255, 0.15); - --backdrop-blur: 12px; + /* On-surface translucent (hover/active) */ + --hover: rgba(16, 24, 40, .045); + --hover-strong: rgba(16, 24, 40, .07); - /* Typography */ - --text-primary: #f1f5f9; - --text-secondary: #94a3b8; - --text-muted: #64748b; - - /* Brand Colors (Neon/Vibrant) */ - --primary: #3b82f6; - --primary-glow: rgba(59, 130, 246, 0.5); - --primary-gradient: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); + /* Elevation */ + --shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 4px 12px rgba(16, 24, 40, .05); + --shadow-lg: 0 10px 30px rgba(16, 24, 40, .10); + --side-bg: #ffffff; + /* Accent + status (shared across themes) */ + --accent: #4f7cff; + --accent-2: #7c5cff; + --accent-soft: rgba(79, 124, 255, .10); + --primary: #4f7cff; + --primary-gradient: linear-gradient(135deg, #4f7cff 0%, #7c5cff 100%); + --primary-glow: rgba(79, 124, 255, .35); --success: #10b981; - --danger: #ef4444; - - /* UI Elements */ - --radius-md: 12px; - --radius-lg: 20px; - --radius-full: 9999px; - - /* Semantic (added) */ + --danger: #f43f5e; --warning: #f59e0b; - --primary-rgb: 59, 130, 246; /* matches --primary #3b82f6; used by rgba(var(--primary-rgb), …) */ + --green: #10b981; + --amber: #f59e0b; + --red: #f43f5e; + --purple: #8b5cf6; + --sky: #0ea5e9; + --primary-rgb: 79, 124, 255; --success-rgb: 16, 185, 129; - --danger-rgb: 239, 68, 68; + --danger-rgb: 244, 63, 94; - /* Spacing scale */ - --space-1: 0.25rem; - --space-2: 0.5rem; - --space-3: 0.75rem; + /* === Back-compat aliases (old names → theme tokens) === */ + --bg-primary: var(--bg); + --bg-glass: var(--surface); + --glass-border: var(--border); + --glass-highlight: var(--border-strong); + --text-primary: var(--text); + --text-secondary: var(--text-2); + --text-muted: var(--text-3); + --bg-hover: var(--surface-2); + --backdrop-blur: 0px; + + /* Radius / spacing / font */ + --radius-md: 10px; + --radius-lg: 14px; + --radius-full: 9999px; + --r: 12px; + --r-sm: 9px; + --space-1: .25rem; + --space-2: .5rem; + --space-3: .75rem; --space-4: 1rem; --space-6: 1.5rem; --space-8: 2rem; - --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } +/* ===== Dark theme overrides ===== */ +:root[data-theme="dark"] { + --bg: #0a0b0e; + --surface: #131519; + --surface-2: #191c22; + --inset: #0e1014; + --border: rgba(255, 255, 255, .08); + --border-strong: rgba(255, 255, 255, .13); + + --text: #e8eaef; + --text-2: #9aa1ad; + --text-3: #6b7280; + + --hover: rgba(255, 255, 255, .05); + --hover-strong: rgba(255, 255, 255, .09); + + --shadow: 0 1px 2px rgba(0, 0, 0, .3); + --shadow-lg: 0 12px 30px rgba(0, 0, 0, .45); + --side-bg: linear-gradient(180deg, #0c0e12, #0a0b0e); + --accent-soft: rgba(79, 124, 255, .16); +} + body { - background-color: var(--bg-primary); - background-image: radial-gradient(circle at 15% 50%, rgba(56, 189, 248, 0.08), transparent 25%), - radial-gradient(circle at 85% 30%, rgba(99, 102, 241, 0.08), transparent 25%); - background-attachment: fixed; - color: var(--text-primary); + background-color: var(--bg); + color: var(--text); font-family: var(--font-sans); font-feature-settings: "tnum" 1, "cv01" 1; margin: 0; -webkit-font-smoothing: antialiased; min-height: 100vh; -} \ No newline at end of file + transition: background-color .2s ease, color .2s ease; +} diff --git a/src/main/resources/static/js/common.js b/src/main/resources/static/js/common.js index 693f63f..09197fe 100644 --- a/src/main/resources/static/js/common.js +++ b/src/main/resources/static/js/common.js @@ -1,3 +1,12 @@ +// Theme toggle (light/dark). State applied pre-paint in base.html
. +function toggleTheme() { + const root = document.documentElement; + const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; + root.setAttribute('data-theme', next); + try { localStorage.setItem('theme', next); } catch (e) { /* ignore */ } + if (window.lucide) window.lucide.createIcons(); +} + // Sidebar Logic document.addEventListener('DOMContentLoaded', () => { const sidebar = document.getElementById('sidebar'); diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index 1b7b96d..1a7f1eb 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -193,7 +193,7 @@ else { opList.innerHTML = op.map((v,i)=>{ const ratio = v.viewsPerSubRatio!=null ? Number(v.viewsPerSubRatio).toFixed(1)+'x' : '-'; - return '' + + return '' + '발행 패키지가 없습니다.
' : '