From 6a3cd074e947532962603094580417f2fa5dbc81 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Wed, 15 Jul 2026 20:04:02 +0200 Subject: [PATCH] fix(ui): correct stack-form field-hint and checkbox layout on settings pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The #635 stack-form rules were verified against Billing/Auswertung but not the settings pages, where two layouts broke: - A DateField's "YYYY-MM-DD" hint floated to the far right: the label right-align (`.field > span`) also caught the hint span, which spans the full width. Scope the right-align to the label (`:first-child`) and place a field's hint under its control (column 2). - A checkbox with its own hint (Personio) stranded the box above its label: sparse grid auto-placement — the box has an explicit column and precedes the label in the DOM — can't put the later label back on row 1. Pin the rows: label + box on row 1, hint under the box on row 2. Verified at 1440px on the sync and account settings pages. Signed-off-by: Sebastian Mendel --- frontend/src/styles/app.css | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index ccc39759a..47983de0f 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -869,8 +869,10 @@ main:has(.tracking) { grid-column: 2; } + /* A field's hint sits under its control (column 2), not full-width under the + label — so a date field's "YYYY-MM-DD" hint lines up beneath the input. */ .stack-form .field > .field-hint { - grid-column: 1 / -1; + grid-column: 2; margin: 0.15em 0 0; } @@ -889,16 +891,18 @@ main:has(.tracking) { 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 { + /* Right-align only the LABEL (the first span) in the beside layout, so its edge + sits at the control's left edge (tight label↔field coupling). Scoped to + :first-child so it never catches a field's other spans — e.g. the DateField's + .date-field-wrap or its .field-hint, which must not be pushed to the right. */ + .stack-form .field > span:first-child { text-align: right; } - .stack-form .field.multiselect > span, - .stack-form .field:has(textarea) > span, - .stack-form .field-row .field > span { + /* The full-width exceptions keep their label on top, left-aligned. */ + .stack-form .field.multiselect > span:first-child, + .stack-form .field:has(textarea) > span:first-child, + .stack-form .field-row .field > span:first-child { text-align: left; } @@ -913,16 +917,30 @@ main:has(.tracking) { grid-template-columns: 18em minmax(0, 22em); } - .stack-form .field-check > span { + /* Pin the rows explicitly: label + box share row 1, an optional hint sits under + the box on row 2. Without pinned rows, sparse auto-placement (the box has an + explicit col 2 and comes first in the DOM) pushes the label onto row 2, leaving + the box stranded above it — visible only once a label wraps or a hint is present. */ + .stack-form .field-check > span:not(.field-hint) { grid-column: 1; + grid-row: 1; text-align: right; } .stack-form .field-check > input { grid-column: 2; + grid-row: 1; justify-self: start; } + /* A checkbox's own hint (e.g. Personio's "available once configured" note) sits + under the box in column 2. */ + .stack-form .field-check > .field-hint { + grid-column: 2; + grid-row: 2; + margin: 0.15em 0 0; + } + /* 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 {