Serve practice mode from the main server, add mode switch, fix touch
Practice mode is now at /practice on server.py instead of a separate server, with a button in the bottom-right corner of each page to the other mode. Touch: no double-tap zoom, no sticky hover after a tap, no long-press selection on the board, and a pressed look on tapped squares. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c52f30dc59
commit
ce6b71eb35
7 files changed
+51
-42
No files matched your search
+25
-4
@@ -42,7 +42,7 @@ body {
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
padding: 48px 16px;
|
||||
padding: 48px 16px 96px; /* room at the bottom to scroll clear of the mode switch */
|
||||
}
|
||||
|
||||
h1 { font-size: 2.2rem; margin: 0 0 8px; letter-spacing: -0.02em; }
|
||||
@@ -57,10 +57,15 @@ button {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
touch-action: manipulation; /* no double-tap zoom or tap delay on touch screens */
|
||||
}
|
||||
button:hover { border-color: var(--muted); }
|
||||
button.primary { background: var(--text); color: var(--bg); border-color: var(--text); width: 100%; }
|
||||
button.primary:hover { opacity: 0.9; }
|
||||
/* Hover styles only for real pointers: on touch screens they stick after a tap */
|
||||
@media (hover: hover) {
|
||||
button:hover { border-color: var(--muted); }
|
||||
button.primary:hover { opacity: 0.9; }
|
||||
.cell.playable:hover { border-color: var(--muted); }
|
||||
}
|
||||
button.small { padding: 6px 12px; font-size: 0.875rem; }
|
||||
button.ghost { background: transparent; }
|
||||
|
||||
@@ -141,6 +146,8 @@ button.player[aria-pressed="true"] .who { font-weight: 600; }
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
grid-template-rows: repeat(3, minmax(0, 1fr));
|
||||
aspect-ratio: 1; width: min(100%, 60vh); margin: 0 auto;
|
||||
touch-action: manipulation;
|
||||
user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; /* no long-press selection or menu */
|
||||
}
|
||||
.cell {
|
||||
position: relative;
|
||||
@@ -148,9 +155,10 @@ button.player[aria-pressed="true"] .who { font-weight: 600; }
|
||||
border: 1px solid var(--border); background: var(--surface);
|
||||
display: grid; place-items: center;
|
||||
cursor: default;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.cell.playable { cursor: pointer; }
|
||||
.cell.playable:hover { border-color: var(--muted); }
|
||||
.cell.playable:active { border-color: var(--muted); background: color-mix(in srgb, var(--border) 40%, var(--surface)); }
|
||||
.cell.win { background: color-mix(in srgb, var(--win) 30%, var(--surface)); border-color: var(--win); }
|
||||
.cell svg { position: absolute; inset: 21%; width: 58%; height: 58%; overflow: visible; }
|
||||
.cell svg * { fill: none; stroke-width: 12; stroke-linecap: round; }
|
||||
@@ -178,4 +186,17 @@ button.player[aria-pressed="true"] .who { font-weight: 600; }
|
||||
.actions > button { flex: 1; }
|
||||
.actions > button.primary { flex: 2; }
|
||||
.actions > button:disabled { opacity: 0.45; cursor: default; border-color: var(--border); }
|
||||
/* Fixed bottom-right button to the other mode (multiplayer <-> practice) */
|
||||
.mode-switch {
|
||||
position: fixed;
|
||||
right: calc(16px + env(safe-area-inset-right, 0px));
|
||||
bottom: calc(16px + env(safe-area-inset-bottom, 0px));
|
||||
padding: 8px 16px;
|
||||
border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
|
||||
box-shadow: 0 2px 10px rgb(0 0 0 / 0.12);
|
||||
color: var(--text); font-size: 0.875rem; font-weight: 600; text-decoration: none;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
@media (hover: hover) { .mode-switch:hover { border-color: var(--muted); } }
|
||||
|
||||
.meta { color: var(--muted); font-size: 0.875rem; text-align: center; margin: 12px 0 0; min-height: 1.5em; }
|
||||
Reference in new issue
Block a user