mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-11 03:13:45 +00:00
style(web): fit a finished game and its lobby on one screen
After a game the buttons that start the next one were under the fold on a 1080p screen: the result panel needed 206px more than the column had, and 300 with four players and a list of words the loser missed. Every game ended with a scroll to find "Sẵn sàng". The duplication went first. The board's scoreboard and the result's standings are the same names and the same scores, so the board's stops at the final whistle and the standings are shown for two players as well as for four. Under a result the lobby lists the seats that exist rather than three "Còn trống" rows the count line above already accounts for, the two stat tiles are one line of small type — the score is in the table now — and the words the loser could have played sit on the line of their own label rather than under it. The rest is chrome giving back pixels: a shorter header and footer, a 40px theme button, tighter seat rows and card padding, and the panel's "Về trang chủ" and "Tải chuỗi từ" side by side instead of stacked, which wrap back to one each on a phone. Readying, starting and playing a word all keep their 44px. Four seats, a loser's suggestions and a full lobby now come to exactly the height of a 1080p column, with the chain scrolling inside what is left. A phone still scrolls, which is what a phone is for.
This commit is contained in:
1 parent
a0029ba5aa
commit
644379fa06
8 files changed
+96
-44
No files matched your search
@@ -31,10 +31,10 @@
|
||||
* the credit is an obligation, not a decoration.
|
||||
*/
|
||||
.credit {
|
||||
padding-block: var(--space-3);
|
||||
padding-block: var(--space-2);
|
||||
padding-left: max(16px, env(safe-area-inset-left));
|
||||
padding-right: max(16px, env(safe-area-inset-right));
|
||||
padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
|
||||
padding-bottom: max(var(--space-2), env(safe-area-inset-bottom));
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-2);
|
||||
|
||||
@@ -84,7 +84,12 @@
|
||||
{#if modeLabel}<span class="mode">{modeLabel}</span>{/if}
|
||||
</div>
|
||||
|
||||
<ScoreBoard />
|
||||
<!-- Not once the game is over: the result panel below carries the same
|
||||
names and scores as a ranked table, and two of them is one screenful
|
||||
of duplication between the result and the lobby's own buttons. -->
|
||||
{#if game.state.phase !== 'over'}
|
||||
<ScoreBoard />
|
||||
{/if}
|
||||
|
||||
{#if banner}{@render banner()}{/if}
|
||||
|
||||
@@ -176,7 +181,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
gap: 14px;
|
||||
gap: var(--space-3);
|
||||
min-height: 0;
|
||||
padding-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
@@ -62,9 +62,11 @@
|
||||
<p class="reason">{endReasonMessages[result.reason]}</p>
|
||||
{/if}
|
||||
|
||||
{#if standings.length > 2}
|
||||
{#if standings.length > 1}
|
||||
<!-- Ranked by who outlasted whom, which is what the game is decided
|
||||
on. The score sits beside the place rather than setting it. -->
|
||||
on. The score sits beside the place rather than setting it.
|
||||
Shown for two players as well now that it is the only table on
|
||||
the screen: the board's own stops at the final whistle. -->
|
||||
<ol class="standings" aria-label={t.standingsTitle} data-testid="standings">
|
||||
{#each standings as player (player.playerId)}
|
||||
<li class:me={player.isMe} class:winner={player.rank === 1}>
|
||||
@@ -97,7 +99,7 @@
|
||||
nothing, which is worth hearing too. -->
|
||||
{#if elimination.suggestions.length > 0}
|
||||
<div class="suggestions">
|
||||
<h3>{t.suggestionsTitle}</h3>
|
||||
<h3>{t.suggestionsTitle}:</h3>
|
||||
<ul>
|
||||
{#each elimination.suggestions as word}
|
||||
<li>{word}</li>
|
||||
@@ -115,14 +117,17 @@
|
||||
<p class="record">{t.newRecord}</p>
|
||||
{/if}
|
||||
|
||||
<!-- One row, wrapping when the words no longer fit it. Stacked, the
|
||||
three of them were 150px of buttons, which on a 1080p screen was
|
||||
what pushed the lobby's own — the ones that start the next game —
|
||||
off the bottom. -->
|
||||
<div class="actions">
|
||||
{#if onrematch}
|
||||
<button type="button" class="primary" onclick={onrematch}>{t.rematch}</button>
|
||||
{/if}
|
||||
<button type="button" onclick={onhome}>{t.home}</button>
|
||||
<button type="button" class="export" onclick={exportHistory}>{t.exportHistory}</button>
|
||||
</div>
|
||||
|
||||
<button type="button" class="export" onclick={exportHistory}>{t.exportHistory}</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -130,8 +135,8 @@
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-5);
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: var(--surface);
|
||||
@@ -142,7 +147,7 @@
|
||||
h2 {
|
||||
margin: 0;
|
||||
color: var(--danger);
|
||||
font-size: var(--text-8);
|
||||
font-size: var(--text-7);
|
||||
}
|
||||
|
||||
h2.won {
|
||||
@@ -168,7 +173,8 @@
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
padding: var(--space-1) var(--space-3);
|
||||
font-size: var(--text-5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-alt);
|
||||
@@ -201,27 +207,48 @@
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* One line rather than two stacked tiles. The score is in the table above
|
||||
this, so the tiles were 40px of repeating it — and 40px is the
|
||||
difference between the lobby's buttons being on a 1080p screen and
|
||||
under it. */
|
||||
.stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: var(--space-8);
|
||||
gap: var(--space-2) var(--space-5);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.stats div {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
dt {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-3);
|
||||
font-size: var(--text-4);
|
||||
}
|
||||
|
||||
dd {
|
||||
margin: 0;
|
||||
font-size: var(--text-8);
|
||||
font-size: var(--text-5);
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Label and words on one line, wrapping when they run out of it: a heading
|
||||
of its own cost a whole row of a screen the lobby's buttons are also on. */
|
||||
.suggestions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 6px var(--space-2);
|
||||
}
|
||||
|
||||
.suggestions h3 {
|
||||
margin: 0 0 6px;
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-3);
|
||||
font-weight: 600;
|
||||
@@ -242,7 +269,7 @@
|
||||
}
|
||||
|
||||
.suggestions li {
|
||||
padding: 6px var(--space-3);
|
||||
padding: var(--space-1) var(--space-3);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--surface-alt);
|
||||
@@ -263,26 +290,19 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.export {
|
||||
min-height: 44px;
|
||||
padding: 10px var(--space-3);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-5);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Wrapping rather than shrinking: three of these across a phone would
|
||||
break "Tải chuỗi từ" over two lines, so the row gives way instead. */
|
||||
.actions button {
|
||||
flex: 1;
|
||||
flex: 1 1 auto;
|
||||
min-width: 120px;
|
||||
min-height: 44px;
|
||||
padding: var(--space-3);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
@@ -294,4 +314,11 @@
|
||||
background: var(--accent);
|
||||
color: var(--accent-text);
|
||||
}
|
||||
|
||||
/* Keeping the chain is worth offering and not worth pressing first. */
|
||||
.actions .export {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-5);
|
||||
}
|
||||
</style>
|
||||
@@ -36,7 +36,12 @@
|
||||
const s = $derived(game.state);
|
||||
// The seats nobody is in yet, drawn so a room that is waiting on people
|
||||
// looks like one rather than like a room that is simply small.
|
||||
const empties = $derived(Array.from({ length: game.freeSeats }, (_, i) => i));
|
||||
// Not under a finished game: there the count line above says 3/4 already,
|
||||
// and three empty rows between the result and the button that starts the
|
||||
// next game is what pushed that button off a 1080p screen.
|
||||
const empties = $derived(
|
||||
compact ? [] : Array.from({ length: game.freeSeats }, (_, i) => i)
|
||||
);
|
||||
const shortHanded = $derived(s.roomPlayers.length < s.minPlayers);
|
||||
const offline = $derived(connection.status !== Status.OPEN);
|
||||
// The owner is who everybody else is waiting on, so the hint has to stop
|
||||
@@ -77,7 +82,7 @@
|
||||
$effect(() => () => clearTimeout(armTimer));
|
||||
</script>
|
||||
|
||||
<section class="lobby" aria-label={t.lobbyTitle}>
|
||||
<section class="lobby" class:compact aria-label={t.lobbyTitle}>
|
||||
<!-- The board carries these in the compact case, and two connection badges
|
||||
on one screen say nothing the first one did not. -->
|
||||
{#if !compact}
|
||||
@@ -239,7 +244,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 14px;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.top {
|
||||
@@ -268,12 +273,21 @@
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
padding: var(--space-3) 14px;
|
||||
/* A seat is a row to read, not a target to hit, so it keeps the
|
||||
type and gives up the padding: four of them decide whether the
|
||||
buttons under the list are on screen. */
|
||||
padding: var(--space-2) 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
/* Tighter still under a finished game, where the list is competing with a
|
||||
result panel for the same screen. */
|
||||
.compact .seat {
|
||||
padding: var(--space-1) 14px;
|
||||
}
|
||||
|
||||
/* Readiness is a tint. Being the owner is a marker down the edge: a fact
|
||||
about the seat, not a state it has entered. */
|
||||
.seat.ready {
|
||||
@@ -401,7 +415,10 @@
|
||||
|
||||
.actions button {
|
||||
flex: 1;
|
||||
padding: 14px;
|
||||
/* Still a full 44px: readying and starting are what a player came
|
||||
here to press. */
|
||||
min-height: 44px;
|
||||
padding: 10px var(--space-3);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
@@ -427,8 +444,8 @@
|
||||
|
||||
.leave {
|
||||
align-self: center;
|
||||
min-height: 44px;
|
||||
padding: 10px var(--space-4);
|
||||
min-height: 36px;
|
||||
padding: var(--space-1) var(--space-4);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
background: transparent;
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
align-items: center;
|
||||
flex: 1 1 0;
|
||||
min-width: 72px;
|
||||
padding: 6px var(--space-2);
|
||||
padding: var(--space-1) var(--space-2);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
@@ -115,7 +115,7 @@
|
||||
}
|
||||
|
||||
.score {
|
||||
font-size: var(--text-8);
|
||||
font-size: var(--text-7);
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 50%;
|
||||
background: var(--surface);
|
||||
|
||||
@@ -61,7 +61,10 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding-block: 14px;
|
||||
/* The header is chrome: every pixel of it is one the board and the
|
||||
lobby below do not get, and on a 1080p screen the post-game panel
|
||||
was the one that had to scroll for them. */
|
||||
padding-block: var(--space-2);
|
||||
padding-left: max(16px, env(safe-area-inset-left));
|
||||
padding-right: max(16px, env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@
|
||||
*/
|
||||
.pane.game {
|
||||
flex: 1;
|
||||
gap: var(--space-4);
|
||||
gap: var(--space-3);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user