diff --git a/web/src/components/Kartenspalten.jsx b/web/src/components/Kartenspalten.jsx
new file mode 100644
index 0000000..87e9a91
--- /dev/null
+++ b/web/src/components/Kartenspalten.jsx
@@ -0,0 +1,47 @@
+import { Children, useEffect, useRef, useState } from "react";
+
+/**
+ * Karten in feste Spalten verteilen.
+ *
+ * Warum nicht CSS: Ein Raster richtet die Karten zeilenweise aus – die zweite
+ * Reihe beginnt erst unter der höchsten Karte der ersten, was bei sehr
+ * unterschiedlich hohen Karten grosse Löcher hinterlässt. Der CSS-Spaltenfluss
+ * (`column-width`) füllt zwar schön auf, verteilt aber ALLE Karten neu, sobald
+ * eine ihre Höhe ändert. Beim Bearbeiten oder nach einem Abruf sprang deshalb
+ * die halbe Seite.
+ *
+ * Hier bekommt jede Karte fest ihre Spalte (reihum). Wächst eine, verschiebt
+ * sich nur, was in derselben Spalte darunter steht – alles andere bleibt liegen.
+ */
+export default function Kartenspalten({ children, min = 380, gap = "var(--sp-4)" }) {
+ const container = useRef(null);
+ const [spalten, setSpalten] = useState(1);
+
+ useEffect(() => {
+ const el = container.current;
+ if (!el) return undefined;
+ const messen = () => {
+ const breite = el.getBoundingClientRect().width;
+ setSpalten(Math.max(1, Math.floor((breite + 24) / (min + 24))));
+ };
+ messen();
+ const beobachter = new ResizeObserver(messen);
+ beobachter.observe(el);
+ return () => beobachter.disconnect();
+ }, [min]);
+
+ const karten = Children.toArray(children).filter(Boolean);
+ const verteilt = Array.from({ length: spalten }, () => []);
+ karten.forEach((karte, i) => verteilt[i % spalten].push(karte));
+
+ return (
+
+ {verteilt.map((spalte, i) => (
+
+ {spalte}
+
+ ))}
+
+ );
+}
diff --git a/web/src/finance.css b/web/src/finance.css
index f66f352..042e86b 100644
--- a/web/src/finance.css
+++ b/web/src/finance.css
@@ -284,22 +284,6 @@
min-width: 0;
}
-/* Einstellungen & Mein Konto: Karten fliessen spaltenweise.
- Ein Raster richtet die Karten zeilenweise aus – die zweite Reihe beginnt erst
- unter der HOECHSTEN Karte der ersten, was bei sehr unterschiedlich hohen
- Karten ein grosses Loch hinterlaesst. Der Spaltenfluss stapelt stattdessen
- weiter, sobald oben Platz frei wird. Die Spalten sind bewusst breit: vier
- schmale Karten nebeneinander waren auf dem Notebook nicht mehr lesbar. */
-.settings-grid {
- column-width: 380px;
- column-gap: var(--sp-4);
-}
-.settings-grid > * {
- break-inside: avoid;
- margin-bottom: var(--sp-4);
- width: 100%;
-}
-
/* Nutzer-Chip in der Seitenleisten-Fußzeile ist ein Link auf „Mein Konto". */
a.sidebar-user {
text-decoration: none;
diff --git a/web/src/pages/Account.jsx b/web/src/pages/Account.jsx
index 470eb27..13b9795 100644
--- a/web/src/pages/Account.jsx
+++ b/web/src/pages/Account.jsx
@@ -6,6 +6,7 @@ import { api, ApiError } from "../api";
import { useToast } from "../toast";
import Icon from "../components/Icon";
import PasswordInput from "../components/PasswordInput";
+import Kartenspalten from "../components/Kartenspalten";
import PasswordRules from "../components/PasswordRules";
import Geraetemarken from "./settings/Geraetemarken";
import { DEFAULT_POLICY, meetsPolicy } from "../passwordPolicy";
@@ -56,7 +57,7 @@ export default function Account() {
return (
{t("account.title")}
-
+
-
+
);
}
diff --git a/web/src/pages/Settings.jsx b/web/src/pages/Settings.jsx
index e2a052c..257dc51 100644
--- a/web/src/pages/Settings.jsx
+++ b/web/src/pages/Settings.jsx
@@ -5,6 +5,7 @@ import { useAuth } from "../auth";
import { useToast } from "../toast";
import { api } from "../api";
import Icon from "../components/Icon";
+import Kartenspalten from "../components/Kartenspalten";
import PolicyCard from "./settings/PolicyCard";
import Postfaecher from "./settings/Postfaecher";
import RestoreCard from "./settings/RestoreCard";
@@ -76,7 +77,7 @@ export default function Settings() {
)}
-