fix(ui): a longer label fits its control — pill value truncates, headers get their room (E30 S5)

Found in the Hungarian UAT: a toolbar SelectTrigger's text value wrapped out of the pill in a narrow
place (the playlists rail) — it now truncates in its own span; the Channels "Prioritás" and "Utolsó
feltöltés" headers were clipped by their fixed columns — sized for the label, funnel and sort arrow;
a sortable header is at least 24px wide (Hungarian "Ki" was a 12px target). PRE-EXISTING, any
language: the messages unlock button and "Forgot passphrase" sat flush — one gapped row.
This commit is contained in:
2026-09-30 01:08:29 +02:00
parent 12077fbebc
commit 95b94a1d72
4 changed files with 28 additions and 15 deletions
+10 -4
View File
@@ -33,7 +33,7 @@ export const SelectTrigger = forwardRef<
ref={ref}
type="button"
className={clsx(
"glass-hover inline-flex shrink-0 items-center gap-1.5 border text-left text-sm transition touch-target",
"glass-hover inline-flex max-w-full shrink-0 items-center gap-1.5 border text-left text-sm whitespace-nowrap transition touch-target",
context === "toolbar"
? "glass-card h-7 rounded-full px-3"
: "h-8 w-full rounded-lg bg-transparent px-2.5",
@@ -43,17 +43,23 @@ export const SelectTrigger = forwardRef<
{...rest}
>
{icon}
{label != null && <span className="text-muted">{label}:</span>}
{label != null && <span className="shrink-0 text-muted">{label}:</span>}
{/* The toolbar value is a row, whatever the caller puts in it: an <svg> is display:block under the
preflight, and in a plain span it broke the value onto its own line (the saved-view chip,
E28). The form value is text that truncates, which a flex box would not do. */}
E28). The form value is text that truncates, which a flex box would not do — nor would a bare
text node in the toolbar row, so a text value gets its own truncating span: in a narrow place
(the playlists rail) a long value ("Rendezés: Egyéni sorrend") used to wrap out of the pill. */}
<span
className={clsx(
"text-fg",
context === "form" ? "flex-1 truncate" : "flex min-w-0 items-center gap-1.5",
)}
>
{children}
{context === "toolbar" && typeof children === "string" ? (
<span className="truncate">{children}</span>
) : (
children
)}
</span>
{trailing}
<ChevronDown
+3 -1
View File
@@ -229,10 +229,12 @@ export function ColumnHead<T>({
{/* Only a sortable header is a control. A non-sortable one used to render a
focusable no-op button, which put unlabeled dead stops in the tab order —
two of them per playlist table, whose grip and actions headers are empty. */}
{/* min-w-6 as well as min-h-6: a short header ("Ki", Hungarian "Who") was a 12px
target beside its funnel, under the 24px floor. */}
{col.sortable ? (
<Pressable
onClick={() => onToggleSort(col.key)}
className="inline-flex min-h-6 items-center gap-1 hover:text-fg cursor-pointer touch-target"
className="inline-flex min-h-6 min-w-6 items-center gap-1 hover:text-fg cursor-pointer touch-target"
>
{col.header}
{isSorted &&