diff --git a/CLAUDE.md b/CLAUDE.md index d7f1ea7..b6aa2a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,7 @@ The constitution (`.specify/memory/constitution.md`) governs all feature work: - Browser localStorage (existing adapter, transparent JSON serialization) (016-combatant-ac) - TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, Lucide React (icons) (017-combat-conditions) - N/A (no storage changes — existing localStorage persistence unchanged) (019-combatant-row-declutter) +- TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React (icons) (020-fix-zero-hp-opacity) ## Recent Changes - 003-remove-combatant: Added TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite diff --git a/apps/web/src/components/combatant-row.tsx b/apps/web/src/components/combatant-row.tsx index 3a55f29..0a06684 100644 --- a/apps/web/src/components/combatant-row.tsx +++ b/apps/web/src/components/combatant-row.tsx @@ -154,17 +154,24 @@ function ClickableHp({ currentHp, maxHp, onAdjust, + dimmed, }: { currentHp: number | undefined; maxHp: number | undefined; onAdjust: (delta: number) => void; + dimmed?: boolean; }) { const [popoverOpen, setPopoverOpen] = useState(false); const status = deriveHpStatus(currentHp, maxHp); if (maxHp === undefined) { return ( - + -- ); @@ -180,6 +187,7 @@ function ClickableHp({ status === "bloodied" && "text-amber-400", status === "unconscious" && "text-red-400", status === "healthy" && "text-foreground", + dimmed && "opacity-50", )} > {currentHp} @@ -271,6 +279,7 @@ export function CombatantRow({ }: CombatantRowProps) { const { id, name, initiative, maxHp, currentHp } = combatant; const status = deriveHpStatus(currentHp, maxHp); + const dimmed = status === "unconscious"; const [pickerOpen, setPickerOpen] = useState(false); const prevHpRef = useRef(currentHp); @@ -309,7 +318,6 @@ export function CombatantRow({ : combatant.isConcentrating ? "border-l-2 border-l-purple-400" : "border-l-2 border-l-transparent", - status === "unconscious" && "opacity-50", isPulsing && "animate-concentration-pulse", )} > @@ -323,7 +331,9 @@ export function CombatantRow({ className={cn( "flex items-center justify-center transition-opacity", combatant.isConcentrating - ? "opacity-100 text-purple-400" + ? dimmed + ? "opacity-50 text-purple-400" + : "opacity-100 text-purple-400" : "opacity-0 group-hover:opacity-50 text-muted-foreground", )} > @@ -336,7 +346,10 @@ export function CombatantRow({ inputMode="numeric" value={initiative ?? ""} placeholder="--" - className="h-7 w-[6ch] text-center text-sm tabular-nums" + className={cn( + "h-7 w-[6ch] text-center text-sm tabular-nums", + dimmed && "opacity-50", + )} onChange={(e) => { const raw = e.target.value; if (raw === "") { @@ -351,10 +364,14 @@ export function CombatantRow({ /> {/* Name */} - +
+ +
{/* AC */} - onSetAc(id, v)} /> +
+ onSetAc(id, v)} /> +
{/* HP */}
@@ -362,20 +379,31 @@ export function CombatantRow({ currentHp={currentHp} maxHp={maxHp} onAdjust={(delta) => onAdjustHp(id, delta)} + dimmed={dimmed} /> {maxHp !== undefined && ( - + / )} - onSetHp(id, v)} /> +
+ onSetHp(id, v)} /> +
{/* Actions */}