Improvement dashboard widget sizing - Expand dashboard widget sizing options - #456
Improvement dashboard widget sizing - Expand dashboard widget sizing options#456rafaelfoster wants to merge 4 commits into
Conversation
Review: Dashboard widget sizingThanks for this — the motivation (sovereignty through customization) is exactly right for a self-hosted product, and the execution is clean: all 23 locales with real translations, SPEC in sync, a focused test, no constraint violations. Two things block the merge, though, and a third is a deliberate product decision we should make explicitly rather than reverse silently. I reproduced the critical point with an isolated A/B test (below) — it's confirmed. 🔴 Blocker 1 —
|
before (minmax(132px, auto)) |
after (206px) |
|
|---|---|---|
| card height | 697 px (grows) | 206 px (capped) |
| body visible / actual | 626 / 626 px | 135 / 610 px |
| clipped rows | 0 / 21 | 18 / 21 |
| scrollable | — | no (overflow-y: visible) |
Why this isn't an edge case:
- It hits default layouts, not just the new presets:
shopping,notes,family,birthdays,weather,health,housekeepingall start single-row (dashboard.js:238-240). - It runs against the PR's own intent. The goal — bringing single-row widgets down to a reduced height — is valid (697px-tall single-row widgets look clumsy). But because of the no-scroll body decision, "reduced height" here means losing content, not rendering it more compactly.
- The headline feature makes it worse:
Wide (3x1)/Full row (4x1)add horizontal space, but the list stays vertically capped at 206px. So a "wide overview" shows less content, not more.
Fix options (by cleanliness):
- Cap instead of fix:
grid-auto-rows: minmax(132px, 206px). Bounds the over-expansion (the actual concern) without decapitating content-heavy widgets. Solves most of it with no data loss — my preferred suggestion. - Make the body scrollable again — but this collides head-on with [Bug] Scrolling on mobile safari results in blank screen #166; only with real-device regression testing.
- Offer single-row presets only for content-light widgets (weather/family), suppress them for list widgets.
🟡 Blocker 2 — the size control shows the wrong active state for matrix values
normalizeDashboardConfig now preserves arbitrary valid matrix values (3x3, 4x3, 2x4, …) instead of collapsing them to a preset (dashboard.js:283). But the UI only offers 8 buttons and highlights the nearest one via nearestPreset (dashboard.js:1198).
Result: a widget stored as 4x3 shows "Full (4x2)" as active (wrong), and any button click silently drops the 3rd row. Previously the UI was always truthful because normalize collapsed the value — now the stored value and the displayed state drift apart. The new test even locks in this reachable state (4x3 is preserved, test-dashboard.js:213).
Fix: either offer all 16 matrix values in the UI or keep collapsing on normalize. The current middle ground (preserve, but only show 8) is inconsistent.
🎛️ Product decision — doubling 4→8 presets: why 4 was chosen, and how 8 can still work
This is not a bug — it's a deliberate change of direction that overwrites the removed comment (dashboard.js, "≤4-choices rule, Critique P2"). It deserves an explicit call. The full reasoning behind the original limit of 4:
1. Decision cost scales with options × widgets. Size isn't an isolated choice — it's made per widget, across up to 13 widgets. At 4 options the choice is a reflex (Hick's law: decision time grows with the number of options, and the cumulative load across 13 tiles is real). At 8, the search space per tile doubles and Customize mode tips from "make it pretty quickly" into "reason about every tile individually." For a family audience touching the dashboard on a phone while cooking, that's friction in exactly the wrong place.
2. It collides with two documented product principles:
- "Power without noise" — feature depth is revealed through navigation, not laid out up front. 8 flat options are the opposite.
- Anti-reference "overloaded feature dashboard" — "Power is revealed on demand, not laid out in one giant grid." A size picker with 8 shapes per tile is a micro-cosm of exactly that pattern.
3. Curation is the value here. Four shapes (compact / tall / standard / wide) cover the real layout needs; the other 4 are mostly variants almost nobody deliberately reaches for but everybody has to look at. "Fewer, but the right ones" is a feature in a family tool, not a shortcoming.
But: your sovereignty argument is legit, and there are users who want exactly this control. The conflict isn't 4-vs-8, it's a flat wall vs. a structured choice. Hence the compromise:
Keep the 8 presets — but group them in the UI into two visually separated rows: one row for "1 row tall" (1x1 · 2x1 · 3x1 · 4x1) and below it "2 rows tall" (1x2 · 2x2 · 3x2 · 4x2). That turns "8 things I have to compare" into two times "4 along one clear axis (width)" — the cognitive load stays around 4 per decision, because height pre-sorts the rows. Progressive disclosure with no loss of capability. It satisfies both goals: full control and "power without noise."
If you go that route, Blocker 2 should be fixed alongside it (then every offered value is also a stored value — no more nearestPreset lie).
Verdict
Request changes. Blocker 1 (clipping) is the hard stopper and it's proven — please resolve via fix option 1 (minmax(132px, 206px)). Fix Blocker 2 alongside. The preset count is your call; if 8, please group rather than flatten. Once that's in, this is a strong and welcome contribution.
Reproduction: seeded DB, single dashboard, only the grid-auto-rows value swapped at runtime on the same rendered DOM; measurements via getBoundingClientRect / scrollHeight. Before/after screenshots available on request.
|
Thanks @ulsklyc for the detailed review and the reproduction data. I addressed the blockers in
Validation passed:
|
Re-review of the revision (
|
before minmax(132px, auto) |
this PR minmax(132px, 206px) |
|
|---|---|---|
| card height | 587 px (grows) | 206 px (capped) |
| body visible / actual | 546 / 546 px | 165 / 530 px |
| items clipped | 0 / 18 | 13 / 18 |
| scrollable | — | no |
So a content-heavy single-row widget still loses 13 of 18 rows with no scrollbar and no indicator. minmax(132px, 206px) only reduces whitespace for sparse widgets; for full ones it's the same decapitation as fixed 206px.
The root is the triangle I flagged in the first review: single-row-short + no body scroll (#166) + overflow:hidden makes clipping unavoidable — no grid-auto-rows value squares that circle. My "option 1" never could; the workable direction is closer to my original "option 3."
A fix that actually holds (not another CSS tweak):
- Preferred: in single-row sizes, cap the number of rendered items and reuse the existing "+N more" row (Shopping already computes
moreCount; Tasks/Calendar/Notes can do the same). Nothing is lost — a single-row widget becomes an honest at-a-glance summary that links to the full module. - Alternative: add a bottom fade/gradient as a "more below" affordance and treat single-row explicitly as a summary.
- Making the body scrollable stays off the table (reintroduces [Bug] Scrolling on mobile safari results in blank screen #166).
Also: needs a rebase
The branch is now conflicting — main has moved to v1.7.x while this PR sits on an older base. It'll need a rebase before it can merge regardless.
Net: presets grouping and the size-control drift are done and good. The clipping blocker is still open — and that's because my recommended CSS cap can't fix it. Sorry for sending you down that path. The item-cap + "+N more" approach is the one that resolves it without losing content.
|
Thanks for this, Rafael - the wide one-row sizes are a genuinely useful addition and the locale coverage is thorough. A few things need addressing before it can go in, mostly because The main point: this collides with #547. Blocking
Should fix
Happy to re-review once the server side is reworked against current |
Dashboards are a big deal for some users, and as a self-hosted solution, empowering users and their families to have more customization options is not just a nice-to-have feature—it’s a core expectation. Self-hosting is fundamentally about control. By offering modularity and deep personalization, we respect the user's sovereignty over their self-hosted environment, turning a static interface into a truly tailored home hub.
Summary: