From e20aa1d4d8db7827e2e83d805fa0739f5370cbda Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Tue, 11 Aug 2026 19:34:21 +0200 Subject: [PATCH] style(settings): pair Save and Close into one tray in the phone sheet header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Below 860px Save moves into the header (a bottom action bar would cost 60px of a phone sheet), which left the two ways OUT of the sheet sitting side by side in mismatched shapes: a fat accent pill next to a bare 1.5rem glyph with no box at all. They are the same decision (save-and-close vs discard-and-close), hit in the same corner with the same thumb, so they now share a recessed tray and matching pill geometry and read as one cluster. - 36px on both, so the tray comes out at 44px including its 3px padding and 1px border — the same height as the phone header it sits in. - `.modal-close` gets a real box (36x36, radius 9) only inside the tray; its bare-glyph form is still right in a plain modal header. - Tray colors come from skin tokens (--border/--bg-input). A hardcoded black alpha would render as a grey slab on the four light skins, the same trap the layout preview frame hit. - `:has(.set-head-save)` keeps the tray off the sheets that carry a lone x: Session Options and Add Case save from inside their own forms. - The shared focus ring offsets OUTWARD, which inside the tray would draw on top of the tray border, so it is inset to ring the button instead. DOM order stays close-then-save so the focus trap still lands on Close; row-reverse paints Save to its left. Verified at 390x844: tray 44px tall, Save 36px, Close 36x36, both radius 9 inside a 12-radius tray. PostCSS-parsed (prettier does not catch an unclosed CSS block, and styles.css is prettier-ignored by design). Co-Authored-By: Claude Opus 5 (1M context) --- src/web/public/mobile.css | 29 +++++++++++++++++++- src/web/public/styles.css | 58 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/web/public/mobile.css b/src/web/public/mobile.css index cfea0252..a28fa102 100644 --- a/src/web/public/mobile.css +++ b/src/web/public/mobile.css @@ -3157,9 +3157,36 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat border-radius: 10px; } - /* Save moves into the header; the bottom action bar would cost 60px */ + /* Save + Close are the two ways out of the sheet (save-and-close vs + discard-and-close), hit in the same corner with the same thumb, so here — + and only here, since Save is header-only below 860px — they share a + recessed tray and matching pill geometry instead of reading as a fat + accent pill parked beside a stray × glyph. Tray colors come from skin + tokens, never a hardcoded black alpha, or the light skins get a grey slab. + `:has()` keeps the tray off the two sheets that carry a lone × (Session + Options and Add Case save from inside their own forms). */ + :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions:has(.set-head-save) { + padding: 3px; + border: 1px solid var(--border); + border-radius: 12px; + background: var(--bg-input); + } + + /* Save moves into the header; the bottom action bar would cost 60px. Both + buttons grow to a thumb-sized target and keep identical heights so the + pair reads as one cluster — 36 + the tray's 3px padding and 1px border on + each side is a 44px block, the same height as the phone header. */ :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-save { display: inline-flex; + height: 36px; + padding: 0 16px; + font-size: 0.86rem; + } + + :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions .modal-close { + width: 36px; + height: 36px; + font-size: 1.35rem; } :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-foot { diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 1ed76252..1eb0fe08 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -14759,11 +14759,14 @@ html[data-skin="daylight-blue"] .welcome-btn-tunnel.active:hover { flex-shrink: 0; } +/* DOM order stays close-then-save so the focus trap lands on Close; + row-reverse paints Save to the left of it. Below 860px, where Save joins the + header, the two become ONE control cluster inside a tray (mobile.css). */ :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions { display: flex; flex-direction: row-reverse; align-items: center; - gap: 6px; + gap: 3px; margin-left: auto; } @@ -14771,16 +14774,65 @@ html[data-skin="daylight-blue"] .welcome-btn-tunnel.active:hover { instead; on desktop the footer owns Save, so this stays hidden. */ :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-save { display: none; + align-items: center; + justify-content: center; + height: 30px; font: inherit; font-size: 0.82rem; font-weight: 620; letter-spacing: -0.01em; - padding: 8px 15px; + padding: 0 14px; border: 0; - border-radius: 10px; + border-radius: 9px; cursor: pointer; background: linear-gradient(180deg, var(--accent-grad-a), var(--accent-grad-b)); color: var(--accent-ink); + transition: + filter 0.15s ease, + transform 0.1s ease; +} + +:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-save:hover { + filter: brightness(1.08); +} + +:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-save:active { + transform: scale(0.97); +} + +/* `.modal-close` ships as a bare 1.5rem glyph with no box — fine in a plain + modal header, but inside the tray it needs the same footprint as Save. */ +:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions .modal-close { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + padding: 0; + border-radius: 9px; + font-size: 1.2rem; + color: var(--text-dim); + background: transparent; + transition: + background 0.15s ease, + color 0.15s ease, + transform 0.1s ease; +} + +:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions .modal-close:hover { + background: var(--bg-hover); + color: var(--text); +} + +:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions .modal-close:active { + transform: scale(0.94); +} + +/* The shared rule offsets the ring OUTWARD, which inside the tray draws it on + top of the tray border. Inset it so the focused button is ringed, not the + cluster. */ +:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions .modal-close:focus-visible { + outline-offset: -2px; } :is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-body {