Skip to content

Tutorials

Claude edited this page Oct 26, 2026 · 3 revisions

Tutorials

English · Español

Each of the four screens (Deck, Design, Library, Play) has a short guided tour: it darkens the window except one area, outlines that area and explains it in a card beside it, step by step. It opens by itself the first time the user enters a screen and the help button in the top bar replays it. It is pure frontend (src/features/tutorial/): no Rust command, no network, nothing stored in the deck.

Files

File Responsibility
src/features/tutorial/pasos.ts type Paso = { ancla?, titulo, texto } and PASOS: Record<Vista, Paso[]>, the steps of each screen.
src/features/tutorial/tutorial.tsx Tutorial (mounted once), its inner Recorrido (the overlay and card), useTutorialInicial() (first-visit opening), and the helpers zona (measure an anchor) and sitioTarjeta (place the card).
src/stores/ui-store.ts tutorial: Vista | null (the open tour), abrirTutorial(v), and the persisted preference tutorialesVistos: Vista[]. See State-Stores.
src/features/shell/app-shell.tsx The help button (CircleHelp icon, tooltip "Tutorial de esta pantalla"), <Tutorial /> and the useTutorialInicial() call. See Settings-and-Shell.
src/i18n/<code>/tutorial.json The tour's strings, an i18n area of its own in the 15 languages. See Internationalization.
src/index.css [data-slot="tutorial-tarjeta"] shares the opaque-modal rule. See Themes-and-Skins.

Steps and anchors

