Skip to content

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-material ya 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:

  1. Read a theory or lab page.
  2. Write a journal entry or a note.
  3. Answer a quiz or exam question.
  4. 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.locale and 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.