Skip to content

Commit 7d2e7a0

Browse files
garthvhclaude
andcommitted
dashboard: fill the empty space on plain stat tiles
On a wide screen the plain label+number tiles left a big gap: the number was capped and pinned to the bottom by space-between. Now the number grows to fill the leftover height (not just width) and sits centered in it, so it fills the tile instead of stranding. Two fixes: fitValues() measures the real glyph box with a Range (the value is a stretched flex item, so its scrollWidth was the tile width, which pinned the width ratio at 1.0 and blocked any growth), and it now also scales to the tile's leftover height. Width-limited values (324.0k) center in the space rather than dropping to the bottom. Tiles that carry a sub-line or sparkline (Saved, Output tokens/s) keep their stacked layout via a :has() guard and are only ever shrunk to fit, never grown. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a89da74 commit 7d2e7a0

1 file changed

Lines changed: 34 additions & 10 deletions

File tree

dashboard.html

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@
5353
}
5454
.tile .sub { color: var(--ink-muted); font-size: 12px; font-size: clamp(11px, 5.5cqi, 13px); }
5555
.tile svg { display: block; width: 100%; height: clamp(34px, 20cqi, 56px); }
56+
/* Plain label+number tiles: let the number own the leftover space and sit
57+
centered in it (fitValues() grows the font to fill), so it never strands at
58+
the bottom. Tiles with a sub-line or sparkline keep their stacked layout. */
59+
.tile:not(:has(.sub)):not(:has(svg)) .value {
60+
flex: 1 1 auto; min-height: 0; display: flex; align-items: center;
61+
}
5662

5763
.card { background: var(--surface-1); border: 1px solid var(--border); border-radius: 10px; overflow-x: auto; }
5864
.pager { display: flex; align-items: center; padding: 8px 12px; border-top: 1px solid var(--grid);
@@ -246,19 +252,37 @@ <h3>Response <button class="copy" data-copy="d-resp">copy</button></h3>
246252

247253
function esc(s) { return String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c])); }
248254

249-
// Size each stat number as big as fits its tile: the CSS aims high, this shrinks
250-
// only the values whose text is wider than the tile (long token counts, dollar
251-
// amounts) so short numbers stay huge and nothing clips.
255+
// Size each stat number to fill its tile. Plain label+number tiles grow the
256+
// number to fill the leftover width AND height (so it fills the gap on tall
257+
// tiles); tiles that carry sub-text or a sparkline only shrink long numbers to
258+
// fit the width, keeping their existing layout.
252259
function fitValues() {
260+
const range = document.createRange();
253261
for (const el of document.querySelectorAll(".tile .value")) {
254-
el.style.fontSize = ""; // reset to the CSS-driven size
255-
const avail = el.clientWidth;
256-
if (!avail) continue;
257-
const w = el.scrollWidth; // full text width (white-space:nowrap)
258-
if (w > avail) {
259-
const base = parseFloat(getComputedStyle(el).fontSize);
260-
el.style.fontSize = Math.max(16, Math.floor(base * (avail / w) * 0.97)) + "px"; // 0.97 = sub-pixel safety
262+
el.style.fontSize = ""; // reset to the CSS-driven size
263+
const availW = el.clientWidth;
264+
if (!availW || !el.firstChild) continue;
265+
// Measure the actual text box — the value is a stretched flex item, so its own
266+
// scrollWidth is the tile width, not the glyphs'. A Range gives the real size.
267+
range.selectNodeContents(el);
268+
const r = range.getBoundingClientRect();
269+
if (!r.width || !r.height) continue;
270+
const base = parseFloat(getComputedStyle(el).fontSize);
271+
const tile = el.closest(".tile");
272+
const fillH = !tile.querySelector(".sub") && !tile.querySelector("svg");
273+
let ratio = availW / r.width; // fit the width
274+
if (fillH) {
275+
// Leftover vertical space = tile content height minus the other rows (label);
276+
// let the number grow to fill it so it doesn't strand at the bottom.
277+
const cs = getComputedStyle(tile);
278+
let used = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
279+
for (const c of tile.children) if (c !== el) used += c.offsetHeight;
280+
const availH = tile.clientHeight - used;
281+
if (availH > 0) ratio = Math.min(ratio, availH / r.height);
282+
} else {
283+
ratio = Math.min(1, ratio); // sub/sparkline tiles: shrink only, never grow
261284
}
285+
el.style.fontSize = Math.max(16, Math.floor(base * ratio * 0.92)) + "px"; // 0.92 = breathing room
262286
}
263287
}
264288
let fitScheduled = false;

0 commit comments

Comments
 (0)