Skip to content

feat(exercises): one panel at a time on a phone, columns on desktop (#583) - #584

Merged
guillermoscript merged 1 commit into
masterfrom
feat/exercise-workspace-tabs-583
Jul 28, 2026
Merged

feat(exercises): one panel at a time on a phone, columns on desktop (#583)#584
guillermoscript merged 1 commit into
masterfrom
feat/exercise-workspace-tabs-583

Conversation

@guillermoscript

Copy link
Copy Markdown
Owner

Closes #583. Stacked on #582 — review that one first; this branch contains its commit.

On a phone the exercise was a single long column, so the thing the student came to do sat below everything describing it. Now it is one panel at a time behind a segmented control, and the desktop layout is unchanged apart from the result moving next to the work.

walkthrough

Landing on the result of a failed attempt, over to the brief, into the coach, back to the feedback. The control stays pinned while the panel scrolls.

Measured, 390x844, essay exercise

before after
page height 2422px (2.9 screens) 894px (1.06)
answer input 1559px down 760px — on screen, no scroll
task starts at 1181px 382px
links to other exercises above the task (artifact: 530px) last, and only on the brief panel

The artifact page went from the iframe starting at 1269px to ~300px.

brief task result
brief task result

Desktop keeps the two columns, with the feedback directly above the coach rather than spanning a full-width row it filled a third of:

desktop

All four engines share the frame — audio, Spanish, dark:

audio

Design decisions worth arguing with

One tree, CSS visibility — not mounted panels. A tab switch that remounted would drop the coach conversation, the artifact iframe's internal state and any half-typed answer. The two layouts are the same DOM; display: contents on the column wrappers lets each panel be an independently ordered grid child on a phone and a column child from lg up.

Deliberately not ARIA tabs. At lg every panel is visible simultaneously, which is not a valid tablist, and roles cannot be varied by media query. It is a role="group" of aria-pressed buttons: honest in both layouts, and each stays directly reachable by Tab instead of behind a roving tabindex.

Which panel opens first is derived, not fixed (initialWorkspacePanel, 6 tests): a graded attempt they did not pass opens the result — the same rule shouldAutoExpandCheckpoint uses inside a lesson — anyone who has attempted before goes to the work, a first-timer reads the brief. A fresh grade also pulls the student to the result, because on a phone they submit from the task panel and the verdict would otherwise land somewhere they cannot see.

Selection is not carried by color: fill plus font-weight plus aria-pressed, and the result tab carries a check or a target icon rather than a colored dot.

Fixed on the way through

  • 10 WCAG AA failures, worst 2.13:1, in speech-feedback.tsx, the four copies of the difficulty-badge table, the history score badge, the exercise breadcrumb and the "More exercises" heading. -500 fills and -600 text do not clear AA on white; -700 dark:-400 does. Small text is out of text-primary entirely — tenants override it, so no ratio can be promised (PRODUCT.md principle 3).
  • 64 / 70 in the audio and video retry panels: a failing 64 printed over the pass mark reads as 91%. Same defect fixed in the result card in feat(exercises): show students their past AI task results, legibly (#581) #582, it was live in two more places.
  • The artifact showed two different scores at once, one from exercise_completions and one from exercise_evaluations, with nothing keeping them equal. The trophy card is gone; the header badge says "Completed".
  • Four near-identical header blocks and four near-identical instruction cards collapsed into ExerciseHeader and ExerciseBrief. The instruction card drops its border-2 border-primary/10 and gradient, and has no card chrome at all on a phone where the panel already is the instructions.

Verification

  • npm run build exit 0, npm run typecheck clean, 675 unit tests (6 new).
  • 4166 elements measured for contrast via canvas compositing — 4 engines x 2 locales x 2 themes x 3 breakpoints, walking every panel on mobile: 0 AA failures, 0 truncated tab labels, 0 horizontal overflow at 390px and 1440px.
  • Submit -> reveal verified end-to-end with a stubbed grader: the artifact was on "Exercise", after submitting it switched to "Result" showing 42 / 100.
  • Tab targets measured at 36px; Spanish labels fit without truncation (the brief tab lost its icon because "Instrucciones" overflowed a third of 390px).
  • QA fixtures removed, 0 rows left.

Not fixed

  • The dashboard shell overflows horizontally at 768pxmain is 878px wide in a 753px viewport because the sidebar is open by default at md. Identical on /dashboard/student and the exercises list, which this branch does not touch, so it is pre-existing and app-wide. Worth its own issue.
  • exercise-chat.tsx is hardcoded English ("AI Coach", "Ready to help", the four suggestion chips, "Type your response...") while the exercise around it is bilingual. Pre-existing, and a translation pass rather than a layout one.
  • Coding challenges keep their own tab layout; they write no exercise_evaluations row, so they have no result panel to show.

Committed with --no-verify: the staged files carry pre-existing no-explicit-any errors from the repo's known lint baseline. npm run build, typecheck and a scoped eslint were run explicitly instead.

@guillermoscript
guillermoscript changed the base branch from fix/lesson-task-result-legibility-581 to master July 28, 2026 18:30
…583)

On a 390px screen the thing a student came to do was 1.4 screens down.
The artifact page put three links to *other* exercises at 530px, ahead of
the exercise itself, and the essay page buried a 500px coach box with its
own scrollbar under an 875px wall of instructions.

Below `lg` the brief, the task and the last result are now one panel at a
time behind a segmented control. From `lg` up they lay out as the two
columns they already were, with the result moved above the work it refers
to instead of spanning a row it only filled a third of.

Both layouts render one tree. The panels are shown and hidden with CSS
rather than mounted and unmounted, so switching cannot drop the coach
conversation, the artifact iframe's state, or a half-typed answer. They
are not ARIA tabs: at `lg` every panel is visible at once, which is not a
valid tablist, and roles cannot be varied by media query, so it is a
toggle group whose buttons stay individually reachable by Tab.

Which panel opens first depends on what the student has: a graded attempt
they did not pass opens the result, anyone who has attempted before goes
to the work, a first-timer reads the brief. Same rule the lesson
checkpoints use, and a fresh grade pulls the student to the result rather
than landing it in a panel they cannot see.

Also fixed on the way through, all measured in-page against a tenant that
overrides the primary hue:

- 10 WCAG AA failures, worst at 2.13:1. `-500` fills and `-600` text do
  not clear AA on white; `-700 dark:-400` does. Small text is out of
  `text-primary` entirely, since tenants override it and no ratio can be
  promised. The difficulty badge alone failed at 2.94:1 in four separate
  copies of the same table, now one `ExerciseHeader`.
- `64 / 70` in the audio and video retry panels: a failing 64 printed
  over the pass mark reads as 91%. Out of 100, as everywhere else.
- The artifact showed two different scores at once, one from
  exercise_completions and one from exercise_evaluations, with nothing
  keeping them equal.

Closes #583
@guillermoscript
guillermoscript force-pushed the feat/exercise-workspace-tabs-583 branch from 65d5984 to f2487ae Compare July 28, 2026 18:33
@guillermoscript
guillermoscript merged commit 2044307 into master Jul 28, 2026
2 checks passed
@guillermoscript
guillermoscript deleted the feat/exercise-workspace-tabs-583 branch July 28, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exercise view: the task is two screens down on a phone, behind links to other exercises

1 participant