From 73043d42d1b435955a7e4b66b06ca7d1a822e784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 16 May 2026 17:35:07 +0200 Subject: [PATCH] fix(settings): tile-padding parity, passkey breakpoint, scroll-into-view, F17 threshold toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five small polish items from the v1.4.33 audit, plus the maintainer's scroll-into-view item: * Export-section card padding promoted from `p-5` to `p-6` so the Export grid rhythm matches every other Settings card. * Passkey list flips from a desktop table to a mobile card list at `lg` instead of `md`. iPad portrait (768 px) used to sit on the `md` inflection and flip-flop between the two layouts on rotation; the card layout reads fine on iPad portrait, the table needs ~620 px of column width to show the destructive delete action without horizontal scroll. * `` mobile chip strip now scrolls its active chip into view on route change (`block: "nearest", inline: "center", behavior: "smooth"`). On a 393 CSS px viewport the 11-chip strip pushes the rightmost four chips below the fold; without the scroll-into-view, tapping one of those chips left the strip pinned to the leftmost chip on the new page, which read as a broken nav. Smooth scroll honours the user's `prefers-reduced-motion` via the browser's own auto-detection. * F17 — `thresholds-editor-section` toggle label anchors on the *action* it performs ("Eigene Werte" / "Custom range") instead of mirroring the current state ("Auto" / "Überschrieben"). The old label read as "Auto is off, why are there no inputs?" when the toggle was off and inputs were hidden; the new label unambiguously announces "flip me to enter a custom range". The `thresholds.sourceOverride` badge still surfaces when the override is the active source. Adds `thresholds.overrideToggleLabel`. Locale parity across de, en, es, fr, it, pl. Co-Authored-By: Claude Opus 4.7 (1M context) --- messages/de.json | 1 + messages/en.json | 1 + messages/es.json | 1 + messages/fr.json | 1 + messages/it.json | 1 + messages/pl.json | 1 + src/components/settings/account-section.tsx | 21 +++++++++++----- src/components/settings/export-section.tsx | 2 +- src/components/settings/settings-shell.tsx | 24 +++++++++++++++++++ .../settings/thresholds-editor-section.tsx | 14 ++++++++--- 10 files changed, 57 insertions(+), 10 deletions(-) diff --git a/messages/de.json b/messages/de.json index 545e59204..4abc23b6f 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1321,6 +1321,7 @@ "defaultLabel": "Default", "autoModeLabel": "Auto", "overrideModeLabel": "Überschrieben", + "overrideToggleLabel": "Eigene Werte", "resetAction": "Auf Default zurücksetzen", "resetAllAction": "Alle auf Default zurücksetzen", "minLabel": "Min", diff --git a/messages/en.json b/messages/en.json index 6639f5cde..05c96729c 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1321,6 +1321,7 @@ "defaultLabel": "Default", "autoModeLabel": "Auto", "overrideModeLabel": "Override", + "overrideToggleLabel": "Custom range", "resetAction": "Reset to default", "resetAllAction": "Reset all to default", "minLabel": "Min", diff --git a/messages/es.json b/messages/es.json index c3b6d7bc9..072956e69 100644 --- a/messages/es.json +++ b/messages/es.json @@ -1321,6 +1321,7 @@ "defaultLabel": "Default", "autoModeLabel": "Auto", "overrideModeLabel": "Override", + "overrideToggleLabel": "Custom range", "resetAction": "Reset to default", "resetAllAction": "Reset all to default", "minLabel": "Mín", diff --git a/messages/fr.json b/messages/fr.json index 0d7d65f87..1f32a3c87 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -1321,6 +1321,7 @@ "defaultLabel": "Default", "autoModeLabel": "Auto", "overrideModeLabel": "Override", + "overrideToggleLabel": "Custom range", "resetAction": "Reset to default", "resetAllAction": "Reset all to default", "minLabel": "Min", diff --git a/messages/it.json b/messages/it.json index 400746aed..aa96d2f21 100644 --- a/messages/it.json +++ b/messages/it.json @@ -1321,6 +1321,7 @@ "defaultLabel": "Default", "autoModeLabel": "Auto", "overrideModeLabel": "Override", + "overrideToggleLabel": "Custom range", "resetAction": "Reset to default", "resetAllAction": "Reset all to default", "minLabel": "Min", diff --git a/messages/pl.json b/messages/pl.json index 8399074df..f815e202e 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -1321,6 +1321,7 @@ "defaultLabel": "Default", "autoModeLabel": "Auto", "overrideModeLabel": "Override", + "overrideToggleLabel": "Custom range", "resetAction": "Reset to default", "resetAllAction": "Reset all to default", "minLabel": "Min", diff --git a/src/components/settings/account-section.tsx b/src/components/settings/account-section.tsx index ba833ad58..08862f54a 100644 --- a/src/components/settings/account-section.tsx +++ b/src/components/settings/account-section.tsx @@ -740,7 +740,13 @@ function PasskeyListSection({ isAuthenticated }: { isAuthenticated: boolean }) { table only at `≥ md`, and at `< md` paint a card-list where every passkey's name, device type, backup status, created date, and delete action are all visible without scrolling. */} -
+ {/* v1.4.33 — desktop table flips to card list at `lg` instead + of `md` so iPad portrait (768 px = exactly the `md` + inflection) lands on the card layout. The table needs + ~620 px of column width to read; below that it scrolls + horizontally and the destructive delete column is the one + that disappears. */} +
@@ -824,12 +830,15 @@ function PasskeyListSection({ isAuthenticated }: { isAuthenticated: boolean }) {
- {/* Mobile card list — < md only. Each passkey gets its own - card so the delete action stays visible and tap-targetable - (44px) without the user having to horizontally scroll - through a wide table. */} + {/* Mobile + tablet card list — < lg only. Each passkey gets + its own card so the delete action stays visible and + tap-targetable (44 px) without the user having to + horizontally scroll through a wide table. v1.4.33 bumped + the breakpoint from `md` to `lg` so iPad portrait (768 px) + stays on the card layout instead of flipping between + layouts on rotation. */}
    {passkeys.map((pk) => ( diff --git a/src/components/settings/export-section.tsx b/src/components/settings/export-section.tsx index 32ac496b4..92ebbfde9 100644 --- a/src/components/settings/export-section.tsx +++ b/src/components/settings/export-section.tsx @@ -122,7 +122,7 @@ function ExportCardShell({ return (
    diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index 787d415aa..72b964f18 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -135,6 +135,29 @@ export function SettingsShell({ active, children }: SettingsShellProps) { const { t } = useTranslations(); const activeSlug = deriveActiveSlug(pathname, active); + // v1.4.33 IW4 — scroll the active chip into view inside the + // horizontal mobile strip. On a 393 CSS px viewport the strip + // is 11 chips wide and the rightmost four sit below the fold; a + // user who tapped one of those chips lands on the new route + // with the strip still scrolled to the leftmost chip, which + // reads as if the navigation is broken. `inline: "center"` + // anchors the active chip in the visible middle of its scroll + // container so the user keeps spatial orientation between + // sections. + const mobileStripRef = React.useRef(null); + React.useEffect(() => { + if (typeof window === "undefined") return; + const strip = mobileStripRef.current; + if (!strip) return; + const active = strip.querySelector('[aria-current="page"]'); + if (!active) return; + active.scrollIntoView({ + block: "nearest", + inline: "center", + behavior: "smooth", + }); + }, [activeSlug]); + // v1.4.25 W8 — AuthShell wraps the page in `px-4 py-6 md:px-6` // already, so this inner shell only carries the wider max-width. // Previously the duplicate `px-4 py-6 md:px-6 md:py-8` here was @@ -150,6 +173,7 @@ export function SettingsShell({ active, children }: SettingsShellProps) { page, which makes the page feel like every section card has an overflow problem. */}
    + {/* v1.4.33 F17 — the switch label used to flip between + "Auto" and "Überschrieben" with the current state. The + maintainer's audit caught that as confusing: a user + looking at a row with "Auto" + the toggle off reads it + as "Auto is off, why are there no inputs?" instead of + "flip the switch to enter a custom range". Anchor the + label on the *action* ("Eigene Werte" / "Custom range") + so the affordance is unambiguous; the + `thresholds.sourceOverride` badge to the right still + announces when the override is active. */}