Compare commits

...

2 Commits

Author SHA1 Message Date
ddidderr 12a0d7abe9 feat(ui): align peer count chip with design reference
Mirror the design-doc update in the actual download progress component
so the GUI matches the trimmed chip. Previously the large progress
panel rendered `[icon] from N peer(s)` inline; now it renders just
`[icon] N`, with the full "Downloading from N peers on the LAN"
sentence retained as the `title` tooltip for discoverability.

Changes:
- `DownloadProgress.tsx` (lg variant): drop the "from" / unit text from
  the inline span, keeping only the count. `peerUnit` stays in scope
  because the tooltip still needs singular/plural.
- `launcher.css`: collapse `.dl-peers` and `.dl-peers strong` into a
  single rule that puts the t-1 colour, 600 weight and tabular-nums
  directly on the chip (the inner `<strong>` no longer exists). Gap
  drops from 5px to 4px to match the tighter icon+number layout.
- Container queries: peers drops at <=240px and ETA drops at <=320px,
  matching the new thresholds in the design reference. The narrower
  chip simply fits in smaller modals, so the old 300/380 cutoffs were
  hiding stats that would have rendered fine.

Test Plan
- `just frontend-test` (passes)
- `just run`, start a download, confirm the chip reads `[icon] N`,
  hover shows the tooltip, and narrowing the window collapses ETA
  before peers at the new breakpoints.
2026-05-21 00:41:10 +02:00
ddidderr b96e8c5747 docs(design): trim peer count chip to icon + bare number
The download progress "peers" chip previously rendered as
`[icon] from N peers` — a full mini-sentence sitting between the speed
and ETA stats. With four groups already separated by middots, the extra
preposition and unit made the row read as prose rather than a stat
strip, and the singular/plural switch added a second source of layout
jitter on top of the digit-width change.

Update the design reference (README, components.jsx, styles.css) so the
chip shows just the users glyph and the count, matching the visual
weight of the other stat groups. The full sentence
("Downloading from N peers on the LAN") moves to the `title` tooltip,
which keeps the affordance discoverable without spending row width on
it. The count adopts `var(--t-1)` + 600 + tabular-nums directly on
`.dl-peers` (no inner `<strong>` needed), so the chip is a single span.

Also tighten the container-query breakpoints. Removing the prose makes
the chip much narrower, so the previous 300px cutoff for hiding peers
and 380px cutoff for hiding ETA were over-eager — both stats now fit
comfortably in narrower modals. Drop them to 240px (peers) and 320px
(ETA). The pct/cancel column still never collapses.

Test Plan
- Visual review of the design reference HTML at component widths
  240px / 320px / 380px to confirm peers and ETA drop at the new
  thresholds rather than the old ones.
