diff --git a/app/static/css/app.css b/app/static/css/app.css index ee8cec7..65df60d 100644 --- a/app/static/css/app.css +++ b/app/static/css/app.css @@ -80,6 +80,18 @@ ul { box-shadow: var(--shadow); padding: 40px 36px; } +@media (max-width: 480px) { + .auth-container { + padding: 12px; + } + .auth-card { + padding: 28px 20px; + border-radius: 12px; + } + .auth-title { + font-size: 20px; + } +} .auth-logo { display: flex; @@ -1347,39 +1359,300 @@ ul { } /* ── Responsive ──────────────────────────────────────────────────── */ + +/* Mobile top bar — hidden on tablet+ */ +.mobile-topbar { + display: none; + align-items: center; + gap: 12px; + padding: 0 16px; + height: 54px; + background: var(--color-sidebar-bg); + color: #fff; + flex-shrink: 0; + position: sticky; + top: 0; + z-index: 50; +} +.btn-mobile-menu { + font-size: 22px; + color: rgba(255, 255, 255, 0.8); + background: none; + border: none; + padding: 4px 6px; + cursor: pointer; + flex-shrink: 0; +} +.btn-mobile-menu:hover { + color: #fff; +} +.mobile-logo { + flex: 1; + font-size: 16px; + font-weight: 700; + color: #fff; +} +.btn-fab-mobile { + width: 36px; + height: 36px; + font-size: 20px; + flex-shrink: 0; +} + +/* Sidebar backdrop — only active on mobile */ +.sidebar-backdrop { + display: none; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 89; +} +.sidebar-backdrop.open { + display: block; +} + +/* ── Phone (≤ 640px) ─────────────────────────────────────────────── */ @media (max-width: 640px) { + /* Layout: stack topbar above main, no sidebar space in flow */ + .app-layout { + flex-direction: column; + height: 100vh; + overflow: hidden; + } + + /* Topbar always visible */ + .mobile-topbar { + display: flex; + } + + /* Sidebar slides in from the left as an overlay */ .sidebar { - width: 72px; - overflow-y: visible; + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 260px !important; /* always full-width on mobile regardless of collapsed state */ + z-index: 90; + transform: translateX(-100%); + transition: transform 0.25s ease; + overflow-y: auto; } - .sidebar-header .logo-text, - .sidebar-label { - opacity: 0; - max-width: 0; + .sidebar.mobile-open { + transform: translateX(0); } - .sidebar-section-header { - opacity: 0; - pointer-events: none; - height: 0; - padding: 0; + /* Show all labels inside the mobile overlay sidebar */ + .sidebar .sidebar-label { + opacity: 1 !important; + max-width: none !important; } - .sidebar-item { - justify-content: center; - padding: 10px 0; + .sidebar .sidebar-section-header { + opacity: 1 !important; + height: auto !important; + padding: 12px 16px 4px !important; } + .sidebar .sidebar-item { + justify-content: flex-start !important; + padding: 9px 16px !important; + overflow: hidden !important; + } + .sidebar .sidebar-item::after { + display: none !important; + } + /* Hide desktop collapse toggle on mobile */ + .btn-sidebar-toggle { + display: none; + } + + /* Main area fills everything below topbar */ + .vault-main { + flex: 1; + overflow: hidden; + } + + /* Tighter padding */ .vault-header { padding: 12px 16px; + gap: 8px; + } + .vault-title { + font-size: 16px; } .vault-list { - padding: 8px 16px 16px; + padding: 8px 12px 80px; + } /* bottom pad for FAB */ + .panel-body { + padding: 16px; + } + .security-dashboard { + padding: 12px; + gap: 16px; + } + + /* Search takes full width, hide sort */ + .vault-toolbar { + gap: 8px; + } + .search-wrapper { + max-width: none; } .sort-wrapper { display: none; } + + /* FAB in header is hidden on mobile (use topbar FAB) */ + .vault-header .btn-fab { + display: none; + } + + /* Forms */ .form-row { flex-direction: column; gap: 0; } + .modal { + padding: 20px; + } + .modal-actions { + flex-wrap: wrap; + } + + /* Security stats wrap */ + .security-summary { + padding: 16px; + gap: 12px; + } + .sec-score { + font-size: 40px; + } + .sec-stats { + gap: 8px; + } + .sec-stat { + padding: 8px 12px; + min-width: 68px; + } + .sec-stat-num { + font-size: 22px; + } + + /* Modal: full-screen on phones */ + .modal-overlay { + padding: 0; + align-items: flex-end; + } + .modal { + max-width: 100%; + max-height: 92vh; + border-radius: 16px 16px 0 0; + padding: 20px 16px; + } + .modal-settings { + max-width: 100%; + } + .modal-item { + max-width: 100%; + } + + /* Detail label stacks above value on small screens */ + .detail-field { + flex-direction: column; + gap: 4px; + } + .detail-label { + width: auto; + } +} + +/* ── Tablet (641px – 1024px) ─────────────────────────────────────── */ +@media (min-width: 641px) and (max-width: 1024px) { + /* Sidebar always shows as icon rail on tablet — user can expand with toggle */ + .sidebar:not(.collapsed) { + width: 200px; + } + + /* Slightly tighter padding */ + .vault-header { + padding: 14px 18px; + } + .vault-list { + padding: 10px 18px 18px; + } + .panel-body { + padding: 16px 18px; + } + .security-dashboard { + padding: 16px; + gap: 16px; + } + + /* Search a bit narrower */ + .search-wrapper { + max-width: 280px; + } + + /* Security stats wrap gracefully */ + .security-summary { + flex-wrap: wrap; + gap: 12px; + } + .sec-stats { + flex-wrap: wrap; + gap: 10px; + } + + /* Form rows stay side-by-side */ + .form-row { + flex-direction: row; + } + + /* Modal fits tablet width */ + .modal { + max-width: 460px; + } +} + +/* ── Laptop (1025px – 1440px) ───────────────────────────────────── */ +@media (min-width: 1025px) and (max-width: 1440px) { + /* Full sidebar, comfortable padding */ + .vault-header { + padding: 16px 24px; + } + .vault-list { + padding: 12px 24px 24px; + } + .security-dashboard { + padding: 20px 24px; + } +} + +/* ── Large PC (> 1440px) ────────────────────────────────────────── */ +@media (min-width: 1441px) { + /* Wider sidebar, more generous spacing */ + .sidebar:not(.collapsed) { + width: 260px; + } + .vault-header { + padding: 18px 32px; + } + .vault-list { + padding: 16px 32px 32px; + } + .panel-body { + padding: 24px 32px; + } + .security-dashboard { + padding: 24px 32px; + gap: 28px; + } + .search-wrapper { + max-width: 440px; + } + .modal { + max-width: 520px; + } + .modal-settings { + max-width: 580px; + } } /* ── Settings — new sections (Phase 6) ──────────────────────────── */ diff --git a/app/static/js/vault.js b/app/static/js/vault.js index 3fc5a5d..665804c 100644 --- a/app/static/js/vault.js +++ b/app/static/js/vault.js @@ -2645,6 +2645,34 @@ const Vault = (() => { }); } + // Mobile sidebar: hamburger opens overlay, backdrop/item-click closes it + const backdrop = document.getElementById("sidebar-backdrop"); + const mobileMenuBtn = document.getElementById("btn-mobile-menu"); + function openMobileSidebar() { + sidebar?.classList.add("mobile-open"); + backdrop?.classList.add("open"); + document.body.style.overflow = "hidden"; + } + function closeMobileSidebar() { + sidebar?.classList.remove("mobile-open"); + backdrop?.classList.remove("open"); + document.body.style.overflow = ""; + } + mobileMenuBtn?.addEventListener("click", openMobileSidebar); + backdrop?.addEventListener("click", closeMobileSidebar); + // Close mobile sidebar whenever a nav item is clicked + document.querySelectorAll(".sidebar-item").forEach((item) => { + item.addEventListener("click", () => { + if (window.innerWidth <= 640) closeMobileSidebar(); + }); + }); + // Mobile FAB mirrors the desktop add-item button + document + .getElementById("btn-add-item-mobile") + ?.addEventListener("click", () => { + document.getElementById("btn-add-item")?.click(); + }); + if (!VaultSession.getKey()) { showUnlockOverlay(); } else { diff --git a/app/templates/vault/index.html b/app/templates/vault/index.html index cf086b8..cfd8478 100644 --- a/app/templates/vault/index.html +++ b/app/templates/vault/index.html @@ -1,6 +1,24 @@ {% extends "base.html" %} {% block title %}My Vault — PassKeeper{% endblock %} {% block body_class %}vault-page{% endblock %} {% block body %}
+ +
+ + + +
+ + + +