Skip to content

Commit 3540fc9

Browse files
fuzzie360claude
andcommitted
learn: the output pane was unusable on a phone
Measured on a 390x844 phone: page chrome took 263px, the brief took 321px at 38vh, and the 1.4:1 editor/output split left the Tests panel 72px of content — one and a half rows. The reported symptom was "it only shows a single line", which is exactly what 72px buys. Three changes, and the third is what makes the other two affordable: brief 38vh -> 24vh on phones. It is a third of the screen given to text you have usually finished reading by the time you are looking at test output. editor/output split evened out (1.4:1 -> 1:1.15) with an 11rem floor so the panel can never collapse to a sliver again. a full-screen button on BOTH the output pane and the task description, phones only — which is why the brief can afford to be short by default: a long description is one tap from the whole screen. 72px and 1 visible row -> 166px and 3, with either surface expandable to the full 844. All three full screens (editor, output, brief) are mutually exclusive; two fixed-inset panels would stack. Desktop is untouched: both buttons are display:none above 720px, and a min-width guard undoes the fixed positioning if a viewport widens while a panel is expanded — otherwise the button would vanish with the media query and leave no way out. Verified on a real 390x844 viewport: rows visible without scrolling, both full screens filling the viewport, mutual exclusivity, and desktop metrics unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RJujXBsBhXBbQ72tS7E7GV
1 parent 9298cf8 commit 3540fc9

2 files changed

Lines changed: 129 additions & 4 deletions

File tree

‎src/Learn/scss/_task.scss‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,51 @@
778778
color: var(--blue);
779779
}
780780

781+
/* Phones only. On a desktop the panel already has room, and a second
782+
fixed-inset surface competing with the editor's full screen would be
783+
clutter. Shown by the media query at the bottom of this file. */
784+
/* Same affordance for the task description. Floats within the brief's own
785+
scroll box and sticks to the top, so it stays reachable however far down
786+
the reader has scrolled. */
787+
.brief-fullbtn {
788+
display: none;
789+
position: sticky;
790+
top: 0;
791+
float: right;
792+
z-index: 2;
793+
margin: -.4rem -.4rem 0 .5rem;
794+
background: var(--surface);
795+
border: 1px solid var(--line);
796+
border-radius: 6px;
797+
color: var(--muted);
798+
font-size: .72rem;
799+
padding: .15rem .5rem;
800+
height: auto;
801+
line-height: 1.5;
802+
text-transform: none;
803+
box-shadow: none;
804+
}
805+
806+
.panel-fullbtn {
807+
display: none;
808+
margin-left: auto;
809+
background: none;
810+
border: 1px solid var(--line);
811+
border-radius: 6px;
812+
color: var(--muted);
813+
font-size: .72rem;
814+
padding: .15rem .5rem;
815+
height: auto;
816+
line-height: 1.5;
817+
text-transform: none;
818+
box-shadow: none;
819+
}
820+
821+
/* clear keeps its own right-alignment only while the button is absent */
822+
.btabs .panel-fullbtn + .clearbtn {
823+
margin-left: .4rem;
824+
}
825+
781826
.bpanel {
782827
flex: 1;
783828
overflow-y: auto;
@@ -1271,6 +1316,61 @@
12711316
.ed-hint { display: none; }
12721317
}
12731318

1319+
/* ---- phones: the output pane was unusable ----
1320+
Measured on a 390x844 phone before this block: chrome ate 263px, the brief
1321+
took 321px at 38vh, and the 1.4:1 editor/output split left the panel 72px
1322+
of content — one and a half test rows. */
1323+
@media (max-width: 720px) {
1324+
.brief {
1325+
// Now that the brief has its own full screen, it does not need to be
1326+
// tall by default — which is what buys the output pane its room.
1327+
max-height: 24vh;
1328+
}
1329+
1330+
.brief-fullbtn { display: inline-block; }
1331+
1332+
.brief.brief-full {
1333+
position: fixed;
1334+
inset: 0;
1335+
z-index: 100;
1336+
max-height: none;
1337+
border-bottom: none;
1338+
}
1339+
1340+
// even split, and a floor so the panel can never collapse to a sliver
1341+
.editor { flex: 1; }
1342+
1343+
.bottompane {
1344+
flex: 1.15;
1345+
min-height: 11rem;
1346+
}
1347+
1348+
.panel-fullbtn { display: inline-block; }
1349+
1350+
/* Full screen output. Same z-index as the editor's full screen — they are
1351+
mutually exclusive in TaskPage, so they never coexist. */
1352+
.bottompane.bottompane-full {
1353+
position: fixed;
1354+
inset: 0;
1355+
z-index: 100;
1356+
flex: none;
1357+
min-height: 0;
1358+
border-top: none;
1359+
}
1360+
}
1361+
1362+
/* Safety net: if the viewport widens while the panel is full screen the
1363+
button vanishes with the media query above, so undo the fixed positioning
1364+
too — otherwise there would be no way out. */
1365+
@media (min-width: 721px) {
1366+
.bottompane.bottompane-full,
1367+
.brief.brief-full {
1368+
position: static;
1369+
inset: auto;
1370+
z-index: auto;
1371+
}
1372+
}
1373+
12741374
/* ============ completion modal (module / track celebration) ============ */
12751375
.cmodal-overlay {
12761376
position: fixed;

‎src/Learn/task/TaskPage.jsx‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ function TaskInputs({ docs }) {
255255
);
256256
}
257257

