Let a long change chip wrap instead of running off the phone

A chip's value carried `white-space: nowrap`, which was there so a signed
number never breaks between its sign and its digits. It was applied to every
value a chip can hold, and most of them are phrases rather than numbers: a
refusal reason, "no change — at its limit", and the whole new value of a
free-text stat, which is a sentence the AI wrote rather than a word.

An unbreakable phrase sets the chip's minimum width to the width of the
phrase, so `max-width: 100%` capped the box while the text kept painting past
the rounded border and off the screen. The chip row sits inside the story
text, which has nothing to scroll, so the page grew a horizontal scrollbar
instead: a 87-character status value measured 470px against a 320px phone.

The nowrap now applies only to the signed number, under `chg-num`. Every other
value wraps inside its chip, which is what the surrounding `overflow-wrap`
already did for the label.

Measured in Chromium at 320, 360, 390 and 430px wide: that same value paints
between 55px and 165px outside its chip before, and 0px after, with the page
no longer scrolling sideways. The number chip stays on one line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017imUKVPhqophVUZZwJK7ST
This commit is contained in:
Claude
2026-08-30 06:56:34 +00:00
parent 91907a30bd
commit 1ed8646a04
2 changed files with 13 additions and 6 deletions
+10 -4
View File
@@ -1163,13 +1163,19 @@ button:disabled { opacity: 0.45; cursor: default; transform: none; box-shadow: n
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
/* A chip must never be wider than the column it sits in: an NPC stat like /* A chip must never be wider than the column it sits in: an NPC stat like
"bandit leader aggression +10" overruns a phone, and the chip row is "bandit leader aggression +10" overruns a phone, and the chip row is
inside the story text, which has nothing to scroll. The label may break inside the story text, which has nothing to scroll. Text may break
mid-word as a last resort; the value is glued to it by its own nowrap so mid-word as a last resort, which is what keeps a chip's minimum width
the sign and number never end up on separate lines. */ small enough for the flex row to shrink it. */
max-width: 100%; max-width: 100%;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.chg-val { white-space: nowrap; } .chg-val { overflow-wrap: anywhere; }
/* Only a signed number is held on one line, so the sign never ends up on a
line of its own. The other values a chip can carry are phrases — a refusal
reason, "no change — at its limit", or the whole new value of a free-text
stat — and holding those together set the chip's minimum width to the width
of the phrase, which is what pushed a chip out of the message on a phone. */
.chg-num { white-space: nowrap; }
.chg-up { color: var(--player, #4caf82); border-color: color-mix(in srgb, var(--player, #4caf82) 40%, var(--border)); } .chg-up { color: var(--player, #4caf82); border-color: color-mix(in srgb, var(--player, #4caf82) 40%, var(--border)); }
.chg-down { color: var(--danger, #e5484d); border-color: color-mix(in srgb, var(--danger, #e5484d) 40%, var(--border)); } .chg-down { color: var(--danger, #e5484d); border-color: color-mix(in srgb, var(--danger, #e5484d) 40%, var(--border)); }
.chg-flag { color: var(--accent-bright); } .chg-flag { color: var(--accent-bright); }
+3 -2
View File
@@ -1252,9 +1252,10 @@ function StateChangeChips({ changes }) {
// neither an update nor a refusal, and "+0" read as the former. // neither an update nor a refusal, and "+0" read as the former.
const blocked = c.clamped && d === 0 const blocked = c.clamped && d === 0
const dir = blocked ? 'refused' : typeof d === 'number' ? (d > 0 ? 'up' : d < 0 ? 'down' : 'flat') : 'flat' const dir = blocked ? 'refused' : typeof d === 'number' ? (d > 0 ? 'up' : d < 0 ? 'down' : 'flat') : 'flat'
const signed = !blocked && typeof d === 'number'
const txt = blocked const txt = blocked
? 'no change — at its limit' ? 'no change — at its limit'
: typeof d === 'number' ? (d > 0 ? `+${d}` : `${d}`) : `→ ${c.value}` : signed ? (d > 0 ? `+${d}` : `${d}`) : `→ ${c.value}`
const title = blocked const title = blocked
? c.fix || 'The story asked to change this and it is already at the limit the scenario allows.' ? c.fix || 'The story asked to change this and it is already at the limit the scenario allows.'
: c.clamped : c.clamped
@@ -1262,7 +1263,7 @@ function StateChangeChips({ changes }) {
: undefined : undefined
return ( return (
<span key={i} className={`chg chg-stat chg-${dir}`} title={title}> <span key={i} className={`chg chg-stat chg-${dir}`} title={title}>
{nice(c.label)} <span className="chg-val">{txt}</span> {nice(c.label)} <span className={signed ? 'chg-val chg-num' : 'chg-val'}>{txt}</span>
{c.clamped && !blocked ? <span className="chg-limited"> (limited)</span> : null} {c.clamped && !blocked ? <span className="chg-limited"> (limited)</span> : null}
</span> </span>
) )