improvement(feed): give the compact row's title the room back

Three from UAT: the row's cap goes 1280 -> 1472 (+15%), since with the
rail and filters hidden there was a lot of page left over and the fixed
columns mean the extra width can only go to the title; the views column
drops the word and keeps it in the tooltip (a table doesn't repeat its
unit every line), taking that column 144 -> 64; and the view switcher
names the current mode where the toolbar has room, falling back to the
icon alone below xl. Title: 431 -> 511 at 1440, 706 at 1900.
This commit is contained in:
2026-07-17 01:58:14 +02:00
parent ed08aa87f0
commit eda6ef4766
3 changed files with 32 additions and 14 deletions
+20 -8
View File
@@ -353,8 +353,15 @@ function VideoCard({
const noThumb = !spec.thumb;
// Each datum on its own, so the one-line row can give each a column of its own while the stacked
// layouts still run them together as one sentence.
// Two shapes for the count: the stacked layouts read it as prose ("1.5K views · 2 min ago"), so
// they need the word; the row's column doesn't — a table doesn't repeat its unit on every line,
// and the tooltip has the exact figure anyway. Same tooltip either way.
const viewsTitle = video.view_count != null ? `${video.view_count.toLocaleString()} ${t("card.views")}` : "";
const viewsCount = video.view_count != null && (
<span title={viewsTitle}>{formatViews(video.view_count)}</span>
);
const views = video.view_count != null && (
<span title={`${video.view_count.toLocaleString()} ${t("card.views")}`}>
<span title={viewsTitle}>
{formatViews(video.view_count)} {t("card.views")}
</span>
);
@@ -439,12 +446,12 @@ function VideoCard({
// still right WITHIN a cell (see RowMetaCells): fixed slots, so they line up across rows too.
// Widths are measured, not chosen by eye — against the worst case in the LIBRARY and in BOTH
// languages, since both have caught me out here:
// actions 188 (6 x 28 + 5 x 4) · meta cells 104 (see RowMetaCells) · views 139 ("101.7K
// megtekintés"; the English "views" is only 79) · published 183 ("11 months ago ·
// Feb 28, 2026"; HU is shorter) · channel 128, which is a deliberate cut rather than a fit:
// names run to 342px, the median is 87, and 12 of 102 truncate here vs 5 at 160 — worth 32px
// to the title, since a clipped channel name still reads while a clipped view count loses the
// number.
// actions 188 (6 x 28 + 5 x 4) · meta cells 104 (see RowMetaCells) · views 48 (the bare
// "101.7K"; language-proof now the word is gone — with it, Hungarian's "megtekintés" needed
// 139) · published 183 ("11 months ago · Feb 28, 2026"; HU is shorter) · channel 128,
// which is a deliberate cut rather than a fit: names run to 342px, the median is 87, and 12
// of 102 truncate here vs 5 at 160 — worth 32px to the title, since a clipped channel name
// still reads while a clipped view count loses the number.
// Three ways this went wrong before: sizing off a PAGE of data (the badges came from 60 videos
// whose longest was 2:00:58, then wrapped on the 11-hour one); measuring with a hand-built probe
// instead of the real element (it read "101.7K megtekintés" as 122px where the live cell renders
@@ -467,7 +474,12 @@ function VideoCard({
<div className="min-w-0 flex-1">{title}</div>
<div className="shrink-0 w-32 hidden sm:block">{channelButton}</div>
<RowMetaCells video={video} className="hidden md:grid" />
<div className="shrink-0 w-36 text-sm text-muted truncate hidden lg:block">{views}</div>
{/* Right-aligned like the duration: it's a number, so the digits share an edge. 64px
holds the widest bare form ("101.7K" at 48) in either language — dropping the word
took this from 144 and handed the difference to the title. */}
<div className="shrink-0 w-16 text-sm text-muted text-right tabular-nums truncate hidden lg:block">
{viewsCount}
</div>
<div className="shrink-0 w-48 text-sm text-muted truncate hidden xl:block">{published}</div>
{/* Thumb draws this over the image; with no image it goes on the row itself. */}
{pct > 0 && (
+8 -3
View File
@@ -96,12 +96,17 @@ export default function ViewSwitcher<T extends string>({
onClick={() => (open ? close() : openMenu())}
aria-haspopup="menu"
aria-expanded={open}
// The aria-label carries the mode's name at every size — the visible text below is a
// nicety for wide windows, not the accessible name.
aria-label={`${label}: ${current.label}`}
title={`${label}: ${current.label}`}
className="shrink-0 inline-flex items-center gap-1 pl-1.5 pr-1 py-1.5 rounded-lg border border-border bg-card text-fg hover:border-accent hover:text-accent active:translate-y-px transition"
className="shrink-0 inline-flex items-center gap-1 pl-2 pr-1 py-1.5 rounded-lg border border-border bg-card text-fg hover:border-accent hover:text-accent active:translate-y-px transition"
>
<CurrentIcon className="w-4 h-4" />
<ChevronDown className={`w-3 h-3 transition-transform ${open ? "rotate-180" : ""}`} />
<CurrentIcon className="w-4 h-4 shrink-0" />
{/* Name the current mode where the toolbar has room; below xl it's a busy row (show chips,
content chips, source, count, sort) and the icon has to carry it alone. */}
<span className="hidden xl:inline text-sm whitespace-nowrap">{current.label}</span>
<ChevronDown className={`w-3 h-3 shrink-0 transition-transform ${open ? "rotate-180" : ""}`} />
</button>
{open && (
<div
+4 -3
View File
@@ -41,9 +41,10 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
// Keeps the 896px cap: its actions dock at the right edge, so a wider row would only stretch the
// dead space between them and the meta.
rows: { family: "row", minCol: null, maxCol: 896, thumb: true, dense: false, actionsBelow: false },
// Wider (1280): everything sits on one line with the actions right-aligned, so there's no gap to
// grow — the extra width just buys more title before it truncates.
rowsCompact: { family: "row", minCol: null, maxCol: 1280, thumb: false, dense: false, actionsBelow: false },
// Much wider (1472): everything sits on one line and the columns after the title are fixed, so
// there's no gap for the extra width to stretch — it all goes to the title. Raised from 1280 at
// the user's request once they saw how much room was left over with the rail and filters hidden.
rowsCompact: { family: "row", minCol: null, maxCol: 1472, thumb: false, dense: false, actionsBelow: false },
// 380 = thumbnail (160) + gap (12) + ~192 for the text + the block's own padding (16). The text
// floor is the action row's measured 156px plus enough for a title to be worth reading.
// Tuned by measuring the real layouts, not guessed: 3 columns need 3n+32 px, so 380 turns the