Restyle HP display as compact rounded pill

Group current HP, temp HP, and max HP into a single bordered
pill container with a subtle slash separator. Removes the
scattered layout with separate elements and gaps. Temp HP +N
only renders when present (no invisible spacer).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-23 13:11:28 +01:00
parent 8bf69fd47d
commit 9cdf004c15
3 changed files with 41 additions and 78 deletions

View File

@@ -172,7 +172,12 @@ function MaxHpDisplay({
<button
type="button"
onClick={startEditing}
className="inline-block h-7 min-w-[3ch] text-center text-muted-foreground text-sm tabular-nums leading-7 transition-colors hover:text-hover-neutral"
className={cn(
"inline-block h-7 min-w-[3ch] text-center leading-7 transition-colors hover:text-hover-neutral",
maxHp === undefined
? "text-muted-foreground text-sm"
: "text-muted-foreground text-xs",
)}
>
{maxHp ?? "Max"}
</button>
@@ -183,35 +188,20 @@ function ClickableHp({
currentHp,
maxHp,
tempHp,
hasTempHp,
onAdjust,
onSetTempHp,
dimmed,
}: Readonly<{
currentHp: number | undefined;
maxHp: number | undefined;
tempHp: number | undefined;
hasTempHp: boolean;
onAdjust: (delta: number) => void;
onSetTempHp: (value: number) => void;
dimmed?: boolean;
}>) {
const [popoverOpen, setPopoverOpen] = useState(false);
const status = deriveHpStatus(currentHp, maxHp);
if (maxHp === undefined) {
return (
<span
className={cn(
"inline-block h-7 w-[4ch] text-center text-muted-foreground text-sm tabular-nums leading-7",
dimmed && "opacity-50",
)}
role="status"
aria-label="No HP set"
>
--
</span>
);
return null;
}
return (
@@ -221,24 +211,17 @@ function ClickableHp({
onClick={() => setPopoverOpen(true)}
aria-label={`Current HP: ${currentHp}${tempHp ? ` (+${tempHp} temp)` : ""} (${status})`}
className={cn(
"inline-block h-7 min-w-[3ch] text-center font-medium text-sm tabular-nums leading-7 transition-colors hover:text-hover-neutral",
"inline-block h-7 min-w-[3ch] text-center font-medium text-sm leading-7 transition-colors hover:text-hover-neutral",
status === "bloodied" && "text-amber-400",
status === "unconscious" && "text-red-400",
status === "healthy" && "text-foreground",
dimmed && "opacity-50",
)}
>
{currentHp}
</button>
{!!hasTempHp && (
<span
className={cn(
"inline-block min-w-[3ch] text-center text-sm tabular-nums leading-7",
tempHp ? "font-medium text-cyan-400" : "invisible",
dimmed && "opacity-50",
)}
>
{tempHp ? `+${tempHp}` : ""}
{!!tempHp && (
<span className="font-medium text-cyan-400 text-sm leading-7">
+{tempHp}
</span>
)}
{!!popoverOpen && (
@@ -463,7 +446,6 @@ export function CombatantRow({
setHp,
adjustHp,
setTempHp,
hasTempHp,
setAc,
toggleCondition,
toggleConcentration,
@@ -615,29 +597,26 @@ export function CombatantRow({
</div>
{/* HP */}
<div className="flex items-center gap-1">
<div
className={cn(
"flex items-center rounded-md tabular-nums",
maxHp === undefined
? ""
: "gap-0.5 border border-border/50 bg-muted/30 px-1.5",
dimmed && "opacity-50",
)}
>
<ClickableHp
currentHp={currentHp}
maxHp={maxHp}
tempHp={combatant.tempHp}
hasTempHp={hasTempHp}
onAdjust={(delta) => adjustHp(id, delta)}
onSetTempHp={(value) => setTempHp(id, value)}
dimmed={dimmed}
/>
{maxHp !== undefined && (
<span
className={cn(
"text-muted-foreground text-sm tabular-nums",
dimmed && "opacity-50",
)}
>
/
</span>
<span className="text-muted-foreground/50 text-xs">/</span>
)}
<div className={cn(dimmed && "opacity-50")}>
<MaxHpDisplay maxHp={maxHp} onCommit={(v) => setHp(id, v)} />
</div>
<MaxHpDisplay maxHp={maxHp} onCommit={(v) => setHp(id, v)} />
</div>
{/* Actions */}