fix(fitness): measure page polish — stable steppers, narrow history, body-parts chrome tweaks
CI / update (push) Successful in 4m5s
CI / update (push) Successful in 4m5s
- Lock +/- button positions by normalizing stepped weight/body-fat
values to .toFixed(1) so trailing zeros stay; placeholders also
normalized. Input width no longer jitters through a step sequence.
- Cap .history-section width on mobile/tablet to match .main-col
(480px / 760px) so "Past measurements" aligns with the metric cards.
- Body-parts page:
- Remove the "Running totals" list from the right panel.
- Hide the keyboard-shortcut legend by default; show on `?` (toggle)
or Escape (dismiss), with a small `?` pill hint in its place.
Added kbd_hint i18n string.
- Push skip + back/next toward the edges of the bottombar; pull
progress dots + close button inward symmetrically.
- Center the keyboard legend / hint on the screen width rather than
between the skip and nav buttons (position: absolute + translate).
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homepage",
|
||||
"version": "1.46.0",
|
||||
"version": "1.46.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -297,6 +297,7 @@ const translations: Translations = {
|
||||
kbd_next: { en: 'next', de: 'weiter' },
|
||||
kbd_skip: { en: 'skip', de: 'auslassen' },
|
||||
kbd_wheel: { en: '\u00b10.1', de: '\u00b10,1' },
|
||||
kbd_hint: { en: 'Press ? for shortcuts', de: '? dr\u00fccken f\u00fcr Tastenk\u00fcrzel' },
|
||||
no_body_parts_selected: {
|
||||
en: 'Enter at least one value before saving.',
|
||||
de: 'Bitte mindestens einen Wert eingeben.'
|
||||
|
||||
@@ -186,12 +186,12 @@
|
||||
|
||||
function stepWeight(delta) {
|
||||
const cur = Number(formWeight) || lastWeight || 0;
|
||||
formWeight = String(Math.round((cur + delta) * 10) / 10);
|
||||
formWeight = (Math.round((cur + delta) * 10) / 10).toFixed(1);
|
||||
}
|
||||
|
||||
function stepBodyFat(delta) {
|
||||
const cur = Number(formBodyFat) || lastBodyFat || 0;
|
||||
formBodyFat = String(Math.round((cur + delta) * 10) / 10);
|
||||
formBodyFat = (Math.round((cur + delta) * 10) / 10).toFixed(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -393,7 +393,7 @@
|
||||
type="number"
|
||||
step="0.1"
|
||||
bind:value={formWeight}
|
||||
placeholder={lastWeight != null ? String(lastWeight) : '0.0'}
|
||||
placeholder={lastWeight != null ? Number(lastWeight).toFixed(1) : '0.0'}
|
||||
class="metric-input"
|
||||
inputmode="decimal"
|
||||
onkeydown={(e) => onMetricKey(e, stepWeight)}
|
||||
@@ -424,7 +424,7 @@
|
||||
type="number"
|
||||
step="0.1"
|
||||
bind:value={formBodyFat}
|
||||
placeholder={lastBodyFat != null ? String(lastBodyFat) : '0.0'}
|
||||
placeholder={lastBodyFat != null ? Number(lastBodyFat).toFixed(1) : '0.0'}
|
||||
class="metric-input"
|
||||
inputmode="decimal"
|
||||
onkeydown={(e) => onMetricKey(e, stepBodyFat)}
|
||||
@@ -588,6 +588,11 @@
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
}
|
||||
.history-section {
|
||||
max-width: 480px;
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
@@ -1314,7 +1319,8 @@
|
||||
/* Weight + body fat side-by-side once there's room (tablet and up) */
|
||||
@media (min-width: 560px) {
|
||||
.main-col,
|
||||
.add-form {
|
||||
.add-form,
|
||||
.history-section {
|
||||
max-width: 760px;
|
||||
}
|
||||
.metric-grid {
|
||||
|
||||
@@ -140,14 +140,18 @@
|
||||
return v ? `${v} cm` : '—';
|
||||
}
|
||||
|
||||
let showShortcuts = $state(false);
|
||||
|
||||
/** @param {KeyboardEvent} e */
|
||||
function onkey(e) {
|
||||
const tag = /** @type {HTMLElement|null} */ (e.target)?.tagName;
|
||||
const inInput = tag === 'INPUT';
|
||||
if (e.key === '?' && !inInput) { e.preventDefault(); showShortcuts = !showShortcuts; return; }
|
||||
if (e.key === 'Escape' && showShortcuts) { e.preventDefault(); showShortcuts = false; return; }
|
||||
if (done) {
|
||||
if (e.key === 'ArrowLeft') { e.preventDefault(); idx = total - 1; direction = -1; }
|
||||
return;
|
||||
}
|
||||
const tag = /** @type {HTMLElement|null} */ (e.target)?.tagName;
|
||||
const inInput = tag === 'INPUT';
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); next(); }
|
||||
else if (e.key === 'ArrowRight' && !inInput) { e.preventDefault(); next(); }
|
||||
else if (e.key === 'ArrowLeft' && !inInput) { e.preventDefault(); back(); }
|
||||
@@ -477,7 +481,7 @@
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<aside class="panel" aria-label={t('running_totals', lang)}>
|
||||
<aside class="panel">
|
||||
{#if !done}
|
||||
{#key step.key}
|
||||
<div class="panel-section chart-section" in:fade={{ duration: 180 }}>
|
||||
@@ -549,32 +553,29 @@
|
||||
{/key}
|
||||
{/if}
|
||||
|
||||
<div class="panel-section totals-section">
|
||||
<div class="panel-head">
|
||||
<h3 class="panel-title">{t('running_totals', lang)}</h3>
|
||||
</div>
|
||||
<ul class="totals">
|
||||
{#each steps as s, i (s.key)}
|
||||
<li class:dim={!isFilled(s)} class:focused={i === idx && !done}>
|
||||
<button type="button" class="totals-item" onclick={() => jumpTo(i)}>
|
||||
<span class="totals-label">{stepLabel(s)}</span>
|
||||
<span class="totals-val">{formatValue(s)}</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<footer class="bottombar">
|
||||
{#if !done}
|
||||
<button type="button" class="ghost" onclick={skip}>{t('skip', lang)}</button>
|
||||
<div class="kbd-legend" aria-hidden="true">
|
||||
<span><kbd>←</kbd><kbd>→</kbd> {t('kbd_nav', lang)}</span>
|
||||
<span><kbd>↵</kbd> {t('kbd_next', lang)}</span>
|
||||
<span><kbd>S</kbd> {t('kbd_skip', lang)}</span>
|
||||
<span><kbd>scroll</kbd> {t('kbd_wheel', lang)}</span>
|
||||
</div>
|
||||
{#if showShortcuts}
|
||||
<div class="kbd-legend" aria-hidden="true">
|
||||
<span><kbd>←</kbd><kbd>→</kbd> {t('kbd_nav', lang)}</span>
|
||||
<span><kbd>↵</kbd> {t('kbd_next', lang)}</span>
|
||||
<span><kbd>S</kbd> {t('kbd_skip', lang)}</span>
|
||||
<span><kbd>scroll</kbd> {t('kbd_wheel', lang)}</span>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="kbd-hint"
|
||||
onclick={() => showShortcuts = true}
|
||||
aria-label={t('kbd_hint', lang)}
|
||||
title={t('kbd_hint', lang)}
|
||||
>
|
||||
<kbd>?</kbd>
|
||||
</button>
|
||||
{/if}
|
||||
<div class="nav-pair">
|
||||
<button type="button" class="nav-btn" onclick={back} disabled={idx === 0} aria-label={t('back', lang)}>
|
||||
<ArrowLeft size={16} />
|
||||
@@ -617,7 +618,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 1.25rem;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
.progress {
|
||||
display: flex;
|
||||
@@ -864,14 +865,13 @@
|
||||
.panel { display: none; }
|
||||
|
||||
.bottombar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 1.25rem 1.5rem;
|
||||
padding: 1rem 0.5rem 1.5rem;
|
||||
gap: 0.75rem;
|
||||
max-width: 520px;
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
}
|
||||
.ghost {
|
||||
border: none;
|
||||
@@ -913,6 +913,7 @@
|
||||
}
|
||||
|
||||
.kbd-legend { display: none; }
|
||||
.kbd-hint { display: none; }
|
||||
.bottom-spacer { flex: 1; }
|
||||
|
||||
.summary {
|
||||
@@ -994,7 +995,7 @@
|
||||
.topbar { grid-area: topbar; padding: calc(1.25rem + var(--fitness-header-offset)) 2rem 0; }
|
||||
.stage { grid-area: stage; padding: 1.5rem 2rem 2rem; align-items: center; }
|
||||
.panel { grid-area: panel; }
|
||||
.bottombar { grid-area: bottom; max-width: none; margin-inline: 0; padding: 1rem 2rem 1.5rem; }
|
||||
.bottombar { grid-area: bottom; max-width: none; margin-inline: 0; padding: 1rem 0.5rem 1.5rem; }
|
||||
|
||||
.topbar .progress { visibility: hidden; }
|
||||
|
||||
@@ -1279,20 +1280,25 @@
|
||||
.stepper.compact { flex: 1 1 180px; min-width: 180px; }
|
||||
|
||||
.kbd-legend {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
font-size: 0.68rem;
|
||||
color: var(--color-text-tertiary);
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
.kbd-legend span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
.kbd-legend kbd {
|
||||
.kbd-legend kbd,
|
||||
.kbd-hint kbd {
|
||||
font-family: inherit;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
@@ -1307,6 +1313,23 @@
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.kbd-hint {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-tertiary);
|
||||
opacity: 0.6;
|
||||
transition: opacity 150ms;
|
||||
}
|
||||
.kbd-hint:hover { opacity: 1; }
|
||||
}
|
||||
|
||||
@media (min-width: 1400px) {
|
||||
|
||||
Reference in New Issue
Block a user