diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 432081c..16d510f 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -11,6 +11,11 @@ html, body, #root { margin: 0; } +/* Высота каркаса — по видимой области (dvh): на мобильных 100% берётся от layout + viewport, который меняется при сворачивании адресной строки. Правило выше остаётся + фоллбэком для движков без dvh. */ +#root { height: 100dvh; } + body { background: var(--bg); color: var(--text); @@ -18,6 +23,7 @@ body { Arial, sans-serif; -webkit-font-smoothing: antialiased; overscroll-behavior-y: none; + overflow: hidden; /* прокручивается только .app-main */ } a { color: var(--accent-2); text-decoration: none; } @@ -190,7 +196,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent); } .toast { position: fixed; left: 50%; - bottom: calc(var(--bottombar-height) + 80px); + bottom: calc(var(--bottombar-height) + var(--safe-bottom) + 16px); transform: translateX(-50%); background: var(--surface-2); border: 1px solid var(--border); @@ -206,7 +212,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent); } /* ─── Уведомления ─────────────────────────────────────────────────────────── */ /* Зоны верхней панели: слева back/заголовок, по центру название, справа колокольчик */ -.top-bar-left { display: flex; align-items: center; min-width: 0; } +.top-bar-left { display: flex; align-items: center; min-width: 0; flex: 0 1 auto; } .top-bar-center { flex: 1; text-align: center; @@ -215,7 +221,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent); } white-space: nowrap; padding: 0 8px; } -.top-bar-right { display: flex; align-items: center; } +.top-bar-right { display: flex; align-items: center; flex: none; } .top-bar-home { background: none; border: none; @@ -223,6 +229,11 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent); } color: var(--text); font-weight: 700; font-size: 17px; + /* На узких экранах длинное название иначе распирает шапку по горизонтали. */ + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .notif-bell-wrap { position: relative; } @@ -258,7 +269,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent); } top: calc(100% + 8px); right: 0; width: min(320px, 86vw); - max-height: 70vh; + max-height: 70dvh; overflow-y: auto; background: var(--surface); border: 1px solid var(--border); diff --git a/frontend/src/styles/layout.css b/frontend/src/styles/layout.css index f0597ae..49e7137 100644 --- a/frontend/src/styles/layout.css +++ b/frontend/src/styles/layout.css @@ -2,17 +2,16 @@ position: relative; max-width: var(--app-max-w); margin: 0 auto; - min-height: 100%; + height: 100%; display: flex; flex-direction: column; background: var(--bg); box-shadow: 0 0 40px rgba(0, 0, 0, 0.5); } +/* Шапка вне прокручиваемой области по построению — sticky ей больше не нужен. */ .top-bar { - position: sticky; - top: 0; - z-index: 10; + flex: none; display: flex; align-items: center; justify-content: space-between; @@ -22,20 +21,24 @@ } .top-bar .title { font-weight: 700; font-size: 17px; } +/* Единственная прокручиваемая область приложения. */ .app-main { flex: 1; + min-height: 0; /* без этого flex-элемент не сжимается и прокрутка уходит в body */ overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; padding: 14px; - padding-bottom: calc(var(--bottombar-height) + var(--fab-height) + var(--safe-bottom) + 28px); + /* Подвал теперь в потоке — компенсировать нужно только выступающий купол FAB. */ + padding-bottom: 24px; } +/* Панель — обычный элемент флекс-колонки, не fixed: в мобильных браузерах со + скрывающейся адресной строкой fixed-подвал дёргается при прокрутке. + position: relative обязателен — на нём держится absolute-позиционирование .fab. */ .bottom-bar { - position: fixed; - bottom: 0; - left: 50%; - transform: translateX(-50%); - width: 100%; - max-width: var(--app-max-w); + position: relative; + flex: none; height: calc(var(--bottombar-height) + var(--safe-bottom)); padding-bottom: var(--safe-bottom); background: var(--surface); @@ -131,6 +134,7 @@ /* Вкладки админки: горизонтальная карусель (не ломают вёрстку при переполнении) */ .admin-tabs { + flex: none; display: flex; gap: 8px; padding: 10px 14px; @@ -218,7 +222,7 @@ position: fixed; top: 0; right: 0; - height: 100%; + height: 100dvh; width: min(300px, 84vw); background: var(--surface); border-left: 1px solid var(--border); diff --git a/frontend/src/styles/tokens.css b/frontend/src/styles/tokens.css index 44a25ae..586dee3 100644 --- a/frontend/src/styles/tokens.css +++ b/frontend/src/styles/tokens.css @@ -20,8 +20,11 @@ --fab-height: 62.5px; --radius: 14px; --radius-sm: 10px; - --safe-bottom: env(safe-area-inset-bottom, 0px); - --safe-top: env(safe-area-inset-top, 0px); + /* Safe-area с потолком: часть мобильных браузеров (DuckDuckGo, Firefox Focus) при + viewport-fit=cover отдаёт inset поверх уже смещённого вьюпорта — без ограничения + шапка раздувается вдвое, а её содержимое съезжает вниз. */ + --safe-bottom: min(env(safe-area-inset-bottom, 0px), 24px); + --safe-top: min(env(safe-area-inset-top, 0px), 24px); /* Брейкпоинты (для справки) */ --bp-lg: 768px;