2 Commits

Author SHA1 Message Date
Lukas
30e7ed4121 Stabilize turn navigation bar layout with CSS grid
All checks were successful
CI / check (push) Successful in 2m18s
CI / build-image (push) Successful in 17s
Use a three-column grid (1fr / auto / 1fr) so the active combatant
name stays centered while round badge and difficulty indicator are
anchored in the left and right zones. Prevents layout jumps when
the name changes between turns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 02:15:16 +02:00
Lukas
5540baf14c Show concentration icon on mobile as grey affordance
On touch devices, the Brain icon was fully hidden (opacity-0) unlike
the edit and condition buttons. Add pointer-coarse:opacity-50 so it
appears as a discoverable grey icon, matching the other action buttons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 01:02:16 +02:00
3 changed files with 26 additions and 25 deletions

View File

@@ -80,7 +80,7 @@ describe("TurnNavigation", () => {
expect(container.textContent).not.toContain("\u2014");
});
it("round badge and combatant name are siblings in the center area", () => {
it("round badge is in the left zone and name is in the center zone", () => {
renderNav(
buildEncounter({
combatants: [buildCombatant({ name: "Goblin" })],
@@ -88,7 +88,8 @@ describe("TurnNavigation", () => {
);
const badge = screen.getByText("R1");
const name = screen.getByText("Goblin");
expect(badge.closest(".flex")).toBe(name.parentElement);
// Badge and name are in separate grid cells to prevent layout shifts
expect(badge.parentElement).not.toBe(name.parentElement);
});
});

View File

@@ -430,7 +430,7 @@ function concentrationIconClass(
dimmed: boolean,
): string {
if (!isConcentrating)
return "opacity-0 group-hover:opacity-50 text-muted-foreground";
return "opacity-0 pointer-coarse:opacity-50 group-hover:opacity-50 text-muted-foreground";
return dimmed ? "opacity-50 text-purple-400" : "opacity-100 text-purple-400";
}

View File

@@ -26,19 +26,19 @@ export function TurnNavigation() {
const activeCombatant = encounter.combatants[encounter.activeIndex];
return (
<div className="card-glow flex items-center gap-3 border-border border-b bg-card px-4 py-3 sm:rounded-lg sm:border">
<Button
variant="ghost"
size="icon"
onClick={retreatTurn}
disabled={!hasCombatants || isAtStart}
title="Previous turn"
aria-label="Previous turn"
>
<StepBack className="h-5 w-5" />
</Button>
<div className="card-glow grid grid-cols-[1fr_minmax(0,auto)_1fr] items-center border-border border-b bg-card px-2 py-3 sm:rounded-lg sm:border sm:px-4">
{/* Left zone: navigation + history + round */}
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
onClick={retreatTurn}
disabled={!hasCombatants || isAtStart}
title="Previous turn"
aria-label="Previous turn"
>
<StepBack className="h-5 w-5" />
</Button>
<Button
variant="ghost"
size="icon"
@@ -59,21 +59,24 @@ export function TurnNavigation() {
>
<Redo2 className="h-4 w-4" />
</Button>
<span className="ml-1 rounded-md bg-muted px-2 py-0.5 font-semibold text-foreground text-sm tabular-nums">
R{encounter.roundNumber}
</span>
</div>
<div className="flex min-w-0 flex-1 items-center justify-center gap-2 text-sm">
<span className="shrink-0 rounded-md bg-muted px-2 py-0.5 font-semibold text-foreground text-sm">
<span className="-mt-[3px] inline-block">
R{encounter.roundNumber}
</span>
</span>
{/* Center zone: active combatant name */}
<div className="min-w-0 px-2 text-center text-sm">
{activeCombatant ? (
<span className="truncate font-medium">{activeCombatant.name}</span>
) : (
<span className="text-muted-foreground">No combatants</span>
)}
</div>
{/* Right zone: difficulty + destructive + forward */}
<div className="flex items-center justify-end gap-1">
{difficulty && (
<div className="relative">
<div className="relative mr-1">
<DifficultyIndicator
result={difficulty}
onClick={() => setShowBreakdown((prev) => !prev)}
@@ -85,9 +88,6 @@ export function TurnNavigation() {
) : null}
</div>
)}
</div>
<div className="flex flex-shrink-0 items-center gap-3">
<ConfirmButton
icon={<Trash2 className="h-5 w-5" />}
label="Clear encounter"