fix: a block bar you can find, and a rail that says which session this is

Exit, Pause and End Block are the loudest decisions on the screen and were
three grey pills the same size as everything else. Each is an icon and a word
now, bold enough to reach for without reading, and on the exam's navy they are
white with End Block in red.

The rail said "Session questions" on every session ever sat. It says which one
this is instead — the mode, the title, how far through — so a repetition, which
is titled as one when it is built, is visibly practice rather than a score that
counts. The Review badge moved there with it; two of them on one screen saying
the same word about the same block was one too many.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
This commit is contained in:
Daniel 2026-09-12 08:10:36 +02:00
parent b06560c515
commit 260e04e77b
2 changed files with 64 additions and 16 deletions

View file

@ -1629,10 +1629,9 @@ const timerStarted = timeLeft !== null
}}>
{isStudy ? '📖 Study' : '🎯 Exam'}
</span>
{/* Said out loud, because the difference between a block being
sat and a block being read back is the difference between a
score that counts and one that has already been counted. */}
{reviewing && <span className="quiz-review-badge">Review</span>}
{/* The Review badge lives on the rail now, beside the name of
the session it belongs to. Two of them on one screen, saying
the same word about the same block, was one too many. */}
<span>Q {currentIdx + 1} / {totalCount}</span>
<span style={{ color: 'var(--text-subtle)' }}>{answeredCount} answered</span>
</div>
@ -2212,7 +2211,15 @@ const timerStarted = timeLeft !== null
{/* Desktop rail — numbers with an excerpt, as in a Qbank session */}
<div className="quiz-sidebar quiz-rail">
<div className="quiz-rail-head">
<span>Session questions</span>
<span className="quiz-rail-name">
{reviewing && <b className="quiz-rail-badge">Review</b>}
{/* Which session this is, in full. A repetition is titled
"… (repetition)" when it is built, and that is worth seeing:
it is the difference between a score that counts towards
what you know and one that is practice. */}
<strong>{isStudy ? 'Study mode' : 'Exam mode'}: {quiz.title}</strong>
<small>{answeredCount} / {totalCount}</small>
</span>
<button type="button" className="quiz-rail-hide" aria-label="Hide the session questions"
onClick={() => setRailOpen(false)}></button>
</div>
@ -2238,8 +2245,10 @@ const timerStarted = timeLeft !== null
{examChrome ? (
<>
<div className="quiz-footbar-left">
<button type="button" className="btn btn-secondary btn-sm quiz-exit"
onClick={() => setLeaving(true)}>Exit session</button>
<button type="button" className="quiz-block-btn quiz-exit"
onClick={() => setLeaving(true)}>
<span aria-hidden="true"></span> Exit session
</button>
{/* The block's own clock, and the only one: a second countdown
elsewhere on the screen is a second chance to misread it. An
untimed block says so rather than showing an empty space
@ -2250,10 +2259,14 @@ const timerStarted = timeLeft !== null
</div>
{/* Pause is the exam's own pause the same flag the clocks read,
and the same Exam Paused dialog that covers the questions. */}
<button type="button" className="btn btn-secondary btn-sm quiz-block-pause"
onClick={() => setClockPaused(true)}>Pause</button>
<button type="button" className="btn quiz-block-end" disabled={submitting}
onClick={() => setShowReview(true)}>End Block</button>
<button type="button" className="quiz-block-btn quiz-block-pause"
onClick={() => setClockPaused(true)}>
<span aria-hidden="true"></span> Pause
</button>
<button type="button" className="quiz-block-btn quiz-block-end" disabled={submitting}
onClick={() => setShowReview(true)}>
<span aria-hidden="true"></span> End Block
</button>
</>
) : (
<>

View file

@ -381,6 +381,33 @@ body:has(.quiz-player.is-boxed) .navbar { margin-bottom: 0; }
}
.quiz-block-end:hover:not(:disabled) { background: #365b8d; }
/* The block bar's own three controls: an icon, a word, and enough weight to
be found without reading. Leaving a block, stopping its clock and handing
it in are the loudest decisions on the screen, and they were three grey
pills of the same size as everything else. */
.quiz-block-btn {
display: inline-flex; align-items: center; gap: 7px; flex-shrink: 0;
padding: 9px 16px; font: inherit; font-size: .8rem; font-weight: 700;
letter-spacing: .04em; text-transform: uppercase; cursor: pointer;
color: var(--text); background: none;
border: 1px solid var(--border); border-radius: 6px;
}
.quiz-block-btn span { font-size: .95rem; line-height: 1; }
.quiz-block-btn:disabled { opacity: .5; cursor: not-allowed; }
/* The rail says which session this is, not that a session has questions. */
.quiz-rail-name { display: flex; flex-direction: column; gap: 3px; min-width: 0; text-transform: none; letter-spacing: 0; }
.quiz-rail-name strong {
font-size: .82rem; font-weight: 700; line-height: 1.35; color: var(--text);
overflow-wrap: anywhere;
}
.quiz-rail-name small { font-size: .72rem; font-weight: 600; color: var(--text-subtle); }
.quiz-rail-badge {
align-self: flex-start; padding: 1px 7px; border-radius: 4px;
font-size: .6rem; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
background: #e5ecf8; color: #365b8d;
}
/* Nothing else on the page while a session is being sat. */
body:has(.quiz-player.is-boxed) .site-footer { display: none; }
@ -745,14 +772,22 @@ body:has(.quiz-player.is-exam-chrome) .site-footer { display: none; }
.quiz-player.is-exam-chrome .quiz-block-time strong { color: #fff; }
.quiz-player.is-exam-chrome .quiz-exit,
.quiz-player.is-exam-chrome .quiz-block-pause {
color: #fff; background: none; border: 1px solid rgba(255, 255, 255, .4);
color: #fff; background: none; border-color: rgba(255, 255, 255, .45);
}
.quiz-player.is-exam-chrome .quiz-exit:hover,
.quiz-player.is-exam-chrome .quiz-block-pause:hover { background: rgba(255, 255, 255, .16); }
.quiz-player.is-exam-chrome .quiz-block-end {
background: #b03047; border-color: #b03047;
.quiz-player.is-exam-chrome .quiz-block-pause:hover {
background: rgba(255, 255, 255, .16); border-color: #fff;
}
.quiz-player.is-exam-chrome .quiz-block-end:hover:not(:disabled) { background: #93253a; }
.quiz-player.is-exam-chrome .quiz-block-end {
color: #fff; background: #b03047; border-color: #b03047;
}
.quiz-player.is-exam-chrome .quiz-block-end:hover:not(:disabled) { background: #93253a; border-color: #93253a; }
/* On the navy, the badge and the title are read against it rather than on a
card of their own. */
.quiz-player.is-exam-chrome .quiz-rail-name strong { color: #fff; }
.quiz-player.is-exam-chrome .quiz-rail-name small { color: rgba(255, 255, 255, .7); }
.quiz-player.is-exam-chrome .quiz-rail-badge { background: rgba(255, 255, 255, .2); color: #fff; }
@media (max-width: 900px) {
.quiz-player.is-exam-chrome .quiz-topbar { gap: 8px; padding: 6px 8px; }