UI-Ueberarbeitung: responsives Grid, konsistente Buttons, Wizard-Breite, Kontrast
- Uebungen in responsivem Grid (2-3 Spalten auf Desktop, 1 Spalte mobil) - Setup-Modal feste Breite 640px (capped am Viewport) - kein Sprung beim Sprachwechsel - Button-Konsistenz: .btn-primary nutzt --accent-border (weisser Akzent bekommt sichtbare graue Umrandung, auch auf hellem Theme); Secondary = transparent/dunkelgrau - Fortschrittssegmente: groesser (40x6, gap 8); Semantik wie gewuenscht - erledigt = voll gefuellt, aktuell = nur Umrandung, kommend = graue Umrandung - Session-Kopf: 'Alles zuruecksetzen' + 'Session abschliessen' gebuendelt, 'Verwerfen' abgesetzt und bestaetigungsgeschuetzt - Wizard: Appearance-Untertitel korrigiert (nicht mehr 'default accent'), Sprach-Buttons mit mehr Abstand - Settings: Formular-Zeilen ausgerichtet (Inputs gestreckt, Button rechts), muted heller (bessere Lesbarkeit), Checkbox groesser - Tests: 158 Checks gruen (39+18+43+32+26)
This commit is contained in:
1 parent
a2c4fdc1bd
commit
ba599fcbf5
2 files changed
+75
-33
No files matched your search
@@ -141,6 +141,7 @@ const LANG = {
|
|||||||
system: "System",
|
system: "System",
|
||||||
oled_toggle: "OLED-Dark",
|
oled_toggle: "OLED-Dark",
|
||||||
oled_desc: "Reines Schwarz für den dunklen Modus.",
|
oled_desc: "Reines Schwarz für den dunklen Modus.",
|
||||||
|
appearance_desc: "Theme, Akzentfarbe und OLED-Dark für deine App.",
|
||||||
last_workout: "Letztes Training vorbefüllen",
|
last_workout: "Letztes Training vorbefüllen",
|
||||||
last_workout_desc: "Neue Session startet mit dem letzten abgeschlossenen Workout der Kategorie (nur ausgefüllte Sätze).",
|
last_workout_desc: "Neue Session startet mit dem letzten abgeschlossenen Workout der Kategorie (nur ausgefüllte Sätze).",
|
||||||
plan_prefill: "Plan-Gewichte vorbefüllen",
|
plan_prefill: "Plan-Gewichte vorbefüllen",
|
||||||
@@ -249,6 +250,7 @@ const LANG = {
|
|||||||
system: "System",
|
system: "System",
|
||||||
oled_toggle: "OLED dark",
|
oled_toggle: "OLED dark",
|
||||||
oled_desc: "Pure black for the dark mode.",
|
oled_desc: "Pure black for the dark mode.",
|
||||||
|
appearance_desc: "Theme, accent color and OLED dark for your app.",
|
||||||
last_workout: "Prefill last workout",
|
last_workout: "Prefill last workout",
|
||||||
last_workout_desc: "New sessions start with the last completed workout of the category (only filled sets).",
|
last_workout_desc: "New sessions start with the last completed workout of the category (only filled sets).",
|
||||||
plan_prefill: "Prefill plan weights",
|
plan_prefill: "Prefill plan weights",
|
||||||
@@ -594,12 +596,15 @@ function renderStrengthSession(card, a, model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dayKeys = day.exercises.map(x => x.key);
|
const dayKeys = day.exercises.map(x => x.key);
|
||||||
|
const grid = el("div", "ex-grid");
|
||||||
|
model.exGrid = grid;
|
||||||
|
card.appendChild(grid);
|
||||||
for (const k of model.exKeys) {
|
for (const k of model.exKeys) {
|
||||||
const ex = model.exByKey[k] || {
|
const ex = model.exByKey[k] || {
|
||||||
key: k, name: k, sets: { count: 0 }, target: "", startKg: "",
|
key: k, name: k, sets: { count: 0 }, target: "", startKg: "",
|
||||||
info: "", tags: [],
|
info: "", tags: [],
|
||||||
};
|
};
|
||||||
card.appendChild(buildExBlock(a, model, ex, !dayKeys.includes(k)));
|
grid.appendChild(buildExBlock(a, model, ex, !dayKeys.includes(k)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,20 +673,23 @@ function sessionHead(a, model, dayTitle, day) {
|
|||||||
noteBox.appendChild(noteInput);
|
noteBox.appendChild(noteInput);
|
||||||
head.appendChild(noteBox);
|
head.appendChild(noteBox);
|
||||||
|
|
||||||
|
const actions = el("div", "session-actions");
|
||||||
if (a.type === "strength") {
|
if (a.type === "strength") {
|
||||||
const resetAll = el("button", "btn btn-small", t("reset_all"));
|
const resetAll = el("button", "btn btn-small", t("reset_all"));
|
||||||
resetAll.type = "button";
|
resetAll.type = "button";
|
||||||
resetAll.title = t("reset_all_title");
|
resetAll.title = t("reset_all_title");
|
||||||
resetAll.addEventListener("click", () => resetAllSets(a, model));
|
resetAll.addEventListener("click", () => resetAllSets(a, model));
|
||||||
head.appendChild(resetAll);
|
actions.appendChild(resetAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fin = el("button", "btn btn-primary", t("finish_session"));
|
const fin = el("button", "btn btn-primary", t("finish_session"));
|
||||||
fin.type = "button";
|
fin.type = "button";
|
||||||
fin.addEventListener("click", () => finishSession(a, model));
|
fin.addEventListener("click", () => finishSession(a, model));
|
||||||
head.appendChild(fin);
|
actions.appendChild(fin);
|
||||||
|
head.appendChild(actions);
|
||||||
|
|
||||||
head.appendChild(discardBtn(a, model));
|
const disc = discardBtn(a, model);
|
||||||
|
disc.classList.add("session-discard");
|
||||||
|
head.appendChild(disc);
|
||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,8 +728,8 @@ function addExercise(a, model, exKey) {
|
|||||||
const count = cfg.list ? cfg.list.length : (cfg.count || 1);
|
const count = cfg.list ? cfg.list.length : (cfg.count || 1);
|
||||||
model.exercises[exKey] = Array.from({ length: count },
|
model.exercises[exKey] = Array.from({ length: count },
|
||||||
() => ({ id: null, weight: "", reps: "", done: 0 }));
|
() => ({ id: null, weight: "", reps: "", done: 0 }));
|
||||||
const card = app.cards[model.activity];
|
const target = model.exGrid || app.cards[model.activity];
|
||||||
if (card) card.appendChild(buildExBlock(a, model, ex, true));
|
if (target) target.appendChild(buildExBlock(a, model, ex, true));
|
||||||
model.dirtyEx.add(exKey);
|
model.dirtyEx.add(exKey);
|
||||||
scheduleAutosave(model);
|
scheduleAutosave(model);
|
||||||
}
|
}
|
||||||
@@ -746,10 +754,10 @@ function reorderExercise(a, model, fromKey, toKey) {
|
|||||||
if (from < 0 || to < 0) return;
|
if (from < 0 || to < 0) return;
|
||||||
keys.splice(from, 1);
|
keys.splice(from, 1);
|
||||||
keys.splice(to, 0, fromKey);
|
keys.splice(to, 0, fromKey);
|
||||||
const card = app.cards[model.activity];
|
const target = model.exGrid || app.cards[model.activity];
|
||||||
if (card) {
|
if (target) {
|
||||||
const blocks = keys.map(k => model.exBlocks[k]).filter(Boolean);
|
const blocks = keys.map(k => model.exBlocks[k]).filter(Boolean);
|
||||||
for (const b of blocks) card.appendChild(b);
|
for (const b of blocks) target.appendChild(b);
|
||||||
}
|
}
|
||||||
model.fields = Object.assign({}, model.fields, { exerciseOrder: keys.slice() });
|
model.fields = Object.assign({}, model.fields, { exerciseOrder: keys.slice() });
|
||||||
model.metaDirty = true;
|
model.metaDirty = true;
|
||||||
@@ -1761,9 +1769,7 @@ function renderWizardStep() {
|
|||||||
} else {
|
} else {
|
||||||
// Letzter Schritt: Darstellung (Theme + OLED + Akzent)
|
// Letzter Schritt: Darstellung (Theme + OLED + Akzent)
|
||||||
box.appendChild(el("h3", "wizard-title", t("appearance")));
|
box.appendChild(el("h3", "wizard-title", t("appearance")));
|
||||||
if (wizard.mode === "setup") {
|
box.appendChild(el("p", "auth-sub", t("appearance_desc")));
|
||||||
box.appendChild(el("p", "auth-sub", t("color_server")));
|
|
||||||
}
|
|
||||||
box.appendChild(el("div", "picker-label", t("theme_label")));
|
box.appendChild(el("div", "picker-label", t("theme_label")));
|
||||||
const themeRow = el("div", "day-picker");
|
const themeRow = el("div", "day-picker");
|
||||||
for (const [key, label] of [["dark", t("dark")], ["system", t("system")], ["light", t("light")]]) {
|
for (const [key, label] of [["dark", t("dark")], ["system", t("system")], ["light", t("light")]]) {
|
||||||
@@ -2110,7 +2116,7 @@ function renderAccountSection() {
|
|||||||
sec.appendChild(el("h3", "", t("account")));
|
sec.appendChild(el("h3", "", t("account")));
|
||||||
const body = el("div", "data-body");
|
const body = el("div", "data-body");
|
||||||
body.appendChild(el("p", "", t("account_desc", { u: app.me.username })));
|
body.appendChild(el("p", "", t("account_desc", { u: app.me.username })));
|
||||||
const row = el("div", "data-row");
|
const row = el("div", "form-row");
|
||||||
const cur = el("input", "file-input");
|
const cur = el("input", "file-input");
|
||||||
cur.type = "password";
|
cur.type = "password";
|
||||||
cur.placeholder = t("current_pw");
|
cur.placeholder = t("current_pw");
|
||||||
@@ -2147,7 +2153,7 @@ function renderSetupUsersSection() {
|
|||||||
sec.appendChild(el("h3", "", t("setup_users")));
|
sec.appendChild(el("h3", "", t("setup_users")));
|
||||||
const body = el("div", "data-body");
|
const body = el("div", "data-body");
|
||||||
body.appendChild(el("p", "", t("users_solo_desc")));
|
body.appendChild(el("p", "", t("users_solo_desc")));
|
||||||
const row = el("div", "data-row");
|
const row = el("div", "form-row");
|
||||||
const name = el("input", "file-input");
|
const name = el("input", "file-input");
|
||||||
name.type = "text";
|
name.type = "text";
|
||||||
name.placeholder = t("username");
|
name.placeholder = t("username");
|
||||||
@@ -2247,7 +2253,7 @@ function renderOwnerUsersSection() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
reload();
|
reload();
|
||||||
const row = el("div", "data-row");
|
const row = el("div", "form-row");
|
||||||
const name = el("input", "file-input");
|
const name = el("input", "file-input");
|
||||||
name.type = "text";
|
name.type = "text";
|
||||||
name.placeholder = t("username");
|
name.placeholder = t("username");
|
||||||
|
|||||||
+53
-17
@@ -14,11 +14,12 @@
|
|||||||
--border: #333;
|
--border: #333;
|
||||||
--border-hover: #4d4d4d;
|
--border-hover: #4d4d4d;
|
||||||
--text: #e8e8e8;
|
--text: #e8e8e8;
|
||||||
--muted: #8f8f8f;
|
--muted: #a0a0a0;
|
||||||
--accent: #ffffff;
|
--accent: #ffffff;
|
||||||
--accent-hover: #f2f2f2;
|
--accent-hover: #f2f2f2;
|
||||||
--accent-active: #e6e6e6;
|
--accent-active: #e6e6e6;
|
||||||
--accent-ink: #141414;
|
--accent-ink: #141414;
|
||||||
|
--accent-border: var(--accent);
|
||||||
--danger: #c0392b;
|
--danger: #c0392b;
|
||||||
--ok: #6fae5c;
|
--ok: #6fae5c;
|
||||||
--font-ui: system-ui, -apple-system, "Segoe UI", sans-serif;
|
--font-ui: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="light"] {
|
html[data-theme="light"] {
|
||||||
|
--muted: #5c5c59;
|
||||||
--bg: #f2f2f0;
|
--bg: #f2f2f0;
|
||||||
--bg-panel: #ffffff;
|
--bg-panel: #ffffff;
|
||||||
--bg-input: #ffffff;
|
--bg-input: #ffffff;
|
||||||
@@ -40,6 +42,7 @@ html[data-theme="light"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="oled"] {
|
html[data-theme="oled"] {
|
||||||
|
--muted: #a0a0a0;
|
||||||
--bg: #000000;
|
--bg: #000000;
|
||||||
--bg-panel: #050505;
|
--bg-panel: #050505;
|
||||||
--bg-input: #000000;
|
--bg-input: #000000;
|
||||||
@@ -58,7 +61,7 @@ html[data-accent="cyan"] { --accent: #25b8cc; --accent-hover: #39c5d8; --accen
|
|||||||
html[data-accent="blue"] { --accent: #4a86c8; --accent-hover: #5a96d6; --accent-active: #4073b0; --accent-ink: #141414; }
|
html[data-accent="blue"] { --accent: #4a86c8; --accent-hover: #5a96d6; --accent-active: #4073b0; --accent-ink: #141414; }
|
||||||
html[data-accent="violet"] { --accent: #9a6fd0; --accent-hover: #ab80e0; --accent-active: #875db7; --accent-ink: #141414; }
|
html[data-accent="violet"] { --accent: #9a6fd0; --accent-hover: #ab80e0; --accent-active: #875db7; --accent-ink: #141414; }
|
||||||
html[data-accent="pink"] { --accent: #e0567f; --accent-hover: #eb6890; --accent-active: #c74b6e; --accent-ink: #141414; }
|
html[data-accent="pink"] { --accent: #e0567f; --accent-hover: #eb6890; --accent-active: #c74b6e; --accent-ink: #141414; }
|
||||||
html[data-accent="white"] { --accent: #ffffff; --accent-hover: #f2f2f2; --accent-active: #e6e6e6; --accent-ink: #141414; }
|
html[data-accent="white"] { --accent: #ffffff; --accent-hover: #f2f2f2; --accent-active: #e6e6e6; --accent-ink: #141414; --accent-border: #9a9a9a; }
|
||||||
|
|
||||||
html[data-theme="dark"], html[data-theme="oled"] { color-scheme: dark; }
|
html[data-theme="dark"], html[data-theme="oled"] { color-scheme: dark; }
|
||||||
html[data-theme="light"] { color-scheme: light; }
|
html[data-theme="light"] { color-scheme: light; }
|
||||||
@@ -203,14 +206,14 @@ h3 { font-size: 15px; font-weight: 700; }
|
|||||||
.btn:active { background: var(--bg-hover); }
|
.btn:active { background: var(--bg-hover); }
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent-border, var(--accent));
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: var(--accent-ink);
|
color: var(--accent-ink);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary:hover { border-color: var(--accent-hover); background: var(--accent-hover); }
|
.btn-primary:hover { background: var(--accent-hover); }
|
||||||
.btn-primary:active { border-color: var(--accent-active); background: var(--accent-active); }
|
.btn-primary:active { background: var(--accent-active); }
|
||||||
|
|
||||||
.btn-danger-ghost {
|
.btn-danger-ghost {
|
||||||
border-color: var(--danger);
|
border-color: var(--danger);
|
||||||
@@ -304,6 +307,18 @@ h3 { font-size: 15px; font-weight: 700; }
|
|||||||
background: var(--accent-hover);
|
background: var(--accent-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Session-Aktionen: Hauptaktionen gebuendelt, Verwerfen abgesetzt */
|
||||||
|
|
||||||
|
.session-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-discard {
|
||||||
|
margin-left: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Session-Kopf (laufende Session) */
|
/* Session-Kopf (laufende Session) */
|
||||||
|
|
||||||
.session-head {
|
.session-head {
|
||||||
@@ -365,10 +380,19 @@ h3 { font-size: 15px; font-weight: 700; }
|
|||||||
|
|
||||||
.ex-block {
|
.ex-block {
|
||||||
border: 2px solid var(--border);
|
border: 2px solid var(--border);
|
||||||
margin: 0 0 12px;
|
margin: 0;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ex-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ex-grid .ex-block { margin: 0; }
|
||||||
|
|
||||||
.ex-block.drag-over { border-color: var(--accent); }
|
.ex-block.drag-over { border-color: var(--accent); }
|
||||||
|
|
||||||
.ex-head {
|
.ex-head {
|
||||||
@@ -398,7 +422,7 @@ h3 { font-size: 15px; font-weight: 700; }
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 12px;
|
padding: 6px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.set-row.is-done { background: var(--bg-subtle); }
|
.set-row.is-done { background: var(--bg-subtle); }
|
||||||
@@ -764,6 +788,17 @@ h3 { font-size: 15px; font-weight: 700; }
|
|||||||
|
|
||||||
.data-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
|
.data-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input { flex: 1 1 0; min-width: 140px; }
|
||||||
|
|
||||||
|
.form-row .btn { flex-shrink: 0; }
|
||||||
|
|
||||||
.file-input {
|
.file-input {
|
||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
@@ -792,8 +827,8 @@ h3 { font-size: 15px; font-weight: 700; }
|
|||||||
.toggle-row:last-child { border-bottom: 0; }
|
.toggle-row:last-child { border-bottom: 0; }
|
||||||
|
|
||||||
.toggle-input {
|
.toggle-input {
|
||||||
width: 20px;
|
width: 22px;
|
||||||
height: 20px;
|
height: 22px;
|
||||||
margin: 2px 0 0;
|
margin: 2px 0 0;
|
||||||
accent-color: var(--accent);
|
accent-color: var(--accent);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -881,8 +916,9 @@ body.boot #setup-gate { visibility: hidden; }
|
|||||||
.auth-box {
|
.auth-box {
|
||||||
border: 2px solid var(--border);
|
border: 2px solid var(--border);
|
||||||
background: var(--bg-panel);
|
background: var(--bg-panel);
|
||||||
max-width: 460px;
|
width: 640px;
|
||||||
padding: 36px;
|
max-width: calc(100vw - 32px);
|
||||||
|
padding: 40px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,7 +965,7 @@ body.boot #setup-gate { visibility: hidden; }
|
|||||||
|
|
||||||
.auth-box .picker-label { margin: 0 0 12px; }
|
.auth-box .picker-label { margin: 0 0 12px; }
|
||||||
|
|
||||||
.auth-box .day-picker { margin-bottom: 16px; }
|
.auth-box .day-picker { gap: 12px; margin-bottom: 20px; }
|
||||||
|
|
||||||
.auth-form { margin: 0 0 18px; }
|
.auth-form { margin: 0 0 18px; }
|
||||||
|
|
||||||
@@ -1016,20 +1052,20 @@ body.boot #setup-gate { visibility: hidden; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wp-step {
|
.wp-step {
|
||||||
width: 36px;
|
width: 40px;
|
||||||
height: 10px;
|
height: 6px;
|
||||||
border: 2px solid var(--border);
|
border: 2px solid var(--border);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wp-step.is-done {
|
.wp-step.is-done {
|
||||||
border-color: var(--muted);
|
border-color: var(--accent);
|
||||||
background: var(--muted);
|
background: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wp-step.is-current {
|
.wp-step.is-current {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
background: var(--accent);
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
|
|||||||
Reference in new issue
Block a user