Phone Aero glass + a timed run you can see
The phone (touch) chat chrome carried a 55% flat white veil while every other Aero surface uses the thin 32% Ask-pill tint, so the header, welcome card and composer read as plain white on a bright sky. They now use the same flat glass as the cards, the answer/user bubbles keep 42% for long reading, and the composer's field is a lighter 42% tile. The shared Aero wash block moved above the touch media query so its background-image can no longer resurrect the bake on phones. The timed run worked but was invisible: the only cue was a small chip among the streak/XP counters. The chrome pill now draws the countdown along its lower edge, the chip is bordered (a control, not a stat), turns rose with the bar in the last ten seconds, and fires one soft haptic tick at ten seconds. Contrast scan clean on the phone chat in all three themes; activities suite pins the countdown bar (25 checks). Version 0.6.2, SW v84.
This commit is contained in:
parent
796e2e9fe9
commit
a8b3dfd0e3
6 changed files with 129 additions and 75 deletions
|
|
@ -175,6 +175,9 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
/* Timed runs ---------------------------------------------------------- */
|
||||
const timedActivity = index < total ? queue[index] : null;
|
||||
const timedAnswered = timedActivity ? Boolean(results[timedActivity.id]) : true;
|
||||
/** Seconds left on the card in view, or null when no countdown runs. */
|
||||
const timedRemaining = timed && remaining !== null && timedActivity && !timedAnswered ? remaining : null;
|
||||
const timedDanger = timedRemaining !== null && timedRemaining <= 10;
|
||||
|
||||
useEffect(() => {
|
||||
if (!timed || !timedActivity || timedAnswered) {
|
||||
|
|
@ -188,6 +191,15 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
return () => window.clearInterval(timer);
|
||||
}, [timed, timedActivity, timedAnswered, runId]);
|
||||
|
||||
/* Last ten seconds: one soft tick per card, like a phone timer. */
|
||||
const warnedRef = useRef<string | null>(null);
|
||||
useEffect(() => {
|
||||
if (!timed || timedRemaining === null || timedRemaining > 10 || !timedActivity) return;
|
||||
if (warnedRef.current === timedActivity.id) return;
|
||||
warnedRef.current = timedActivity.id;
|
||||
haptic("toggle");
|
||||
}, [timed, timedRemaining, timedActivity]);
|
||||
|
||||
/* Ran out: the card counts as a miss (the same path as a wrong answer, so
|
||||
history and the SRS schedule see it), the learner is told, and the feed
|
||||
moves on. */
|
||||
|
|
@ -290,7 +302,24 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
the streak/xp chips at the other end. Everything sits on the pill's
|
||||
centre line with even padding, so nothing crowds the rounded ends. */}
|
||||
<header className="pt-safe absolute inset-x-0 top-0 z-30 px-3">
|
||||
<div className="study-chrome mx-auto flex max-w-xl items-center gap-2.5 rounded-full px-2.5 py-2">
|
||||
<div className="study-chrome relative mx-auto flex max-w-xl items-center gap-2.5 rounded-full px-2.5 py-2">
|
||||
{/* The countdown, drawn along the pill's lower edge: the chip alone
|
||||
was easy to miss mid-card. */}
|
||||
{timedRemaining !== null ? (
|
||||
<span
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-x-4 bottom-[0.3rem] h-[3px] overflow-hidden rounded-full bg-[color-mix(in_oklab,var(--text)_14%,transparent)]"
|
||||
>
|
||||
<span
|
||||
data-timed-bar
|
||||
className={cn(
|
||||
"block h-full rounded-full transition-[width] duration-1000 ease-linear motion-reduce:transition-none",
|
||||
timedDanger ? "bg-rose-500" : "grad-ice",
|
||||
)}
|
||||
style={{ width: `${Math.round((timedRemaining / TIMED_CARD_SECONDS) * 100)}%` }}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
<Link
|
||||
href="/"
|
||||
aria-label={t("feed.back")}
|
||||
|
|
@ -328,12 +357,14 @@ export function StudyFeed({ deck }: { deck: Deck }) {
|
|||
aria-label={timed ? t("feed.timedOn") : t("feed.timed")}
|
||||
title={timed ? t("feed.timedOn") : t("feed.timed")}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-extrabold tabular-nums transition",
|
||||
timed ? "tint-peach" : "bg-[var(--surface-sunken)] text-[var(--text-muted)] hover:text-[var(--text)]",
|
||||
"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-xs font-extrabold tabular-nums transition",
|
||||
timed
|
||||
? cn("border-transparent tint-peach", timedDanger && "tint-rose")
|
||||
: "border-[var(--glass-border)] bg-[var(--surface-strong)] text-[var(--text-muted)] hover:text-[var(--text)]",
|
||||
)}
|
||||
>
|
||||
<ClockIcon className="size-3.5" />
|
||||
{timed && remaining !== null ? `${remaining}s` : `${TIMED_CARD_SECONDS}s`}
|
||||
{timedRemaining !== null ? `${timedRemaining}s` : `${TIMED_CARD_SECONDS}s`}
|
||||
</button>
|
||||
<span
|
||||
className={cn(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue