English · Español
Theory 04 — UX¶
🇪🇸 La interfaz tiene tres personas (estudiante, profesor, invitado) y cuatro pantallas que cubren el 95% del uso. Sin React, sin SPA, sin diseño visual heroico —
mkdocs-materialya renderiza la mayor parte; el portal añade la capa interactiva mínima. La regla: ningún componente vive sin un caso de uso que lo justifique en menos de una frase.
The principles¶
The portal's UX exists to serve four interactions, in order of frequency:
- Read a theory or lab page.
- Write a journal entry or a note.
- Answer a quiz or exam question.
- Review a spaced-repetition card.
Everything else is a long tail. The design budget — Borja's time and the curriculum's scope — is spent on those four. Tertiary features (admin, progress dashboard, locale toggle) get just-enough UI to be usable, not delightful.
The framework choice flows from this: server-rendered HTML with a sprinkle of vanilla JS. No React (anti-goal §10 in spirit; the SPA framework cost is unjustified at this scale). No Vue. No HTMX (interesting but new dependency). The markdown editor is the one significant client-side component, and it's a third-party drop-in.
Layout¶
Every authenticated page has the same chrome:
+--------------------------------------------------------------+
| LYNX CORTEX | Curriculum Journal Notes Quizzes Progress |
| | [Admin] Logout 🇪🇸 / 🇬🇧 |
+--------------------------------------------------------------+
| | | |
| Side | Main pane | Right rail |
| bar | | (theory |
| (40 | Rendered markdown | pages only) |
| phas- | + inline annotations | |
| es) | | |
| | | |
+--------------------------------------------------------------+
| Footer: build sha · phase · last sync · status indicator |
+--------------------------------------------------------------+
- Top nav. Six links plus admin gating, logout, locale toggle. The locale flag is a button; clicking toggles
students.localeand re-renders chrome. - Sidebar. The 40-phase tree. Current phase highlighted. A checkmark glyph appears next to phases with
progress.status = 'done'. Phases are clickable; they navigate to the phase's README in the main pane. - Main pane. The work happens here. Renders markdown (from
docs/phase-NN-*/), journal entries, notes, quiz prompts, exam prompts. Width-capped at ~80ch for readability. - Right rail. Only on theory pages. Shows the learner's notes on this page, the next quiz to take, the due review cards. Empty on every other view.
- Footer. Cheap status signal: git SHA of the deployed build, current phase, last sync timestamp, a green/yellow/red status dot.
The four screens¶
Screen 1: Theory page (the most common view)¶
+--------------------------------------------------------------+
| LYNX CORTEX | Curriculum* Journal Notes Quizzes ... |
+--------------------------------------------------------------+
| > Phase 32 | | |
| theory | # 02 — Grammar tutor agent loop | Notes |
| 00 | | on this|
| 01 | > 🇪🇸 El bucle del agente lee una | page: |
| ▶ 02 | > frase, identifica el verbo... | |
| 03 | | "Why |
| labs | ## The loop | ‘ate’ |
| | | not |
| | The grammar tutor reads an English | ‘eated’|
| | sentence, tokenizes it, applies the | -- §4 |
| | conjugation classifier, and... | |
| | | +new |
| | [+ add a note here] | |
| | | --- ---|
| | ```python | Next |
| | def conjugate(verb, tense, person) | quiz: |
| | ``` | "Past |
| | | simple |
| | ... | irreg." |
| | | |
| | | --- ---|
| | | Due |
| | | review:|
| | | 3 |
| | | cards |
+--------------------------------------------------------------+
The [+ add a note here] action is the one piece of inline interactivity. Clicking it opens an inline editor anchored to that paragraph. On save, a notes row is inserted with page_path = '/docs/phase-32/theory/02-grammar-tutor.md' and the note appears in the right rail.
The right rail is densely informational: notes on the page, next quiz, due reviews. Empty space on the right rail signals "you're caught up here" — also useful.
Screen 2: Journal¶
+--------------------------------------------------------------+
| LYNX CORTEX | Curriculum Journal* Notes Quizzes ... |
+--------------------------------------------------------------+
| |
| Journal — Borja [ Today | Calendar ] |
| |
| 2026-05-23 (today) |
| ---------------- |
| |
| 14:30 — Started phase 32. Read the grammar tutor |
| BLUEPRINT. The "agent loop" framing finally clicked |
| after seeing the diagram in §3. Open question: how does |
| the tool registry handle locale, since the conjugator |
| takes Spanish optionally. |
| |
| 15:15 — Implemented `is_regular(verb)` from the |
| lab statement. First test passes. Second test fails on |
| "have" — turns out `have/has/had/had` is its own class. |
| Updated table. |
| |
| [ Markdown editor below ] |
| ┌─────────────────────────────────────────────────────┐ |
| │ B I H link code ``` list preview │ |
| ├─────────────────────────────────────────────────────┤ |
| │ Append a journal entry... │ |
| │ │ |
| │ │ |
| └─────────────────────────────────────────────────────┘ |
| [ Save entry ] |
+--------------------------------------------------------------+
The Markdown editor is SimpleMDE or a similar pure-JS drop-in (no React, no build step). The portal includes the vendored copy under static/vendor/simplemde/ for reproducibility — no CDN fetch at runtime.
Behavior: each save inserts a journal_entries row with day = CURRENT_DATE. Within the same day, multiple entries accumulate; the rendering joins them with timestamps. Past days are read-only by default; an "edit" affordance per entry triggers a confirmation modal.
Screen 3: Quiz¶
+--------------------------------------------------------------+
| LYNX CORTEX | Curriculum Journal Notes Quizzes* ... |
+--------------------------------------------------------------+
| |
| Quiz: past-simple-irregular-verbs |
| Phase 32 · Question 3 of 8 · 04:12 elapsed |
| |
| ---------------------------------------- |
| |
| Conjugate "eat" in the past simple, 3rd person singular. |
| |
| Answer: [ ate ] |
| |
| [ Skip ] [ Next ] |
| |
| ---------------------------------------- |
| |
| Progress: ████████░░░░░░░░░░░░░░░░░░ 37% |
| |
+--------------------------------------------------------------+
The quiz UI is deliberately minimal. One question at a time, a single answer field, two buttons. The scoring (per Phase 11 — tokens are used to compare normalized text answers against the answer key) is server-side; the client only carries text.
On submit-all: a quiz_attempts row is finalized, score_pct is computed, the response is answers_json. The next view shows per-question right/wrong with a short feedback string drawn from the rubric.
Screen 4: Review¶
+--------------------------------------------------------------+
| LYNX CORTEX | Curriculum Journal Notes Quizzes Progress |
+--------------------------------------------------------------+
| |
| Today's reviews |
| ─────────────── |
| |
| Due today: 7 · Done: 0 |
| |
| Card 1 of 7 |
| |
| ┌────────────────────────────────────────────────────┐ |
| │ Past simple of "go", 1st person singular? │ |
| └────────────────────────────────────────────────────┘ |
| |
| [ Reveal answer ] |
| |
| ... |
| |
| How well did you remember? |
| [ 0 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] |
| blackout forgot hard okay good perfect |
| |
+--------------------------------------------------------------+
This is the spaced-repetition surface (theory 05). The grading button (0–5) maps directly to SM-2's quality input. The transition (show prompt → reveal answer → grade) is the only animated interaction in the portal; everything else is a page transition.
Three personas¶
Persona 1: Student (the default)¶
The student is the modal user. They open the portal each morning, see the sidebar's current phase highlighted, the right rail's "Due reviews: 7", and the day's journal slot empty. They:
- Spend most time on theory and lab pages.
- Maintain the journal as a per-session habit.
- Attempt quizzes when prompted.
- Tackle exam questions at phase close.
- Clear due review cards.
Their navigation is heavily vertical within a phase — they click through theory 00 → 01 → 02 → labs → exam, mostly sequentially.
Persona 2: Teacher / admin¶
The admin is the same person (Borja in the MVP), wearing a different hat. They:
- Onboard new students (in the schema sense; the MVP has only Borja).
- Review another student's progress, if a future learner exists.
- Reset a forgotten-password flag.
- Audit recent activity from the log.
The admin view is an additional page, not a parallel interface. The student-mode chrome is preserved; an Admin link appears in the top nav when students.role IN ('admin', 'teacher').
+--------------------------------------------------------------+
| Admin — Students |
+--------------------------------------------------------------+
| username display_name role last_login actions |
| ---------- -------------- -------- -------------- --------- |
| borja Borja Tarrasó admin 2026-05-23 [Reset] |
| _template _template student — [Reset] |
+--------------------------------------------------------------+
| [+ Create student] |
| |
| Recent audit log |
| ---------------- |
| 14:30 login_success borja |
| 11:02 password_set borja |
| 11:01 login_success borja (must_set=true) |
| 11:00 admin_create_student borja |
+--------------------------------------------------------------+
Persona 3: Guest¶
The guest persona is the read-only sharer. The curriculum is public; the portal supports an unauthenticated browsing mode that:
- Renders curriculum content (
docs/phase-NN-*/theory and lab pages). - Hides journal, notes, quizzes, progress, admin entirely.
- Shows a top-bar banner: "You are browsing as a guest. [Sign in] to track progress."
- Does not write to any DB table.
The guest is the public face of the curriculum. The route / for an unauthenticated visitor renders the same theory tree, minus the personal layers. Phase 41's social goal — let other learners fork the curriculum — depends on this view working without an account.
Locale toggle: chrome only¶
The 🇪🇸 / 🇬🇧 toggle in the top nav switches UI chrome (top nav labels, button text, footer text) between English and Spanish. Theory pages themselves retain their bilingual prose per §0.6 of CLAUDE.md — the Spanish summary block > 🇪🇸 ... at the top of each theory page is part of the content, not the UI.
Why split chrome from content: theory is bilingual by design; chrome is a localization concern. Localizing chrome with a 30-string dictionary is cheap; localizing the entire curriculum is the curriculum's whole point.
Implementation: a Jinja2 filter {% trans %} keyed against students.locale, with translations in src/miniportal/locale/{en,es}.yaml. Total string count is bounded — the Phase 41 lab caps it at 50.
What this UX does NOT have¶
- No real-time updates. No WebSocket for "new card due"; the user refreshes or navigates.
- No mobile-specific layout. Responsive enough to be usable on a phone, but not optimized.
- No drag-and-drop. Quiz reorder, card reorder, etc. — out of scope.
- No theming. mkdocs-material's default theme is the visual baseline; no custom CSS framework.
- No animations beyond card reveal. Page transitions are page loads.
- No global search. Future addition; the per-page browser search (Ctrl+F) is enough for MVP.
One-paragraph recap¶
Four screens (theory, journal, quiz, review) and three personas (student, admin, guest) cover the portal's UX. Layout is server-rendered HTML with a thin vanilla-JS layer for the markdown editor and the SR card reveal. Locale toggling is UI-chrome only; theory pages stay bilingual by content. The right rail on theory pages does most of the cross-feature integration work: notes, next quiz, due reviews — three call-to-action affordances at one glance, where the work actually happens.
Next: theory/05-spaced-repetition.md — SM-2 derivation, FSRS-5 as the future option, card lifecycle.