feat(web): responsive design

- fix mobile accessibility
This commit is contained in:
2026-08-22 22:14:54 +02:00
parent c8a764e82f
commit ab79575942
2 changed files with 118 additions and 85 deletions
+115 -83
View File
@@ -15,7 +15,7 @@
} from "./solver/types.ts"; } from "./solver/types.ts";
import SolverOutput from "./components/SolverOutput.svelte"; import SolverOutput from "./components/SolverOutput.svelte";
import { FolderGit2 } from "@lucide/svelte"; import { FolderGit2 } from "@lucide/svelte";
import { tick } from "svelte"; import { tick, onMount } from "svelte";
let guesses = $state([ let guesses = $state([
{ {
@@ -45,6 +45,19 @@
let w_lines: WordleLine[] = $state([]); let w_lines: WordleLine[] = $state([]);
let cursor = $state(0); let cursor = $state(0);
let is_desktop = $state(false);
onMount(() => {
const update = () => {
is_desktop = window.innerWidth >= 768;
};
update();
window.addEventListener("resize", update);
return () => window.removeEventListener("resize", update);
});
async function toggleSolver() { async function toggleSolver() {
if (solving) { if (solving) {
await stopSolver(); await stopSolver();
@@ -136,92 +149,111 @@
<svelte:window onkeydown={handleKey} /> <svelte:window onkeydown={handleKey} />
<div <div
class="min-h-screen w-full max-w-full class="h-dvh w-full max-w-full
bg-zinc-950 text-zinc-100 bg-zinc-950 text-zinc-100
flex flex-wrap "
items-center justify-center p-10 gap-2
overflow-x-hidden
select-none
"
> >
<a <div
class="fixed top-4 right-4" class="h-full overflow-y-auto
href="https://git.jroeger.de/jonas/wordle-solver-rs" flex flex-wrap md:flex-row
target="_blank" items-center justify-center p-2 md:p-10 gap-4
overflow-hidden select-none"
> >
<FolderGit2 /> <a
</a> class="fixed top-4 right-4"
<div class="text-center"> href="https://git.jroeger.de/jonas/wordle-solver-rs"
<h1 class="mb-4 text-3xl font-bold">Wordle Solver</h1> target="_blank"
<p class="text-sm text-zinc-500">
Use Wordle-Answers:
<input
type="checkbox"
bind:checked={use_default_wordle_answers}
onfocus={(e) => {
if (e.currentTarget) {
e.currentTarget.blur();
}
}}
onchange={reset}
/>
</p>
<p class="mt-0 text-sm text-zinc-500">
{#await solution_space}
loading
{:then s}
Solution-space: {s.size}, Uncertainty: {s.uncertainty.toFixed(
2,
)}bits
{:catch e}
{e}
{/await}
</p>
<div class="ml-16 mt-2 flex flex-col gap-2">
{#each guesses as g, i (g)}
<WordleLine
active={i === guesses.length - 1}
on_guess_pressed={i === guesses.length - 1 ? doGuess : null}
bind:this={w_lines[i]}
bind:cursor
bind:letters={guesses[i].letters}
bind:colors={guesses[i].colors}
info_gained={infos_gained[i]}
/>
{/each}
</div>
<p class="mt-4 text-sm text-zinc-500 mx-auto w-3/4">
Type letters · ← → navigate · Space cycle color · Backspace delete ·
Enter apply · Shift+Enter solve · Escape abort solver · Ctrl+Esc
reset
</p>
<button
tabindex="-1"
class="mt-2 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
onclick={(e) => {
e.currentTarget.blur();
toggleSolver();
}}
> >
{#if solving} <FolderGit2 />
Cancel </a>
{:else} <div class="text-center">
Solve <h1 class="mb-4 text-3xl font-bold">Wordle Solver</h1>
<p class="text-sm text-zinc-500">
Use Wordle-Answers:
<input
type="checkbox"
bind:checked={use_default_wordle_answers}
onfocus={(e) => {
if (e.currentTarget) {
e.currentTarget.blur();
}
}}
onchange={reset}
/>
</p>
<p class="mt-0 text-sm text-zinc-500">
{#await solution_space}
loading
{:then s}
Solution-space: {s.size}, Uncertainty: {s.uncertainty.toFixed(
2,
)}bits
{:catch e}
{e}
{/await}
</p>
<div class="mt-2 flex flex-col gap-2">
{#each guesses as g, i (g)}
<WordleLine
active={i === guesses.length - 1}
on_guess_pressed={i === guesses.length - 1
? doGuess
: null}
bind:this={w_lines[i]}
bind:cursor
bind:letters={guesses[i].letters}
bind:colors={guesses[i].colors}
info_gained={infos_gained[i]}
/>
{/each}
</div>
{#if is_desktop}
<p class="mt-4 text-sm text-zinc-500 mx-auto w-3/4">
Type letters · ← → navigate · Space cycle color · Backspace
delete · Enter apply · Shift+Enter solve · Escape abort
solver · Ctrl+Esc reset
</p>
{/if} {/if}
</button> <button
tabindex="-1"
class="mt-4 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
onclick={(e) => {
e.currentTarget.blur();
toggleSolver();
}}
>
{#if solving}
Cancel
{:else}
Solve
{/if}
</button>
</div>
{#if best_words}
<SolverOutput
data={best_words}
progress={best_words_progress}
insert_guess={(g) => {
guesses[guesses.length - 1].letters = g;
}}
bind:solving
/>
{/if}
</div> </div>
{#if best_words}
<SolverOutput
data={best_words}
progress={best_words_progress}
insert_guess={(g) => {
guesses[guesses.length - 1].letters = g;
}}
bind:solving
/>
{/if}
</div> </div>
<style>
:global(body) {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
overscroll-behavior: none;
background: #09090b;
}
</style>
+3 -2
View File
@@ -38,6 +38,7 @@
if (event.key === "Backspace") { if (event.key === "Backspace") {
if (letters[cursor] === "" && cursor > 0) { if (letters[cursor] === "" && cursor > 0) {
cursor--; cursor--;
inputs[cursor].focus();
} }
if (active) { if (active) {
@@ -69,7 +70,7 @@
disabled={!active} disabled={!active}
tabindex="-1" tabindex="-1"
class=" class="
flex h-16 w-16 items-center justify-center flex aspect-square max-w-16 w-1/6 items-center justify-center
rounded-lg border-2 rounded-lg border-2
text-3xl font-bold text-3xl font-bold
transition-all text-center caret-transparent transition-all text-center caret-transparent
@@ -111,7 +112,7 @@
/> />
{/each} {/each}
<span <span
class="w-16 text-sm text-zinc-500 self-center flex items-center justify-center" class="max-w-14 w-1/6 text-sm text-zinc-500 self-center flex items-center justify-center"
onclick={info_gained === null ? on_guess_pressed : null} onclick={info_gained === null ? on_guess_pressed : null}
> >
{#if info_gained === null} {#if info_gained === null}