srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/styles.css
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-12-04 20:58:00 +0200
committersrdusr <[email protected]>2025-12-04 20:58:00 +0200
commitf48fa830b10d4122d145e9822522d9b71e3f00fa (patch)
tree3ab1696b2fae87c49c056bbd56ad1975c3b7ca5d /web/src/styles.css
parent3d26cb457b2023d686136f1c9ee014c210951729 (diff)
downloadtyperpunk-f48fa830b10d4122d145e9822522d9b71e3f00fa.tar.gz
typerpunk-f48fa830b10d4122d145e9822522d9b71e3f00fa.zip
Fix the multiplayer race hand-off, fill the field, and polish controls
Standings on the end screen - A race used to end on your own numbers alone. The end screen now shows the whole field, filling in live as the slower racers come home. - Two teardown paths were killing the feed it depends on. app.js tears the race screen down before rendering the end screen, and that ran both the inner cleanup (unsubscribing progress and finished) and the outer one (closing the socket). Your own PlayerFinished arrives from the server a moment after your finish callback runs, so nobody - including you - ever got a place. The screen now marks the feed as handed off and leaves it to the end screen, which drops it on its own teardown. More racers - Up to four bots per room instead of two, biased towards a fuller lobby. With four, picking one of two speed tiers put pairs on top of each other, so a bot now takes a pace at least 9 wpm clear of every other bot in the room, drawn from the whole slow-to-fast span. Accuracy tracks speed rather than being rolled independently, which had been pairing 80 wpm with 88% and 35 wpm with 99%. Lobby - The countdown is the whole screen for its three seconds, instead of a small number under a large disabled button. Quick match says "Finding a race" rather than offering a room code to share that nobody needs. Controls - Keyboard focus was invisible almost everywhere: only two inputs and the theme toggle had a :focus rule, and four other controls set outline:none outright. A :focus-visible ring now covers every interactive control, drawn as a box-shadow so it survives those declarations. - Disabled buttons no longer keep the filled hover state of a live primary action, placeholders have a defined colour rather than a per-engine default, inputs gained hover feedback and a consistent width, and pressed controls acknowledge the press.
Diffstat (limited to 'web/src/styles.css')
-rw-r--r--web/src/styles.css153
1 files changed, 151 insertions, 2 deletions
diff --git a/web/src/styles.css b/web/src/styles.css
index 4131f64..8462fbf 100644
--- a/web/src/styles.css
+++ b/web/src/styles.css
@@ -1780,10 +1780,13 @@ body {
screen, so it carried no urgency. --warning-color is used here and on the
device badge, nowhere else. */
.mp-countdown {
- font-size: 3rem;
+ font-size: 6rem;
+ line-height: 1;
+ font-weight: 700;
color: var(--warning-color);
text-align: center;
- min-height: 4rem;
+ margin: 0.5rem 0 1rem;
+ font-family: 'JetBrains Mono', monospace;
}
.stat-label {
@@ -2468,3 +2471,149 @@ body::-webkit-scrollbar, .app::-webkit-scrollbar, #root::-webkit-scrollbar {
color: var(--primary-color);
margin-bottom: 0.3rem;
}
+
+
+/* A disabled control must not still look like the live primary action. The
+ lobby's "Waiting for players..." button kept the filled hover state and
+ read as the thing to press. */
+.menu-button:disabled,
+.menu-button[disabled] {
+ opacity: 0.55;
+ cursor: default;
+}
+
+.menu-button:disabled:hover,
+.menu-button[disabled]:hover {
+ background: none;
+ color: var(--sub-color);
+}
+
+/* Where everyone placed, on the end screen after a race. A multiplayer result
+ used to end on your own numbers alone, with no way to see who beat you or
+ by how much. */
+.mp-standings {
+ width: 100%;
+ max-width: 460px;
+ margin: 1.75rem auto 0 auto;
+ display: flex;
+ flex-direction: column;
+ gap: 0.3rem;
+}
+
+.mp-standings-heading {
+ font-size: 0.7rem;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ color: var(--sub-color);
+ text-align: center;
+ margin-bottom: 0.35rem;
+}
+
+.mp-standings-row {
+ display: grid;
+ grid-template-columns: 1.75rem 1fr auto auto;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.4rem 0.75rem;
+ border: 1px solid transparent;
+ border-radius: 6px;
+ font-size: 0.85rem;
+ color: var(--sub-color);
+}
+
+/* Your own row is the one being looked for. */
+.mp-standings-row.me {
+ border-color: color-mix(in srgb, var(--primary-color) 45%, transparent);
+ color: var(--text-color);
+}
+
+.mp-standings-place {
+ font-family: 'JetBrains Mono', monospace;
+ color: var(--primary-color);
+ text-align: center;
+}
+
+.mp-standings-name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.mp-standings-wpm,
+.mp-standings-acc {
+ font-family: 'JetBrains Mono', monospace;
+ white-space: nowrap;
+}
+
+/* Still typing: no place yet, so the row shows how far along they are. */
+.mp-standings-row.racing {
+ opacity: 0.65;
+}
+
+.mp-standings-place {
+ color: var(--racer-color, var(--primary-color));
+}
+
+/* ---- Interactive control polish -------------------------------------
+ Focus was invisible almost everywhere: only the two text inputs and the
+ theme toggle had a :focus rule, while .corner-icon-button, .theme-toggle,
+ .restart-toggle and .zen-finish-button all set `outline: none` outright.
+ Tabbing through the app showed nothing at all.
+
+ A box-shadow ring rather than an outline, so it works regardless of those
+ existing `outline: none` declarations, and :focus-visible rather than
+ :focus so it appears for keyboard users without ringing every mouse
+ click. */
+.menu-button:focus-visible,
+.corner-icon-button:focus-visible,
+.theme-toggle:focus-visible,
+.restart-toggle:focus-visible,
+.zen-finish-button:focus-visible,
+.top-rail-link:focus-visible,
+.mode-popover-item:focus-visible,
+.lang-item:focus-visible,
+.theme-swatch:focus-visible,
+.end-screen-button:focus-visible,
+.account-input:focus-visible,
+.custom-textarea:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 2px var(--background-color), 0 0 0 4px var(--primary-color);
+ border-radius: 6px;
+}
+
+/* Placeholders were inheriting the input's own text colour at whatever
+ opacity the browser chose, which differed between engines. */
+.account-input::placeholder,
+.custom-textarea::placeholder {
+ color: var(--sub-color);
+ opacity: 1;
+}
+
+.account-input:hover:not(:focus),
+.custom-textarea:hover:not(:focus) {
+ border-color: color-mix(in srgb, var(--primary-color) 45%, var(--neutral-color));
+}
+
+/* Inputs sat at whatever width their container gave them, which left the
+ multiplayer name field and its Join row on different widths. */
+.account-input {
+ width: 100%;
+ box-sizing: border-box;
+ transition: border-color 0.15s ease;
+}
+
+/* A pressed control should acknowledge the press. Nothing in the app moved
+ on :active, so a click on a button that opens a panel elsewhere on screen
+ gave no feedback at the point of contact. */
+.menu-button:active:not(:disabled),
+.corner-icon-button:active,
+.end-screen-button:active {
+ transform: translateY(1px);
+}
+
+/* Popover rows are buttons but read as list items; they need the same hover
+ affordance the chips and menu buttons have. */
+.mode-popover-item,
+.lang-item {
+ transition: background-color 0.12s ease, color 0.12s ease;
+}