Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/components/DrainRing.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="relative shrink-0" :style="{ width: `${size}px`, height: `${size}px` }">
<div
class="drain-ring absolute inset-0 rounded-full transition-[background] duration-100 ease-linear"
class="drain-ring absolute inset-0 rounded-full"
:style="{
'--p': percent,
'--drain-color': color,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/HostAvatarPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async function save() {
border: 1px solid rgb(var(--haze));
border-radius: 999px;
color: rgb(var(--paper) / 0.7);
transition: 0.15s ease;
transition: color 150ms ease, border-color 150ms ease;
}

.arrow:hover {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/ImageField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
>
<img :src="modelValue" alt="" class="max-h-full max-w-full object-contain" />
<div
class="absolute right-2 top-2 flex gap-1.5 opacity-0 transition focus-within:opacity-100 group-hover:opacity-100"
class="absolute right-2 top-2 flex gap-1.5 opacity-0 transition focus-within:opacity-100 group-hover:opacity-100 [@media(hover:none)]:opacity-100"
>
<button
class="image-action"
Expand Down Expand Up @@ -60,6 +60,6 @@ const emit = defineEmits(["update:modelValue"]);
border-radius: 999px;
background: rgb(var(--night) / 0.85);
color: rgb(var(--paper));
transition: 0.15s ease;
transition: color 150ms ease;
}
</style>
4 changes: 2 additions & 2 deletions frontend/src/components/QuestionRail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
</span>
</button>
<div
class="absolute -top-1 right-0 flex gap-0.5 opacity-0 transition focus-within:opacity-100 group-hover:opacity-100"
class="absolute -top-1 right-0 flex gap-0.5 opacity-0 transition focus-within:opacity-100 group-hover:opacity-100 [@media(hover:none)]:opacity-100"
>
<button
class="rail-action"
Expand Down Expand Up @@ -140,7 +140,7 @@ function moveBy(index, step) {
height: 1.5rem;
border-radius: 999px;
color: rgb(var(--paper) / 0.5);
transition: 0.15s ease;
transition: color 150ms ease;
}

