Learning and Practice
Define the learning task and source boundary, preserve attempts and make checked progress distinct from credentials or language authority.
Start with the learning objective
Name what the exercise practices and what successful completion means. This example practices three English interface concepts: native navigation, retained search recovery and the difference between displayed content and authority. Its completion is not a language assessment or a credential.
A real language lesson needs source-attested vocabulary, relevant language/content metadata, qualified review and permission for its text/media/audience. Do not invent missing words or turn a model’s fluent answer into a reviewed lesson.
Anatomy and dimensions
| Part | Actual composition |
|---|---|
| Prompt / context | Visible focused heading, ordinal and total; authored question text with real guidance source links. |
| Choice | Exported named RadioGroup and labeled RadioItem choices. Selection is draft state; a checked answer is a separate event. |
| Commands | 44px minimum Check, Next/Complete and New Round buttons; no automatic advance after a wrong answer. |
| Feedback | Correct/needs-another-try text, explanation/source, linked invalid-choice error and retained attempt history. |
| Progress | Known denominator of three; value counts the latest checked answer for each current-round question, not raw attempts. |
| Direction / adaptation | Wrapping text/commands and installed DirectionProvider for real radio keyboard direction. English text remains the actual fixture language. |
Working authored interface exercise
Choose, check, correct and advance through real authored questions. Changing a draft choice after checking disables advance until that current choice is checked. All attempts remain in history, including earlier rounds.
Three authored English interface-concept questions. This is not a language translation, pronunciation assessment, qualified review or learning credential. Progress is page-memory practice only.
Round 1: 0 of 3 latest checked answers correct. 0 attempts retained across rounds.
Question 1 of 3: Which native control opens an existing destination?
Practice is in progress; no completed exercise outcome yet.
'use client';
import {useEffect,useRef,useState,type CSSProperties} from 'react';
import {Button,RadioGroup,RadioItem,Progress,DirectionProvider} from '@mobtranslate/ui';
import questions from '../public/fixtures/interface-lesson.json';
type Attempt={round:number;question:string;choice:number;correct:boolean};
const action:CSSProperties={minHeight:44,height:'auto',minWidth:0,maxWidth:'100%',whiteSpace:'normal',overflowWrap:'anywhere'};
export function LearningPracticeExample(){
const [index,setIndex]=useState(0),[round,setRound]=useState(1),[choice,setChoice]=useState(''),[checked,setChecked]=useState('');
const [attempts,setAttempts]=useState<Attempt[]>([]),[feedback,setFeedback]=useState(''),[error,setError]=useState(''),[complete,setComplete]=useState(false),[rtl,setRtl]=useState(false);
const heading=useRef<HTMLHeadingElement>(null),group=useRef<HTMLDivElement>(null),arrival=useRef<HTMLParagraphElement>(null);
const moved=useRef(false);
const question=questions[index];
const latest=attempts.filter(attempt=>attempt.round===round&&attempt.question===question.id).at(-1);
const count=questions.filter(item=>attempts.filter(attempt=>attempt.round===round&&attempt.question===item.id).at(-1)?.correct).length;
useEffect(()=>{if(moved.current){if(complete)arrival.current?.focus();else heading.current?.focus();}},[index,round,complete]);
function check(){const value=Number(choice);if(!choice||!Number.isInteger(value)||value<0||value>=question.options.length){setError('Choose one answer before checking.');group.current?.querySelector<HTMLElement>('[role=radio]')?.focus();return;}setError('');const correct=value===question.answerIndex;setAttempts(previous=>[...previous,{round,question:question.id,choice:value,correct}]);setChecked(choice);setFeedback(correct?'The checked answer is correct for this authored interface exercise.':'The checked answer needs another try. Read the explanation and source, then choose again.');}
function next(){if(!latest?.correct||checked!==choice)return;moved.current=true;if(index===questions.length-1)setComplete(true);else{setIndex(value=>value+1);setChoice('');setChecked('');setFeedback('');setError('');}}
return <DirectionProvider direction={rtl?'rtl':'ltr'}><div data-learning-lab lang="en" dir={rtl?'rtl':'ltr'} className="space-y-4" style={{overflowWrap:'anywhere'}}>
<p dir="auto">Three authored English interface-concept questions. This is not a language translation, pronunciation assessment, qualified review or learning credential. Progress is page-memory practice only.</p>
<Button style={action} type="button" variant="outline" aria-pressed={rtl} onClick={()=>setRtl(value=>!value)}>Use RTL learning layout</Button>
<Progress value={count} max={questions.length} aria-label="Current round checked interface questions" getAriaValueText={(_,value)=>`${value} of ${questions.length} current-round questions correct`} />
<p dir="auto" data-learning-count>Round {round}: {count} of {questions.length} latest checked answers correct. {attempts.length} {attempts.length===1?'attempt':'attempts'} retained across rounds.</p>
{!complete&&<div className="space-y-3"><h3 id="learning-question" ref={heading} tabIndex={-1} className="font-semibold" dir="auto">Question {index+1} of {questions.length}: {question.prompt}</h3>
<RadioGroup ref={group} name="lesson-answer" aria-labelledby="learning-question" aria-describedby={error?'learning-answer-error':undefined} value={choice} onValueChange={value=>{if(typeof value==='string')setChoice(value);}}>{question.options.map((option,number)=><RadioItem key={option} value={String(number)} label={option} />)}</RadioGroup>
{error&&<p id="learning-answer-error" role="alert" dir="auto">{error}</p>}
<div className="flex flex-wrap gap-3"><Button style={action} type="button" onClick={check}>Check interface answer</Button><Button style={action} type="button" variant="outline" disabled={!latest?.correct||checked!==choice} onClick={next}>{index===questions.length-1?'Complete local exercise':'Next interface question'}</Button></div>
{feedback&&<div><p role="status" dir="auto" data-learning-feedback>{feedback}{checked!==choice?' Current draft choice differs from the checked answer; check again before advancing.':''}</p><p dir="auto">{question.explanation}</p><a className="underline" href={question.sourceHref}>{question.sourceTitle}</a></div>}
</div>}
<p ref={arrival} tabIndex={-1} role="status" dir="auto" data-learning-completion>{complete?'Local interface exercise complete: all three latest checked answers in this round were correct. Nothing was submitted to a learning service.':'Practice is in progress; no completed exercise outcome yet.'}</p>
<Button style={action} type="button" variant="outline" onClick={()=>{moved.current=true;setRound(value=>value+1);setIndex(0);setChoice('');setChecked('');setFeedback('');setError('');setComplete(false);}}>Start a new practice round</Button>
<ol className="guidance-list" data-learning-history>{attempts.map((attempt,number)=><li key={number} dir="auto">Round {attempt.round}, {attempt.question}: option {attempt.choice+1}, {attempt.correct?'correct':'needs another try'}.</li>)}</ol>
</div></DirectionProvider>;
}
Checked source and question scope
These exact public fixture questions point to local source guidance. They are written for this interface demonstration; they are not an Indigenous language corpus or externally qualified assessment.
[
{"id":"navigation","prompt":"Which native control opens an existing destination?","options":["A link with a real href","An unlabeled decorative span","A progress indicator"],"answerIndex":0,"explanation":"A link supplies a real destination and native navigation behavior. A button performs a command.","sourceTitle":"Button guidance","sourceHref":"/components/button"},
{"id":"recovery","prompt":"What should remain available when a replacement search fails?","options":["An unrelated invented result","The last successful result, with its scope explained","A success badge for the failed request"],"answerIndex":1,"explanation":"Keep useful successful data and the requested scope distinct; offer actual retry rather than fabricating success.","sourceTitle":"Search guidance","sourceHref":"/patterns/search"},
{"id":"authority","prompt":"What is not established merely by a populated record?","options":["That text is displayed","That the row exists in this view","Qualified source or language authority"],"answerIndex":2,"explanation":"Displayed content, source evidence, qualified review and permission are separate concerns.","sourceTitle":"Source and review guidance","sourceHref":"/patterns/source-review"}
]
Answer, attempt and completion lifetime
- Selection alone creates no result. Check validates an actual option and appends a round/question/choice/correct event. An absent choice focuses the real radio target and exposes the error.
- Feedback belongs to the checked choice. If the draft choice changes, explain that difference and require checking again before advance. The latest committed attempt can remain visible independently.
- The latest checked event per current-round question determines progress. A later wrong check does not secretly retain an earlier positive score as current completion.
- Next changes the actual question and focuses its heading. Complete requires the last current checked choice to remain correct; completion focuses the surviving outcome.
- New Round resets current practice state and progress while retaining all historical attempts. It does not delete a learner profile or promise a durable learning-service record.
Feedback and recovery
| State | Useful response |
|---|---|
| No choice | Ask for one answer, associate the error and focus an operable choice. |
| Wrong / correction | Explain the authored concept and source; allow another try without ridicule, fake praise or auto-navigation. |
| Correct / edited draft | Keep the actual checked result and require checking a changed choice before advance. |
| Complete | Name exactly what completed and where its result exists. Do not infer general fluency. |
| Unavailable source/media | Keep a qualified unavailable state and a usable alternative; never invent the missing language content. |
| Retry / restart | State which progress/history/draft is retained or reset and preserve useful ownership. |
Learning content, language and authority
- Keep interface locale, lesson content language, source spelling, pronunciation context and permitted audience independent. A learner’s device locale does not authorize publication or cultural use.
- Cite the exact edition/record/excerpt for real vocabulary and preserve alternatives or qualifications. Example text and synthetic datasets must remain visibly distinguished.
- Separate practice attempts, a local score, qualified language review and an actual credential. Badge colors or completion animations cannot replace their evidence.
- Pronunciation needs real reviewed media and alternatives such as source text/transcription when appropriate. The audio demonstration below is a synthetic test signal, never a pronunciation model.
API and application ownership
| Owner | Actual contract |
|---|---|
| Question fixture | Owned ID/prompt/options/answerIndex/explanation/source context; no remote question-generation service or semantic grading model. |
| Radio / Progress / Button | Exported controls with declared values, task names and known completion range. See their full component APIs. |
| Attempts / round | Immutable append-only events and separate current round/index/choice/checked snapshot. |
| Navigation / focus | Actual heading/outcome refs; ordinary command activation plus installed radio keyboard model. No undocumented wizard or credential API. |
Accessibility, adaptation and failures
- Let prompts and options wrap; retain 44px choice/command targets and readable feedback at narrow widths. Do not force every answer into a fixed tiny card.
- Do not announce every keystroke or the whole attempt list. Use focused validation and concise result feedback; preserve a meaningful target after question/outcome changes.
- Test wrong/correct/retry/changed-choice/new-round histories, both themes, RTL radio behavior and actual content source links.
- Do not treat a visible progress fraction as proof of actual learning retention, qualified language accuracy or recorded remote completion.
Acceptance and scope
- Run test:learning-offline, audio:check, source/token freshness, lint/types/build and the complete route/fragment crawl. Inspect actual changed values, result/state ownership, recovery, keyboard focus and narrow theme/direction geometry.
- Browser model, storage readback and media-clock evidence do not establish language fluency, qualified review, confidential authorization, physical speaker output or durable backend synchronization. Verify those consuming systems separately.