258-
function BriefPane({ module, task, taskNum, total, inputDocs }) {
258+
function BriefPane({ module, task, taskNum, total, inputDocs, full, onToggleFull }) {
259259
const figures = getFigures(module, task);
260260
const briefRef = useRef(null);
261261
// Static syntax highlighting for authored <pre><code> blocks. Layout effect
@@ -265,7 +265,18 @@ function BriefPane({ module, task, taskNum, total, inputDocs }) {
265265
highlightCodeBlocks(briefRef.current);
266266
}, [task]);
267267
return (
268-
<aside className="brief" ref={briefRef}>
268+
<aside className={full ? 'brief brief-full' : 'brief'} ref={briefRef}>
269+
{/* Phones only (hidden by media query): the brief is capped at a fraction
270+
of a phone screen so the editor and output have room, which makes a
271+
long task description a keyhole. */}
272+
<button
273+
type="button"
274+
className="brief-fullbtn"
275+
onClick={onToggleFull}
276+
aria-label={full ? 'Exit full screen task description' : 'Full screen task description'}
277+
>
278+
{full ? '✕ Close' : '⛶'}
279+
</button>
269280
<p className="eyebrow">Task {taskNum} of {total}</p>
270281
<h2>{task.title}</h2>
271282
<div dangerouslySetInnerHTML={{ __html: task.intro || '' }} />
@@ -352,6 +363,10 @@ function TaskWorkspace({ module, task, taskNum, taskIndex, taskId, total }) {
352363
const [tab, setTab] = useState('console');
353364
const [done, setDone] = useState(() => isTaskDone(taskId));
354365
const [fullscreen, setFullscreen] = useState(false);
366+
// Phones only (the button is hidden above 720px). Mutually exclusive with the
367+
// editor's full screen — two fixed-inset panels would stack.
368+
const [panelFull, setPanelFull] = useState(false);
369+
const [briefFull, setBriefFull] = useState(false);
355370
const [progressTick, setProgressTick] = useState(0);
356371
const [status, setStatus] = useState(''); // announced via the role=status region
357372
const [celebration, setCelebration] = useState(null); // null | { kind: 'module' | 'track' }
@@ -709,6 +724,8 @@ function TaskWorkspace({ module, task, taskNum, taskIndex, taskId, total }) {
709724
taskNum={taskNum}
710725
total={total}
711726
inputDocs={inputDocs}
727+
full={briefFull}
728+
onToggleFull={() => { setFullscreen(false); setPanelFull(false); setBriefFull(f => !f); }}
712729
/>
713730

714731
<div className="editpane">
@@ -720,7 +737,7 @@ function TaskWorkspace({ module, task, taskNum, taskIndex, taskId, total }) {
720737
<button
721738
type="button"
722739
className="ed-fullbtn"
723-
onClick={() => setFullscreen(f => !f)}
740+
onClick={() => { setPanelFull(false); setBriefFull(false); setFullscreen(f => !f); }}
724741
aria-label={fullscreen ? 'Exit full screen editor' : 'Full screen editor'}
725742
>
726743
{fullscreen ? '✕ Close' : '⛶ Full screen'}
@@ -745,7 +762,7 @@ function TaskWorkspace({ module, task, taskNum, taskIndex, taskId, total }) {
745762
/>
746763
</div>
747764

748-
<div className="bottompane">
765+
<div className={panelFull ? 'bottompane bottompane-full' : 'bottompane'}>
749766
<div
750767
className="btabs"
751768
role="tablist"
@@ -778,6 +795,14 @@ function TaskWorkspace({ module, task, taskNum, taskIndex, taskId, total }) {
778795
>
779796
Tests {testBadge && <span className="cnt">{testBadge}</span>}
780797
</button>
798+
<button
799+
type="button"
800+
className="panel-fullbtn"
801+
onClick={() => { setFullscreen(false); setBriefFull(false); setPanelFull(f => !f); }}
802+
aria-label={panelFull ? 'Exit full screen output' : 'Full screen output'}
803+
>
804+
{panelFull ? '✕ Close' : '⛶'}
805+
</button>
781806
<button type="button" className="clearbtn" onClick={() => setLogs([])}>
782807
clear
783808
</button>

0 commit comments

Comments
 (0)