Play: Game-Selection step + mobile sticky on-brand keyboard
Mobile polish for the games. * New flow: Play Hub → Game Selection → Game. The 5/6 (word) and S/M/L (search) pickers move OFF the game screen onto a selection screen that shows each option's today-status. Back-button reads "Game Selection" in a game and "Play Hub" on the selection screen — buys vertical room for the keyboard. * Daily Word on mobile now fills the height: the board scrolls in the middle and the keyboard is pinned at the bottom, always reachable (no scrolling down to type). Desktop stays inline. * Keyboard restyled on-brand: warm cream keys (was cool generic grey), the label font, an accent-tinted Enter, and the same green/gold/grey feedback as the tiles; full-bleed, tactile press, safe-area aware. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -153,40 +153,44 @@
|
||||
{#if loading}
|
||||
<p class="muted">Loading today’s puzzle…</p>
|
||||
{:else}
|
||||
<div class="board" style="--len:{length}">
|
||||
{#each Array(maxGuesses) as _, r (r)}
|
||||
{@const g = guesses[r]}
|
||||
{@const cs = cols[r] || null}
|
||||
<div class="row">
|
||||
{#each Array(length) as _, c (c)}
|
||||
{@const ch = g ? g[c] : (r === guesses.length ? current[c] : '')}
|
||||
<div class="tile {cs ? cs[c] : ''}" class:filled={!!ch}>{(ch || '').toUpperCase()}</div>
|
||||
{/each}
|
||||
<div class="play-area">
|
||||
<div class="board" style="--len:{length}">
|
||||
{#each Array(maxGuesses) as _, r (r)}
|
||||
{@const g = guesses[r]}
|
||||
{@const cs = cols[r] || null}
|
||||
<div class="row">
|
||||
{#each Array(length) as _, c (c)}
|
||||
{@const ch = g ? g[c] : (r === guesses.length ? current[c] : '')}
|
||||
<div class="tile {cs ? cs[c] : ''}" class:filled={!!ch}>{(ch || '').toUpperCase()}</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if message}<p class="flash">{message}</p>{/if}
|
||||
|
||||
{#if status !== 'playing'}
|
||||
<div class="result rise">
|
||||
<p class="rmark">{status === 'won' ? '✦ nicely done ✦' : 'today’s word:'}
|
||||
{#if status === 'lost' && answer}<strong>{answer.toUpperCase()}</strong>{/if}</p>
|
||||
{#if why}<p class="why"><span class="lbl">Why this word</span>{why}</p>{/if}
|
||||
{#if stats}
|
||||
<div class="stats">
|
||||
<div><span class="n">{stats.played}</span><span class="l">played</span></div>
|
||||
<div><span class="n">{stats.played ? Math.round(100 * stats.won / stats.played) : 0}%</span><span class="l">solved</span></div>
|
||||
<div><span class="n">{stats.streak}</span><span class="l">streak</span></div>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="share" onclick={share}>{copied ? 'Copied!' : 'Share result'}</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if message}<p class="flash">{message}</p>{/if}
|
||||
|
||||
{#if status !== 'playing'}
|
||||
<div class="result rise">
|
||||
<p class="rmark">{status === 'won' ? '✦ nicely done ✦' : 'today’s word:'}
|
||||
{#if status === 'lost' && answer}<strong>{answer.toUpperCase()}</strong>{/if}</p>
|
||||
{#if why}<p class="why"><span class="lbl">Why this word</span>{why}</p>{/if}
|
||||
{#if stats}
|
||||
<div class="stats">
|
||||
<div><span class="n">{stats.played}</span><span class="l">played</span></div>
|
||||
<div><span class="n">{stats.played ? Math.round(100 * stats.won / stats.played) : 0}%</span><span class="l">solved</span></div>
|
||||
<div><span class="n">{stats.streak}</span><span class="l">streak</span></div>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="share" onclick={share}>{copied ? 'Copied!' : 'Share result'}</button>
|
||||
</div>
|
||||
{:else}
|
||||
{#if status === 'playing'}
|
||||
<div class="keyboard">
|
||||
{#each ROWS as row, ri (ri)}
|
||||
<div class="krow">
|
||||
{#if ri === 2}<button class="key wide" onclick={() => key('enter')}>Enter</button>{/if}
|
||||
{#if ri === 2}<button class="key wide enter" onclick={() => key('enter')}>Enter</button>{/if}
|
||||
{#each row as k (k)}
|
||||
<button class="key {keyState[k] || ''}" onclick={() => key(k)}>{k.toUpperCase()}</button>
|
||||
{/each}
|
||||
@@ -217,19 +221,34 @@
|
||||
text-align: center; background: var(--ink); color: #fff; border-radius: 8px;
|
||||
padding: 7px 14px; width: fit-content; margin: 0 auto 12px; font-size: 0.86rem;
|
||||
}
|
||||
.keyboard { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
|
||||
.keyboard { display: flex; flex-direction: column; gap: 7px; margin-top: 8px; }
|
||||
.krow { display: flex; gap: 5px; justify-content: center; }
|
||||
.key {
|
||||
flex: 1; min-width: 0; max-width: 42px; height: 52px; border: none; border-radius: 7px;
|
||||
background: #e4e7ea; color: var(--ink); font-family: var(--label); font-weight: 600;
|
||||
font-size: 0.92rem; cursor: pointer; text-transform: uppercase;
|
||||
flex: 1; min-width: 0; max-width: 44px; height: 54px; border: none; border-radius: 8px;
|
||||
background: #e9e2d4; color: var(--ink); font-family: var(--label); font-weight: 600;
|
||||
font-size: 1rem; cursor: pointer; text-transform: uppercase;
|
||||
box-shadow: 0 1.5px 0 rgba(70, 58, 36, 0.16); transition: background 0.12s ease, transform 0.05s ease;
|
||||
}
|
||||
.key.wide { max-width: 62px; font-size: 0.7rem; }
|
||||
.key.correct { background: #4a9d6e; color: #fff; }
|
||||
.key.present { background: #d8b24a; color: #fff; }
|
||||
.key.absent { background: #9aa6b2; color: #fff; }
|
||||
.key:hover { filter: brightness(0.96); }
|
||||
.key:active { transform: translateY(1px); box-shadow: none; }
|
||||
.key.wide { max-width: 66px; font-size: 0.68rem; letter-spacing: 0.03em; }
|
||||
.key.enter { background: var(--accent-soft); color: var(--accent-deep); }
|
||||
.key.correct { background: #4a9d6e; color: #fff; box-shadow: 0 1.5px 0 rgba(40, 90, 60, 0.32); }
|
||||
.key.present { background: #d8b24a; color: #fff; box-shadow: 0 1.5px 0 rgba(150, 110, 20, 0.32); }
|
||||
.key.absent { background: #9aa6b2; color: #fff; box-shadow: 0 1.5px 0 rgba(80, 90, 100, 0.32); }
|
||||
.key:hover { filter: brightness(0.97); }
|
||||
.muted { color: var(--muted); text-align: center; }
|
||||
|
||||
/* Mobile: fill the height — board scrolls in the middle, keyboard pinned at the
|
||||
bottom and always reachable (full-bleed, on-brand, safe-area aware). */
|
||||
@media (max-width: 720px) {
|
||||
.wordgame { display: flex; flex-direction: column; height: 100%; max-width: 100%; }
|
||||
.play-area { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column;
|
||||
justify-content: center; padding: 4px 0; }
|
||||
.board { margin-bottom: 12px; }
|
||||
.keyboard { flex-shrink: 0; margin: 6px -16px 0; gap: 6px; background: var(--bg);
|
||||
border-top: 1px solid var(--line); padding: 9px 6px calc(9px + env(safe-area-inset-bottom)); }
|
||||
.key { height: 50px; }
|
||||
}
|
||||
.result { text-align: center; }
|
||||
.rmark { font-family: var(--serif); font-style: italic; color: var(--accent-deep); font-size: 1.2rem; margin: 0 0 10px; }
|
||||
.rmark strong { font-style: normal; letter-spacing: 0.06em; }
|
||||
|
||||
Reference in New Issue
Block a user