Hold the battle arena at a fixed height.
The play area grew and shrank as cards entered and left play. Two slots were sized independently of the cards that fill them: the pet zone reserved 150px for a 162px battle card, and the empty-deck placeholder kept its desktop 84x116 on phones where the face-down card is 56x78. So each faint/reveal moved the arena 12px per side on desktop, and a deck running dry moved it 38px per side on a phone. Every arena slot now derives from one set of --unit-*/--deck-* metrics on .battle, and holds that size whether or not a card occupies it. Measured across a battle's states (no pets, both revealed, one fainted, fans of apples, decks dry), the arena height spread goes from 24px desktop / 76px phone to 0. Also hide the arena's horizontal scrollbar: a wide apple fan made it appear and vanish mid-battle, and with a content-driven height that alone jumped the arena by the scrollbar's thickness. Touch, trackpad and shift+wheel still scroll it.
This commit is contained in:
@@ -295,10 +295,10 @@ export function BattlePhase({ view, send, step, setStep }: Props) {
|
|||||||
// The result dialog pops when the replay ends. "Not yet" dismisses it so the
|
// The result dialog pops when the replay ends. "Not yet" dismisses it so the
|
||||||
// battle can be rewatched; a toolbar button then remains to advance the round.
|
// battle can be rewatched; a toolbar button then remains to advance the round.
|
||||||
const [resultDismissed, setResultDismissed] = useState(false)
|
const [resultDismissed, setResultDismissed] = useState(false)
|
||||||
// The deck-peek popover lives inside .battlefield, which sets overflow-x
|
// The deck-peek popover lives inside .battlefield, which scrolls on one axis
|
||||||
// (and thus overflow-y) to auto — so an absolutely-positioned popover gets
|
// and hides the other — so an absolutely-positioned popover gets clipped to
|
||||||
// clipped to the battlefield. We anchor it to the hovered stack's viewport
|
// the battlefield. We anchor it to the hovered stack's viewport rect and
|
||||||
// rect and portal it to <body> so it floats over the whole window instead.
|
// portal it to <body> so it floats over the whole window instead.
|
||||||
const [peek, setPeek] = useState<{ seat: number; pos: 'top' | 'bottom'; rect: DOMRect } | null>(
|
const [peek, setPeek] = useState<{ seat: number; pos: 'top' | 'bottom'; rect: DOMRect } | null>(
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
|
|||||||
+45
-25
@@ -1990,6 +1990,15 @@ h3 {
|
|||||||
animation divides its base duration by this, so the visuals quicken in
|
animation divides its base duration by this, so the visuals quicken in
|
||||||
lockstep with the JS step timers. Fallback keeps calc() valid if unset. */
|
lockstep with the JS step timers. Fallback keeps calc() valid if unset. */
|
||||||
--battle-speed: 1;
|
--battle-speed: 1;
|
||||||
|
/* Arena slot metrics. Every slot in the battlefield — the pet in play and the
|
||||||
|
deck beneath/above it — is sized to these exactly, whether or not a card
|
||||||
|
currently occupies it. That keeps the arena a fixed reserved area so its
|
||||||
|
height never shifts as cards enter and leave play. Phones override them in
|
||||||
|
the media query below; nothing else should hardcode these sizes. */
|
||||||
|
--unit-w: 96px;
|
||||||
|
--unit-h: 162px;
|
||||||
|
--deck-w: 84px;
|
||||||
|
--deck-h: 116px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
@@ -2067,13 +2076,24 @@ h3 {
|
|||||||
(right) to fan out beside each pet; a big apple stack (Manatee, Fire Ant)
|
(right) to fan out beside each pet; a big apple stack (Manatee, Fire Ant)
|
||||||
scrolls horizontally rather than clipping. */
|
scrolls horizontally rather than clipping. */
|
||||||
padding: 18px 14px;
|
padding: 18px 14px;
|
||||||
min-height: 150px;
|
/* That sideways scroll keeps its scrollbar hidden: on classic-scrollbar
|
||||||
|
platforms it would appear and vanish as fans grow and shrink, and since the
|
||||||
|
arena's height is content-driven, that alone made it jump by the
|
||||||
|
scrollbar's thickness mid-battle. Touch, trackpad and shift+wheel still
|
||||||
|
scroll it. Vertical is hidden outright — pops that reach past the felt
|
||||||
|
(spawns, ailment badges) were never scrollable to anyway. */
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
scrollbar-width: none;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
var(--tray-inset),
|
var(--tray-inset),
|
||||||
inset 0 0 60px rgba(0, 0, 0, 0.25);
|
inset 0 0 60px rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.battlefield::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.battle-center {
|
.battle-center {
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 1.7rem;
|
font-size: 1.7rem;
|
||||||
@@ -2109,10 +2129,12 @@ h3 {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Centres the deck stack under (you) / over (opponent) the pet. */
|
/* Centres the deck stack under (you) / over (opponent) the pet. Its height is
|
||||||
|
the deck slot's, held whether the deck still has cards or has run dry. */
|
||||||
.battle-deck {
|
.battle-deck {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
height: var(--deck-h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- deck stacks --- */
|
/* --- deck stacks --- */
|
||||||
@@ -2125,8 +2147,8 @@ h3 {
|
|||||||
/* The face-down deck: a walnut-backed card with a woven diamond pattern and a
|
/* The face-down deck: a walnut-backed card with a woven diamond pattern and a
|
||||||
little stacked-depth shadow beneath it. */
|
little stacked-depth shadow beneath it. */
|
||||||
.card-back {
|
.card-back {
|
||||||
width: 84px;
|
width: var(--deck-w);
|
||||||
height: 116px;
|
height: var(--deck-h);
|
||||||
border-radius: var(--card-radius);
|
border-radius: var(--card-radius);
|
||||||
border: 2px solid var(--cocoa);
|
border: 2px solid var(--cocoa);
|
||||||
background:
|
background:
|
||||||
@@ -2156,9 +2178,11 @@ h3 {
|
|||||||
box-shadow: inset 0 0 0 2px rgba(255, 230, 200, 0.25);
|
box-shadow: inset 0 0 0 2px rgba(255, 230, 200, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The empty deck slot holds the face-down card's footprint exactly, so a deck
|
||||||
|
running dry doesn't resize the arena. */
|
||||||
.stack-empty {
|
.stack-empty {
|
||||||
width: 84px;
|
width: var(--deck-w);
|
||||||
height: 116px;
|
height: var(--deck-h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes summon-pop {
|
@keyframes summon-pop {
|
||||||
@@ -2303,10 +2327,14 @@ h3 {
|
|||||||
|
|
||||||
/* --- the pet in play --- */
|
/* --- the pet in play --- */
|
||||||
|
|
||||||
|
/* The pet's slot in the arena: a fixed reserved box the size of a battle card,
|
||||||
|
held even while it's empty (between a faint and the next reveal) so the
|
||||||
|
arena's height stays put as cards enter and leave play. Set-aside pets and the
|
||||||
|
food fan hang off it absolutely, so they never resize it either. */
|
||||||
.battle-unit-zone {
|
.battle-unit-zone {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: 100px;
|
width: var(--unit-w);
|
||||||
min-height: 150px;
|
height: var(--unit-h);
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
}
|
}
|
||||||
@@ -2419,11 +2447,11 @@ h3 {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fills its reserved slot. The slot is taller than a shop card so the full
|
||||||
|
ability text fits without shrinking to an unreadable size during battle. */
|
||||||
.battle-unit .card {
|
.battle-unit .card {
|
||||||
width: 96px;
|
width: var(--unit-w);
|
||||||
/* Taller than a shop card so the full ability text fits without shrinking to
|
height: var(--unit-h);
|
||||||
an unreadable size during the battle. */
|
|
||||||
height: 162px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Battle cards are short, so give the ability text more of the card by
|
/* Battle cards are short, so give the ability text more of the card by
|
||||||
@@ -3205,6 +3233,11 @@ h3 {
|
|||||||
/* --- battle: fit both sides and the clash between the rails --- */
|
/* --- battle: fit both sides and the clash between the rails --- */
|
||||||
.battle {
|
.battle {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
/* Smaller arena slots; the pet and deck boxes follow automatically. */
|
||||||
|
--unit-w: 66px;
|
||||||
|
--unit-h: 112px;
|
||||||
|
--deck-w: 56px;
|
||||||
|
--deck-h: 78px;
|
||||||
}
|
}
|
||||||
.battle-header {
|
.battle-header {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -3227,7 +3260,6 @@ h3 {
|
|||||||
}
|
}
|
||||||
.battlefield {
|
.battlefield {
|
||||||
padding: 8px 8px;
|
padding: 8px 8px;
|
||||||
min-height: 0;
|
|
||||||
gap: 3px;
|
gap: 3px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
@@ -3238,14 +3270,6 @@ h3 {
|
|||||||
.battle-side {
|
.battle-side {
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
.battle-unit .card {
|
|
||||||
width: 66px;
|
|
||||||
height: 112px;
|
|
||||||
}
|
|
||||||
.battle-unit-zone {
|
|
||||||
min-width: 72px;
|
|
||||||
min-height: 112px;
|
|
||||||
}
|
|
||||||
/* A battle card is small and glanced at, not read — so on a phone strip it to
|
/* A battle card is small and glanced at, not read — so on a phone strip it to
|
||||||
the essentials: big art, the power badge and any damage marker, and the
|
the essentials: big art, the power badge and any damage marker, and the
|
||||||
name. No suit dot, tier die, or ability text (the magnified view still shows
|
name. No suit dot, tier die, or ability text (the magnified view still shows
|
||||||
@@ -3270,10 +3294,6 @@ h3 {
|
|||||||
.battle-unit .card-art {
|
.battle-unit .card-art {
|
||||||
font-size: 2.1rem;
|
font-size: 2.1rem;
|
||||||
}
|
}
|
||||||
.card-back {
|
|
||||||
width: 56px;
|
|
||||||
height: 78px;
|
|
||||||
}
|
|
||||||
.card-back-count {
|
.card-back-count {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|||||||
Reference in New Issue
Block a user