- Confirm the chip's tooltip still spells out the full sentence.
2026-05-21 00:40:54 +02:00
5 changed files with 14 additions and 19 deletions
@@ -86,9 +86,7 @@ export const DownloadProgress = ({ game, size = 'md', full = false, onCancel }:
title={`Downloading from ${stats.activePeerCount} ${peerUnit} on the LAN`} title={`Downloading from ${stats.activePeerCount} ${peerUnit} on the LAN`}
> >
<Icon.users /> <Icon.users />
<span> <span>{stats.activePeerCount}</span>
from <strong>{stats.activePeerCount}</strong> {peerUnit}
</span>
</span> </span>
</> </>
)} )}
@@ -1048,14 +1048,11 @@
.dl-lg-secondary .dl-peers { .dl-lg-secondary .dl-peers {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 5px; gap: 4px;
color: var(--t-2);
white-space: nowrap;
}
.dl-lg-secondary .dl-peers strong {
color: var(--t-1); color: var(--t-1);
font-weight: 600; font-weight: 600;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
white-space: nowrap;
} }
.dl-lg-secondary .dl-peers svg { .dl-lg-secondary .dl-peers svg {
opacity: 0.7; opacity: 0.7;
@@ -1069,13 +1066,13 @@
.dl-sep { .dl-sep {
opacity: 0.45; opacity: 0.45;
} }
@container (max-width: 380px) { @container (max-width: 320px) {
.dl-lg-secondary .dl-eta, .dl-lg-secondary .dl-eta,
.dl-lg-secondary .dl-sep-eta { .dl-lg-secondary .dl-sep-eta {
display: none; display: none;
} }
} }
@container (max-width: 300px) { @container (max-width: 240px) {
.dl-lg-secondary .dl-peers, .dl-lg-secondary .dl-peers,
.dl-lg-secondary .dl-sep-peers { .dl-lg-secondary .dl-sep-peers {
display: none; display: none;
+3 -3
View File
@@ -254,7 +254,7 @@ grid-template-areas:
- **Secondary row** (`.dl-lg-secondary`, bottom-left) — the live stats. 12px, four groups separated by `·` (0.45 opacity): - **Secondary row** (`.dl-lg-secondary`, bottom-left) — the live stats. 12px, four groups separated by `·` (0.45 opacity):
1. `<strong>11.4 GB</strong> / 35 GB` (`var(--t-1)` strong + `var(--t-2)` rest) 1. `<strong>11.4 GB</strong> / 35 GB` (`var(--t-1)` strong + `var(--t-2)` rest)
2. `47.6 MB/s` (`var(--t-1)`) 2. `47.6 MB/s` (`var(--t-1)`)
3. `[users-icon] from <strong>5</strong> peers` `.dl-peers`, inline-flex with 5px gap, icon at 0.7 opacity, count bold + tabular-nums in `var(--t-1)`, rest in `var(--t-2)`. Singular/plural switches on `peers === 1`. Hidden entirely when `game.peers` is falsy. Communicates that this is a LAN swarm transfer, not a single-source pull. 3. `[users-icon] 5``.dl-peers`, inline-flex with 4px gap, icon at 0.7 opacity, count in `var(--t-1)` 600 tabular-nums. Hidden entirely when `game.peers` is falsy. Communicates this is a LAN swarm transfer; the full sentence lives in the `title` tooltip.
4. `8 min left` (`var(--t-2)`) 4. `8 min left` (`var(--t-2)`)
- **pct column** — large percentage, 20px / 700, `letter-spacing: -0.01em`, `var(--t-1)`. `%` glyph at 12px / 600 / 0.55 opacity. - **pct column** — large percentage, 20px / 700, `letter-spacing: -0.01em`, `var(--t-1)`. `%` glyph at 12px / 600 / 0.55 opacity.
- **cancel column** — 28×28 square, `1px solid var(--bd-2)`, `border-radius: 6px`, X icon. Hover: bg `rgba(239,68,68,0.12)`, border `rgba(239,68,68,0.40)`, text `#fca5a5`. Cancelling reverts the game to its prior state (`local` if any data was kept, `none` otherwise) — dev decides the underlying behavior. - **cancel column** — 28×28 square, `1px solid var(--bd-2)`, `border-radius: 6px`, X icon. Hover: bg `rgba(239,68,68,0.12)`, border `rgba(239,68,68,0.40)`, text `#fca5a5`. Cancelling reverts the game to its prior state (`local` if any data was kept, `none` otherwise) — dev decides the underlying behavior.
@@ -262,8 +262,8 @@ grid-template-areas:
**Graceful degradation in narrow modals:** **Graceful degradation in narrow modals:**
```css ```css
@container (max-width: 380px) { .dl-lg-secondary .dl-eta, .dl-lg-secondary .dl-sep-eta { display: none; } } @container (max-width: 320px) { .dl-lg-secondary .dl-eta, .dl-lg-secondary .dl-sep-eta { display: none; } }
@container (max-width: 300px) { .dl-lg-secondary .dl-peers, .dl-lg-secondary .dl-sep-peers { display: none; } } @container (max-width: 240px) { .dl-lg-secondary .dl-peers, .dl-lg-secondary .dl-sep-peers { display: none; } }
``` ```
ETA drops first, then peers; bytes + speed always stay (they're the actionable numbers). The pct/cancel column never collapses. ETA drops first, then peers; bytes + speed always stay (they're the actionable numbers). The pct/cancel column never collapses.
+1 -1
View File
@@ -164,7 +164,7 @@ function DownloadProgress({ game, accent, size = 'md', full = false }) {
<span className="dl-sep dl-sep-peers">·</span> <span className="dl-sep dl-sep-peers">·</span>
<span className="dl-peers" title={`Downloading from ${game.peers} ${game.peers === 1 ? 'peer' : 'peers'} on the LAN`}> <span className="dl-peers" title={`Downloading from ${game.peers} ${game.peers === 1 ? 'peer' : 'peers'} on the LAN`}>
<Icon.users/> <Icon.users/>
<span>from <strong>{game.peers}</strong> {game.peers === 1 ? 'peer' : 'peers'}</span> <span>{game.peers}</span>
</span> </span>
</React.Fragment> </React.Fragment>
)} )}
+5 -5
View File
@@ -785,21 +785,21 @@
.dl-lg-secondary .dl-bytes .dl-of { color: var(--t-2); font-weight: 500; } .dl-lg-secondary .dl-bytes .dl-of { color: var(--t-2); font-weight: 500; }
.dl-lg-secondary .dl-speed { color: var(--t-1); font-weight: 600; white-space: nowrap; } .dl-lg-secondary .dl-speed { color: var(--t-1); font-weight: 600; white-space: nowrap; }
.dl-lg-secondary .dl-peers { .dl-lg-secondary .dl-peers {
display: inline-flex; align-items: center; gap: 5px; display: inline-flex; align-items: center; gap: 4px;
color: var(--t-2); white-space: nowrap; color: var(--t-1); font-weight: 600; font-variant-numeric: tabular-nums;
white-space: nowrap;
} }
.dl-lg-secondary .dl-peers strong { color: var(--t-1); font-weight: 600; font-variant-numeric: tabular-nums; }
.dl-lg-secondary .dl-peers svg { opacity: 0.7; } .dl-lg-secondary .dl-peers svg { opacity: 0.7; }
.dl-lg-secondary .dl-eta { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; } .dl-lg-secondary .dl-eta { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.dl-sep { opacity: 0.45; } .dl-sep { opacity: 0.45; }
/* Gracefully drop the ETA when the modal is narrow */ /* Gracefully drop the ETA when the modal is narrow */
@container (max-width: 380px) { @container (max-width: 320px) {
.dl-lg-secondary .dl-eta, .dl-lg-secondary .dl-eta,
.dl-lg-secondary .dl-sep-eta { display: none; } .dl-lg-secondary .dl-sep-eta { display: none; }
} }
/* Even narrower: drop peers too, keep size + speed */ /* Even narrower: drop peers too, keep size + speed */
@container (max-width: 300px) { @container (max-width: 240px) {
.dl-lg-secondary .dl-peers, .dl-lg-secondary .dl-peers,
.dl-lg-secondary .dl-sep-peers { display: none; } .dl-lg-secondary .dl-sep-peers { display: none; }
} }