feat(ui): batch of UI polish tweaks
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
- admin dashboard "Users" count excludes robots (CountUsers, humans only) - lobby finished-game delete reveal: background matches header/tab-bar (--bg-elev) - mobile: swipe up on the zoom-out board (no staged tiles) shuffles the rack - lobby: status icons -25%, game-row top/bottom padding halved - toast: info tier drifts up ~a tab-bar height while fading over 2s and dismisses on tap; error tier unchanged - game: confirm-move button stays pinned at the right edge; rack tiles shrink to fit so a full rack never overflows onto it - telegram: a successful invite-link redeem shows a welcome window pointing at the bot - settings: remove the grid-lines toggle; the board is always a gapless checkerboard - settings/comms hubs: the selected tab highlight wraps icon + label as a pill Docs: UI_DESIGN (toast, board surface, selected tab), PLAN; e2e updated for the removed grid-lines toggle.
This commit is contained in:
+18
-33
@@ -18,7 +18,6 @@
|
||||
landscape,
|
||||
variant,
|
||||
labelMode,
|
||||
lines,
|
||||
locale,
|
||||
focus,
|
||||
recenter,
|
||||
@@ -41,8 +40,6 @@
|
||||
landscape: boolean;
|
||||
variant: Variant;
|
||||
labelMode: BoardLabelMode;
|
||||
/** Draw 1px grid lines between cells; when false the board is a gapless checkerboard. */
|
||||
lines: boolean;
|
||||
locale: Locale;
|
||||
focus: { row: number; col: number } | null;
|
||||
/** A monotonic nonce the parent bumps to request a scroll to `focus` even when the zoom state
|
||||
@@ -234,7 +231,7 @@
|
||||
|
||||
<div class="viewport" class:zoomed class:land={landscape} bind:this={viewport}>
|
||||
<div class="scaler" class:land={landscape} style="--z: {z};">
|
||||
<div class="grid" class:gridless={!lines}>
|
||||
<div class="grid">
|
||||
{#each board as rowCells, r (r)}
|
||||
{#each rowCells as cell, c (c)}
|
||||
{@const p = pending.get(key(r, c))}
|
||||
@@ -311,18 +308,21 @@
|
||||
width 0.25s ease,
|
||||
height 0.25s ease;
|
||||
}
|
||||
/* A gapless checkerboard with no grid lines (the board has no lined variant): plain cells
|
||||
alternate shades and tiles get rounded corners plus a soft right-side shadow so adjacent
|
||||
gapless tiles still read as separate pieces. */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(15, 1fr);
|
||||
gap: 1px;
|
||||
background: var(--cell-line);
|
||||
padding: 1px;
|
||||
gap: 0;
|
||||
background: var(--board-bg);
|
||||
padding: 0;
|
||||
}
|
||||
.cell {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
border: none;
|
||||
border-radius: 1px;
|
||||
border-radius: 0;
|
||||
background: var(--cell-bg);
|
||||
color: var(--prem-text);
|
||||
/* No mobile tap flash on a cell tap (parity with the web click; the only intentional
|
||||
@@ -344,11 +344,20 @@
|
||||
.cell.dl {
|
||||
background: var(--prem-dl);
|
||||
}
|
||||
.cell.dark {
|
||||
/* A gentle checkerboard tint: enough to read the alternation without competing with the
|
||||
bonus cells, standing in for the grid lines that once separated the cells. Lightened
|
||||
from a stronger mix that looked too contrasty in light theme. */
|
||||
background: color-mix(in srgb, var(--cell-bg) 94%, #000);
|
||||
}
|
||||
.cell.filled,
|
||||
.cell.pending {
|
||||
background: var(--tile-bg);
|
||||
color: var(--tile-text);
|
||||
box-shadow: inset 0 -2px 0 var(--tile-edge);
|
||||
border-radius: 4px;
|
||||
box-shadow:
|
||||
inset 0 -2px 0 var(--tile-edge),
|
||||
2px 0 3px -1px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.cell.pending {
|
||||
background: var(--tile-pending);
|
||||
@@ -356,30 +365,6 @@
|
||||
board) instead of the touch starting a board pan. */
|
||||
touch-action: none;
|
||||
}
|
||||
/* Lines-off variant: a gapless checkerboard. The 1px grid gaps (and the cell-line they
|
||||
reveal) collapse, saving ~14px of board width; plain cells alternate shades, and tiles
|
||||
get rounded corners and a soft right-side shadow so adjacent gapless tiles still read
|
||||
as separate pieces. */
|
||||
.grid.gridless {
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
background: var(--board-bg);
|
||||
}
|
||||
.grid.gridless .cell {
|
||||
border-radius: 0;
|
||||
}
|
||||
.grid.gridless .cell.dark {
|
||||
/* A gentle checkerboard tint: enough to read the alternation without competing with the
|
||||
bonus cells. Lightened from a stronger mix that looked too contrasty in light theme. */
|
||||
background: color-mix(in srgb, var(--cell-bg) 94%, #000);
|
||||
}
|
||||
.grid.gridless .cell.filled,
|
||||
.grid.gridless .cell.pending {
|
||||
border-radius: 4px;
|
||||
box-shadow:
|
||||
inset 0 -2px 0 var(--tile-edge),
|
||||
2px 0 3px -1px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.cell.droptarget {
|
||||
/* The cell a carried tile is aimed at: an accent ring plus a light accent wash, so the
|
||||
target reads clearly while dragging without obscuring the bonus label underneath. */
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
{#snippet tabbar()}
|
||||
<TabBar>
|
||||
<button class="tab" class:active={tab === 'chat'} onclick={() => (tab = 'chat')}>
|
||||
<span class="sq" aria-hidden="true">💬</span><span class="lbl">{t('game.chat')}</span>
|
||||
<span class="face"><span class="sq" aria-hidden="true">💬</span><span class="lbl">{t('game.chat')}</span></span>
|
||||
</button>
|
||||
{#if active}
|
||||
<button class="tab" class:active={tab === 'dictionary'} onclick={() => (tab = 'dictionary')}>
|
||||
<span class="sq" aria-hidden="true">🔎</span><span class="lbl">{t('game.dictionary')}</span>
|
||||
<span class="face"><span class="sq" aria-hidden="true">🔎</span><span class="lbl">{t('game.dictionary')}</span></span>
|
||||
</button>
|
||||
{/if}
|
||||
</TabBar>
|
||||
|
||||
+10
-1
@@ -813,6 +813,8 @@
|
||||
// - closed: a downward "pull" opens the history, but only on the zoom-out board scrolled
|
||||
// to its top — zoomed, the one-finger drag pans the board, and mid-scroll a downward drag
|
||||
// is the stage's own vertical scroll (the conflict that once retired this open gesture).
|
||||
// On that same closed, zoom-out board an upward swipe with no staged tiles shuffles the
|
||||
// rack (a convenience mirror of the shuffle control).
|
||||
// Both genuinely set `historyOpen` (closing no longer merely scrolls the slid board out of
|
||||
// view, which left a stale-open state that made a follow-up score-bar tap "jump" the board).
|
||||
let stageEl = $state<HTMLDivElement>();
|
||||
@@ -869,6 +871,14 @@
|
||||
if (-dy > 32) closeHistoryByGesture(); // enough upward travel
|
||||
} else if (dy > 40 && dy > Math.abs(dx) * 1.4) {
|
||||
openHistoryByGesture(); // a clear, vertical-dominant downward pull
|
||||
} else if (-dy > 40 && -dy > Math.abs(dx) * 1.4 && placement.pending.length === 0) {
|
||||
// The same closed-board arm, opposite direction: a clear upward swipe on the zoom-out
|
||||
// board with no staged tiles shuffles the rack (a convenience mirror of the shuffle
|
||||
// control). Disarm and swallow the synthesised click so it cannot also place a tile.
|
||||
shuffle();
|
||||
boardSwipe = null;
|
||||
swallowClick = true;
|
||||
setTimeout(() => (swallowClick = false), 120);
|
||||
}
|
||||
}
|
||||
function onBoardWrapUp(e: PointerEvent) {
|
||||
@@ -1156,7 +1166,6 @@
|
||||
{landscape}
|
||||
{variant}
|
||||
labelMode={app.boardLabels}
|
||||
lines={app.boardLines}
|
||||
locale={app.locale}
|
||||
{focus}
|
||||
{recenter}
|
||||
|
||||
@@ -83,7 +83,12 @@
|
||||
}
|
||||
.tile {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
/* Sit at the natural tile width, but shrink to fit when the row is crowded so a full
|
||||
rack never overflows its wrapper onto the MakeMove control to its right (the confirm
|
||||
button stays pinned at the right screen edge). flex-grow stays 0 so a sparse rack does
|
||||
not stretch its tiles. */
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
width: min(12.5vw, 46px);
|
||||
aspect-ratio: 1;
|
||||
background: var(--tile-bg);
|
||||
|
||||
Reference in New Issue
Block a user