From bf811e7d9193e20e8da2ab697225533d0819a0f1 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Wed, 15 Jul 2026 18:42:11 +0200 Subject: [PATCH] fix(ui): unify form comboboxes and align controls (SearchableSelect review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SearchableSelect now uses one layout for single and multi: the input is the search field, the value(s) show as chips, and opening always lists all options (the current selection highlights, it no longer pre-filters). The popup renders inline with a fixed positioning strategy, so a modal dialog's focus trap can no longer inert it — the Billing (Monatsabrechnung) selects open again. - Drop the leading search magnifier; the trailing ▾ is the single affordance. - Native wears the same neutral ▾ as the searchable combobox next to it + (Billing has both in one form). appearance:none drops the OS arrow — which looks + nothing like the combobox one — for a hand-drawn triangle; the #6b7280 grey reads + on both light and dark grounds and tracks the combobox's --color-text-muted ▾. */ +.field select { + appearance: none; + padding-right: 1.8rem; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='%236b7280' d='M0 0h10L5 7z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.65rem center; +} + /* Date field whose typed value couldn't be parsed (in the user's date format or ISO). The :focus-visible variant keeps the danger outline while the field is focused — it out-specifies the generic input:focus-visible accent rule. */ @@ -876,6 +888,47 @@ main:has(.tracking) { .stack-form .field-row .field > * { grid-column: auto; } + + /* Right-align labels in the beside layout so the label's edge sits at the + control's left edge (tight label↔field coupling) — except the full-width + exceptions above, whose label sits on top and stays left-aligned. */ + .stack-form .field > span { + text-align: right; + } + + .stack-form .field.multiselect > span, + .stack-form .field:has(textarea) > span, + .stack-form .field-row .field > span { + text-align: left; + } + + /* Checkboxes/radios join the same two-column rhythm: label right-aligned in + column 1, the control at the START of column 2 — so every control type (input, + select, checkbox, radio) shares one left edge. Grid placement reorders the + existing markup visually; no markup change needed. */ + .stack-form .field-check { + align-items: center; + display: grid; + gap: 0.3rem 1.2rem; + grid-template-columns: 18em minmax(0, 22em); + } + + .stack-form .field-check > span { + grid-column: 1; + text-align: right; + } + + .stack-form .field-check > input { + grid-column: 2; + justify-self: start; + } + + /* Help-icon rows keep the compact inline pattern (checkbox + its ⓘ side by side), + so they opt out of the two-column grid. */ + .stack-form .field-check-row .field-check { + display: flex; + grid-template-columns: none; + } } /* ---- Settings: grouped preference cards ---- */ @@ -2575,40 +2628,6 @@ kbd { opacity: 0.6; } -/* SINGLE: the current value fills the row, a trailing ▾ opens the popup; the - whole control is the click target. */ -.searchable-select-trigger { - align-items: center; - background: none; - border: 0; - color: var(--color-text); - cursor: pointer; - display: flex; - flex: 1; - font: inherit; - gap: 0.3rem; - justify-content: space-between; - min-height: 40px; - padding: 0; - text-align: start; -} - -.searchable-select.is-disabled .searchable-select-trigger { - cursor: not-allowed; -} - -.searchable-select-value { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.combobox-trigger-glyph { - color: var(--color-accent); - flex: none; - font-size: 0.8rem; -} - /* MULTI: the cell IS the search field — surface fill + accent border + focus ring, with the magnifier, chips and input on one (wrapping) row. */ .data-table td[data-inline-editing] .chip-select.inline-tags { @@ -2681,6 +2700,14 @@ kbd { padding: 0 0.15rem; } +/* The FORM control's ▾ matches the native