A step points at an area through an anchor: the value of a data-tutorial attribute on the element to outline. The tour finds it with document.querySelector('[data-tutorial="…"]'), so the screens import nothing from the tutorial and the tutorial imports no screen component; moving a panel only needs the attribute to move with it. A step without ancla (each tour's introduction) shows its card centred over a fully dimmed window. titulo and texto are Spanish source strings wrapped in paraTraducir (the table lives outside React) and translated with t() when shown.

Screen (Vista) Anchor Title (English UI) Element (file)
deck none Welcome to Proxy Builder
pestanas Four screens the view ToggleGroup (shell/app-shell.tsx)
nombre-deck The deck's name NombreDeck input (shell/app-shell.tsx)
lista-cuadro The card list list editor panel (decklist/decklist-panel.tsx)
lista-opciones Import and options options panel (decklist/decklist-panel.tsx)
vista-barra The preview toolbar toolbar (preview/preview-pane.tsx)
vista-hojas The sheets sheets area (preview/preview-pane.tsx)
exportar-boton Export export header and button (export/export-panel.tsx)
exportar-ajustes How the paper comes out settings sections (export/export-panel.tsx)
ajustes Settings Settings button (shell/app-shell.tsx)
ayuda To go over it again help button (shell/app-shell.tsx)
disenar none Design: the card editor
disenar-lista The deck's cards ListaCartas (disenar/lista-cartas.tsx)
disenar-barra Deck tools top toolbar (disenar/disenar-screen.tsx)
disenar-vista The preview VistaPrevia (disenar/vista-previa.tsx)
disenar-incidencias Warnings Incidencias (disenar/incidencias.tsx)
disenar-formulario What you can change the form Tabs (disenar/formulario.tsx)
disenar-pie Save save footer (disenar/disenar-screen.tsx)
biblioteca none The library
biblioteca-barra Search and sort BarraHerramientas (library/barra-herramientas.tsx)
biblioteca-acciones New, import and space its actions slot (library/barra-herramientas.tsx)
biblioteca-lateral Tags and favourites BarraLateral (library/barra-lateral.tsx)
biblioteca-decks Your decks deck area (library/library-screen.tsx)
mesa none The game table
mesa-entrada Choose deck and format the entrance (mesa/entrada.tsx)
mesa-solitario Solitaire the «Solitario» button (mesa/entrada.tsx)
mesa-barra The table bar BarraMesa (mesa/barra-mesa.tsx)
mesa-campo The battlefield the battlefield layer (mesa/tablero.tsx)
mesa-mano The hand the hand label (mesa/tablero.tsx)
mesa-biblioteca The library the library zone (mesa/tablero.tsx)
mesa-vida Your life VidaJugador (mesa/vida-jugador.tsx)
mesa-tablero Shortcuts the whole board (mesa/tablero.tsx)

The texts describe what each area does in the current version (for example the card sizes in "How the paper comes out", or that split, aftermath and double-faced cards have one artwork per part in the Design preview step), so a feature change that alters an area should also update its step.

The Play tour mixes two screens: the entrance (no game open) and the table (a game open). Only the anchors of the one on screen exist, so the others show centred; the tour opens by itself the first time «Jugar» is entered, which is normally the entrance. Other table anchors exist for future steps or reuse (mesa-lista, the game list selector of the entrance, mesa-pila, mesa-fantasma, mesa-pasar, mesa-cementerio, mesa-exilio, mesa-mando, mesa-banquillo). The table elements are described in Game-Table.

The overlay (Recorrido)

flowchart LR
  A["useUi().tutorial = vista"] --> B["Tutorial: Recorrido key=vista"]
  B --> C["zona(ancla): getBoundingClientRect"]
  C -->|found, at least 4 x 4 px| D["ring + box-shadow dims the rest"]
  C -->|missing or hidden| E["full dim, card centred"]
  D --> F["sitioTarjeta: right, left, below, above, else centre"]
  E --> F
Loading
  • Portal into document.body: a fixed inset-0 z-[100] layer (data-tutorial-capa) that covers the window, so clicks outside the card do nothing.
  • Highlight: a non-interactive box 6 px (AIRE) larger than the area, with a primary ring and a 0 0 0 9999px black shadow at 60 % that dims everything else. Without an area, a plain 60 % black layer.
  • Card (data-slot="tutorial-tarjeta", role="dialog", aria-modal, labelled by its title): 320 px wide (ANCHO). sitioTarjeta tries, in order, to the right of the area, to the left, below and above, 14 px away (HUECO) and at least 16 px from the window edges (MARGEN), clamped along the other axis; if no side has room, centred. Its height is measured after each render so the placement uses the real size.
  • Re-measuring: on every step change, a ResizeObserver on the anchored element and the window resize event, so dragging the resizable panels or resizing the window moves the outline and the card along.
  • Missing anchor: if the element is not in the DOM or is smaller than 4 x 4 px (for example a Design panel that is not rendered while no card is selected), the step still shows, centred.
  • Controls: a "Step {a} of {b}" counter, Back (from step 2), Next (Finish on the last step) and an X ("Close the tutorial"). The main button takes the focus at each step, so Enter advances.
  • Keyboard: a keydown listener on window in the capture phase handles Escape (close), ArrowRight (next, or close on the last step) and ArrowLeft (back), and calls preventDefault + stopPropagation for them. Why capture: the screens have their own arrow handlers (Alt+arrows in Design, selection in the sheets) and they must not react while the tour is open. Other keys pass through untouched.
  • Tutorial renders Recorrido with key={vista}, so opening another screen's tour starts from step 1.

When it opens and what is remembered

  • abrirTutorial(v) sets tutorial and, when v is not null and not yet in tutorialesVistos, appends it; then it saves the preferences. A tour therefore counts as seen as soon as it opens, even if the user closes it at the first step: it is offered once, never nagging. abrirTutorial(null) closes.
  • tutorialesVistos is persisted with the other preferences in localStorage["proxybuilder.preferencias"]; the open tour (tutorial) is session-only.
  • useTutorialInicial() (called by AppShell) watches useUi().vista. If that screen is not in tutorialesVistos and no tour is open, it waits 700 ms (so the screen is painted and its anchors measurable) and then opens it, but only if the user is still on that screen, no tour opened meanwhile, useUi().dialogo is empty and no [role="dialog"] / [role="alertdialog"] is in the DOM. A tour never opens on top of a dialog; if one was open, it is offered again the next time the user enters that screen.
  • The help button calls abrirTutorial(vista) for the current screen at any time.

Extending

  • New step: add data-tutorial="<id>" to the element, add a Paso to PASOS[vista] at the right position, and add both strings to tutorial.json in all 15 languages (pnpm idiomas fails otherwise). Keep the anchor on an element that is always rendered on that screen, or accept that the step will show centred.
  • New screen (Vista): PASOS is a Record<Vista, Paso[]>, so TypeScript requires its steps; useTutorialInicial and the help button then work without changes.
  • Keep the card opaque: it relies on the opaque-modal rule in src/index.css that also covers dialogs.

Tests

src/stores/__tests__/ui-store.test.ts: opening a tour marks it as seen once and the list is persisted, while the open tour is not (abrir un tutorial lo apunta como visto (una vez) y se recuerda; el abierto no), and the persisted preference keys include tutorialesVistos. The overlay itself has no automated test (it depends on layout measurements that Vitest's node environment does not have).

See also

Documentation · Documentación

Language: English · Español

Use and build · Uso y desarrollo

Architecture · Arquitectura

Data · Datos

Getting cards · Conseguir cartas

Layout, images, PDF · Maquetación, imagen y PDF

Card designer · Diseñador de cartas

Game table · Mesa de juego

Frontend · Frontend

Reference · Referencia

Clone this wiki locally