From 5525a0317941de4802a2cbf37bb5a2cd0e8c25b9 Mon Sep 17 00:00:00 2001 From: luxick Date: Fri, 5 Jun 2026 10:47:38 +0200 Subject: [PATCH] Pin toolbar to the bottom on mobile --- assets/editor/main.html | 2 +- assets/editor/main.js | 21 +++++++++++++++++---- assets/layout.html | 2 +- assets/style.css | 24 +++++++++++++++++++++--- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/assets/editor/main.html b/assets/editor/main.html index 20d49b6..f1aeb6d 100644 --- a/assets/editor/main.html +++ b/assets/editor/main.html @@ -13,6 +13,7 @@
+ @@ -44,7 +45,6 @@ - diff --git a/assets/editor/main.js b/assets/editor/main.js index 1e5e319..6c97bb9 100644 --- a/assets/editor/main.js +++ b/assets/editor/main.js @@ -212,15 +212,28 @@ // --- Dropdowns --- - // The toolbar scrolls horizontally on mobile, which makes it an overflow - // container that would clip its absolutely-positioned dropdown menus. Pin an - // open menu to the viewport under its trigger so it escapes the clip. + // The toolbar scrolls horizontally (so it clips its absolutely-positioned + // menus) and on mobile is fixed to the bottom of the viewport. Pin an open + // menu to the viewport so it escapes the clip, opening upward when there + // isn't room below it (the bottom-toolbar case). var toolbar = document.querySelector('.editor-toolbar'); function pinMenu(toggle, menu) { if (!menu.classList.contains('is-open')) return; var r = toggle.getBoundingClientRect(); + var vh = window.innerHeight; menu.style.position = 'fixed'; - menu.style.top = r.bottom + 'px'; + menu.style.overflowY = 'auto'; + var spaceBelow = vh - r.bottom; + var spaceAbove = r.top; + if (spaceBelow < menu.offsetHeight + 8 && spaceAbove > spaceBelow) { + menu.style.top = 'auto'; + menu.style.bottom = (vh - r.top) + 'px'; + menu.style.maxHeight = (spaceAbove - 8) + 'px'; + } else { + menu.style.bottom = 'auto'; + menu.style.top = r.bottom + 'px'; + menu.style.maxHeight = (spaceBelow - 8) + 'px'; + } var left = Math.min(r.left, document.documentElement.clientWidth - menu.offsetWidth - 4); menu.style.left = Math.max(4, left) + 'px'; } diff --git a/assets/layout.html b/assets/layout.html index bd6c91e..36b6109 100644 --- a/assets/layout.html +++ b/assets/layout.html @@ -2,7 +2,7 @@ - + {{.Title}} diff --git a/assets/style.css b/assets/style.css index 8567694..db21ece 100644 --- a/assets/style.css +++ b/assets/style.css @@ -695,9 +695,27 @@ aside.sidebar:empty { display: none; } display: inline-flex; align-items: center; justify-content: center; - min-width: 2.75rem; - min-height: 2.75rem; - padding: 0 var(--space-2); + min-width: 2rem; + min-height: 2rem; + padding: 0 var(--space-1); + } + /* Pin the toolbar above the on-screen keyboard rather than at the top, which + is out of thumb reach while typing. interactive-widget=resizes-content + (layout.html viewport) shrinks the viewport on keyboard open so bottom: 0 + sits directly above it. cm-content reserves matching scroll space so the + last lines aren't hidden behind the bar. */ + body.edit-mode .editor-toolbar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 50; + border: none; + border-top: var(--border); + padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom)); + } + body.edit-mode .cm-content { + padding-bottom: calc(5rem + env(safe-area-inset-bottom)); } .modal-backdrop { padding: var(--space-2); align-items: flex-start; } .modal { max-width: none; margin-top: var(--space-4); }