.rail-action:hover {
Expand Down
102 changes: 87 additions & 15 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import 'frappe-ui/style.css';
@import "frappe-ui/style.css";

/* RGB channels so Tailwind's opacity modifiers still work. */
:root {
Expand All @@ -13,10 +13,12 @@
--on-alert: 10 16 14;
--accent: 32 238 160;
--ok: 23 176 190;
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Accents are re-derived, not inverted: each must read as small text on a light ground. */
:root[data-theme='light'] {
:root[data-theme="light"] {
color-scheme: light;
--night: 246 251 248;
--dusk: 232 244 238;
Expand All @@ -29,7 +31,7 @@
}

@media (prefers-color-scheme: light) {
:root:not([data-theme='dark']) {
:root:not([data-theme="dark"]) {
color-scheme: light;
--night: 246 251 248;
--dusk: 232 244 238;
Expand All @@ -46,7 +48,7 @@ html,
body {
background-color: rgb(var(--night));
color: rgb(var(--paper));
font-family: 'Instrument Sans', system-ui, sans-serif;
font-family: "Instrument Sans", system-ui, sans-serif;
}

/* Everything sits on the same ground, so one mint ring works everywhere. */
Expand Down Expand Up @@ -74,7 +76,7 @@ body {
);
}

.drain-ring[data-urgent='true'] {
.drain-ring[data-urgent="true"] {
animation: drain-pulse 1s ease-in-out infinite;
}

Expand All @@ -95,22 +97,31 @@ body {
font-size: 0.875rem;
line-height: 1.25rem;
color: rgb(var(--paper) / 0.7);
transition: 0.15s ease;
transition:
color 150ms ease,
border-color 150ms ease,
background-color 150ms ease,
filter 150ms ease,
transform 120ms var(--ease-out);
}

.ctl:hover:not(:disabled, [data-on='true']) {
.ctl:active:not(:disabled) {
transform: scale(0.97);
}

.ctl:hover:not(:disabled, [data-on="true"]) {
border-color: rgb(var(--paper));
color: rgb(var(--paper));
}

.ctl[data-on='true'] {
.ctl[data-on="true"] {
background: rgb(var(--paper));
border-color: rgb(var(--paper));
color: rgb(var(--night));
font-weight: 600;
}

.ctl[data-on='true']:hover:not(:disabled) {
.ctl[data-on="true"]:hover:not(:disabled) {
filter: brightness(0.92);
}

Expand Down Expand Up @@ -155,7 +166,7 @@ body {
}

.qz-dialog[open] {
animation: dialog-in 0.14s ease-out;
animation: dialog-in 200ms var(--ease-out);
}

@keyframes dialog-in {
Expand Down Expand Up @@ -184,12 +195,12 @@ body {
}

/* the native spinner arrows render light-themed and crowd a short number box */
.field[type='number'] {
.field[type="number"] {
appearance: textfield;
}

.field[type='number']::-webkit-inner-spin-button,
.field[type='number']::-webkit-outer-spin-button {
.field[type="number"]::-webkit-inner-spin-button,
.field[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
Expand All @@ -201,15 +212,76 @@ body {
}

.podium-rise {
animation: podium-rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
animation: podium-rise 600ms var(--ease-out) both;
}

/* clip, not height: no layout per frame, and the rank number is not squashed */
@keyframes podium-rise {
from {
height: 0;
clip-path: inset(100% 0 0 0);
}
}

.bar-grow {
transform-origin: bottom;
animation: bar-grow 400ms var(--ease-out) both;
}

@keyframes bar-grow {
from {
transform: scaleY(0);
}
}

.pop-in {
animation: pop-in 300ms var(--ease-pop) both;
}

@keyframes pop-in {
from {
opacity: 0;
transform: scale(0.9);
}
}

.phase-enter-active {
transition:
opacity 220ms var(--ease-out),
transform 220ms var(--ease-out);
}

.phase-leave-active {
transition: opacity 120ms ease;
}

.phase-enter-from {
opacity: 0;
transform: translateY(8px);
}

.phase-leave-to {
opacity: 0;
}

.chip-enter-active {
transition:
opacity 250ms var(--ease-out),
transform 250ms var(--ease-out);
}

.chip-leave-active {
transition: opacity 150ms ease;
}

.chip-enter-from {
opacity: 0;
transform: scale(0.95);
}

.chip-leave-to {
opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
*,
*::before,
Expand Down
49 changes: 28 additions & 21 deletions frontend/src/pages/Host.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<button class="group" @click="qrFullscreen = true">
<AnimatedQr
:url="joinUrl"
class="size-40 rounded-2xl bg-card p-2 ring-1 ring-haze transition group-hover:scale-105 sm:size-48"
class="size-40 rounded-2xl bg-card p-2 ring-1 ring-haze transition group-hover:scale-105 group-active:scale-[0.97] sm:size-48"
/>
<span
class="mt-2 block font-mono text-[11px] uppercase tracking-wider text-paper/35 transition group-hover:text-paper/70"
Expand All @@ -91,25 +91,27 @@
class="flex w-full max-w-5xl flex-wrap items-center justify-center gap-2.5 p-1"
>
<!-- not the whole chip: a full-name-sized kick button is too easy to hit by accident -->
<div
v-for="participant in visibleParticipants"
:key="participant.name"
class="group relative flex items-center gap-2 rounded-full border border-haze bg-dusk py-1 pl-1 pr-4 text-base font-medium text-paper sm:gap-3 sm:pr-5 sm:text-xl"
>
<AvatarPic
:id="participant.avatar"
:nickname="participant.nickname"
:size="40"
/>
<span>{{ participant.nickname }}</span>
<button
class="absolute -right-1 -top-1 grid size-6 place-items-center rounded-full bg-haze text-sm leading-none text-paper opacity-0 transition hover:bg-ember hover:text-sunk focus-visible:opacity-100 group-hover:opacity-100"
:aria-label="`Remove ${participant.nickname}`"
@click="kick(participant)"
<TransitionGroup name="chip">
<div
v-for="participant in visibleParticipants"
:key="participant.name"
class="group relative flex items-center gap-2 rounded-full border border-haze bg-dusk py-1 pl-1 pr-4 text-base font-medium text-paper sm:gap-3 sm:pr-5 sm:text-xl"
>
×
</button>
</div>
<AvatarPic
:id="participant.avatar"
:nickname="participant.nickname"
:size="40"
/>
<span>{{ participant.nickname }}</span>
<button
class="absolute -right-1 -top-1 grid size-6 place-items-center rounded-full bg-haze text-sm leading-none text-paper opacity-0 transition hover:bg-ember hover:text-sunk focus-visible:opacity-100 group-hover:opacity-100 [@media(hover:none)]:opacity-100"
:aria-label="`Remove ${participant.nickname}`"
@click="kick(participant)"
>
×
</button>
</div>
</TransitionGroup>
<div
v-if="overflowCount"
class="flex items-center rounded-full border border-haze bg-dusk px-4 py-2.5 text-base font-medium text-paper/50 sm:px-5 sm:text-xl"
Expand Down Expand Up @@ -191,7 +193,10 @@
<div
class="podium-rise flex w-full items-start justify-center rounded-t-2xl pt-3 font-mono text-2xl font-bold text-sunk sm:text-3xl"
:class="PODIUM_FILL[entry.rank]"
:style="{ height: `${180 - (entry.rank - 1) * 45}px` }"
:style="{
height: `${180 - (entry.rank - 1) * 45}px`,
animationDelay: `${PODIUM_DELAY_MS[entry.rank]}ms`,
}"
>
{{ entry.rank }}
</div>
Expand Down Expand Up @@ -427,7 +432,7 @@
</span>
<div class="flex flex-1 flex-col justify-end rounded-t-lg bg-dusk">
<div
class="rounded-t-lg transition-[height] duration-500"
class="bar-grow rounded-t-lg"
:class="shape.fill"
:style="{ height: `${barHeight(shape.id)}%` }"
/>
Expand Down Expand Up @@ -479,6 +484,8 @@ import { initSound, muted, playCue, toggleMute } from "@/sound";
import AnimatedQr from "@/components/AnimatedQr.vue";

const PODIUM_FILL = { 1: "bg-gold", 2: "bg-lagoon", 3: "bg-orchid" };
// third rises first so the winner lands last
const PODIUM_DELAY_MS = { 1: 600, 2: 250, 3: 0 };
// remembered so a reload on the podium restores it: get_host_state only auto-finds live sessions
const HOSTED_SESSION_KEY = "tt_hosted_session";
// long enough to read the old order before it moves, and to watch the points climb
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Join.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@

<button
type="submit"
class="rounded-2xl bg-brand py-4 font-display text-xl font-extrabold text-sunk transition hover:brightness-110 disabled:opacity-50"
class="rounded-2xl bg-brand py-4 font-display text-xl font-extrabold text-sunk transition hover:brightness-110 active:scale-[0.97] disabled:opacity-50 disabled:active:scale-100"
:disabled="joining || !ready"
>
{{ joining ? "Joining…" : "Join game" }}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@

<button
type="submit"
class="mt-1 rounded-2xl bg-brand py-4 font-display text-xl font-extrabold text-sunk transition hover:brightness-110 disabled:opacity-50"
class="mt-1 rounded-2xl bg-brand py-4 font-display text-xl font-extrabold text-sunk transition hover:brightness-110 active:scale-[0.97] disabled:opacity-50 disabled:active:scale-100"
:disabled="busy"
>
{{
Expand Down
Loading
Loading