From 13f374567561d945d34d01ed65d893b320d1db94 Mon Sep 17 00:00:00 2001 From: Hanrim <148833226+MerciHanrim@users.noreply.github.com> Date: Tue, 6 Oct 2026 03:08:29 +0900 Subject: [PATCH] fix(tour): the guided tour says each step once and never drops focus (v0.18.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of #308. Version 0.18.2. Measured on v0.18.1 before the change: the step popover is one dialog whose title and body change in place, the only live text was the `N / 6` position, so a screen reader heard `2 / 6` and nothing else from step 2 on; focus entered on the close control; pressing Back down to step 1 disabled the focused Back button and dropped focus to
inside the dialog; and a first-run tour ended by Done left focus on . The contract, decided by Lumi on 2026-10-06 (docs/guided-tour.md §GT4): step 1 is said by the dialog's name (its `1 / 6` position and its title, both named by aria-labelledby, so the number is not lost) and description (its body, aria-describedby) when focus enters it, on Next. Every later step is said once by one hidden live region inside the popover (polite, atomic), written by the Next or Back press itself, as `N / 6. title. body`; it starts empty, so step 1 is not said twice, and a re-render that is not a step change (a language switch) writes nothing. The visible `N / 6` is no longer live and the title never was. The sentence is its own key, tour.nav.announce, in all 18 languages, with each language's own punctuation (the ideographic full stop in Japanese and Chinese, a space in Thai), not English joined in code. On Back to step 1, focus moves to Next before Back is disabled (disabled stays, not aria-disabled). Every exit (Done, Escape, the close control), first run or replay, lands on the Help button, the overflow button when the toolbar has folded Help into it, or More on a phone, skipping a target that is gone, hidden (visibility included: the overflow button at 1280 px is in the page with visibility: hidden), inert or disabled; only when none of them is usable, the last resort is the top bar's first usable menu button outside the palette, never a palette piece and never . The Welcome card is described by its question; its buttons, order and first focus are unchanged. The phone tour has the same contract and the same layout. New e2e/tour-announcement.spec.ts (19 tests) records every text the tour's live regions come to hold and every focus loss to from before the app boots. It pins the Welcome card's name and its aria-describedby question; step 1's dialog named `1 / 6` plus its title and described by its body, in Playwright's computation and in Chromium's own accessibility tree, with focus on Next and nothing live; steps 2 to 6 each said exactly once and the announcer as the tour's only live region; Back to step 1 leaving focus on Next; a language switch announcing nothing; and Done, Escape and the close control, first run and replay, at 1280 px, 760 px and 390 px, landing on Help, the overflow button or More, plus the fallback with Help hidden. Run against the code with the two focus fixes reverted, 9 of the 19 fail. Three release-note lines and the step sentence in 18 languages; the 16 other than English and Korean have not been reviewed by a native speaker. The English lines do not say "screen" (the pt-PT audit forbids it: the product has no screen concept). The per-language copy tests move their pinned counts by the four keys and declare the step sentence where it reads the same as English and Escape where each guard asks; the pt-PT audit's differing keys move from 269 to 270 and those outside the password keys from 241 to 242, under the unchanged bound of a quarter of the 970 non-password keys. The ICU argument total moves from 212 to 216 (two numbers and two catalog strings, classified by the checker itself). Docs: docs/guided-tour.md §GT4, §GT8, §GT9 and §GT10, CHANGELOG.md and README.md. --- .changes/tour-announcement.json | 1 + CHANGELOG.md | 10 + README.md | 23 +- docs/guided-tour.md | 64 ++++- e2e/tour-announcement.spec.ts | 380 ++++++++++++++++++++++++++ e2e/whats-new.spec.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- scripts/icu-argument-disposition.json | 2 +- src/components/GuidedTour.tsx | 110 ++++++-- src/i18n/es419Copy.test.ts | 2 + src/i18n/itCopy.test.ts | 15 +- src/i18n/locales/ar/ui.ts | 4 + src/i18n/locales/de/ui.ts | 4 + src/i18n/locales/en/ui.ts | 4 + src/i18n/locales/es-419/ui.ts | 4 + src/i18n/locales/es-ES/ui.ts | 4 + src/i18n/locales/fr/ui.ts | 4 + src/i18n/locales/it/ui.ts | 4 + src/i18n/locales/ja/ui.ts | 4 + src/i18n/locales/ko/ui.ts | 4 + src/i18n/locales/nl/ui.ts | 4 + src/i18n/locales/pt-BR/ui.ts | 4 + src/i18n/locales/pt-PT/ui.ts | 4 + src/i18n/locales/ru/ui.ts | 4 + src/i18n/locales/th/ui.ts | 4 + src/i18n/locales/tr/ui.ts | 4 + src/i18n/locales/vi/ui.ts | 4 + src/i18n/locales/zh-Hans/ui.ts | 4 + src/i18n/locales/zh-Hant/ui.ts | 4 + src/i18n/nlCopy.test.ts | 8 +- src/i18n/ptPtCopy.test.ts | 9 +- src/i18n/ruCopy.test.ts | 2 +- src/i18n/thCopy.test.ts | 8 +- src/i18n/trCopy.test.ts | 3 +- src/i18n/viCopy.test.ts | 12 +- src/releaseNotes/releaseNotes.ts | 8 + 37 files changed, 669 insertions(+), 68 deletions(-) create mode 100644 .changes/tour-announcement.json create mode 100644 e2e/tour-announcement.spec.ts diff --git a/.changes/tour-announcement.json b/.changes/tour-announcement.json new file mode 100644 index 00000000..25a7e035 --- /dev/null +++ b/.changes/tour-announcement.json @@ -0,0 +1 @@ +{ "type": "user-facing", "releaseNoteId": "release:0.18.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ee3921..a9682a6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable Loop Studio releases, newest first. Behavioral changes are pinned in versioned spec documents (see the [README](README.md#technical-reference)); this file is the narrative history, not the contract. +## v0.18.2 — 2026-10-06 + +A fix release (issue #308): the guided tour says each step once and never loses focus. + +- **Each step is announced once.** Step 1 is read as the tour opens, by its title and text; every later step is read once, as its number, title and text, when Next or Back reaches it. The visible step counter no longer repeats it. The welcome card's question is now read with its title. +- **Focus stays where you are.** The tour opens with focus on Next and keeps it on Next or Back while the steps change; going Back to the first step moves focus to Next instead of losing it. +- **Ending the tour lands somewhere.** Done, Escape and the close button, on a first run or a replay, return focus to the Help button (to the overflow button when Help is folded into it, to More on a phone), never to the page itself. + +**No migration.** One new spoken sentence and three release-note lines in 18 languages, 16 of them without native review. The informational `meta.tool` string is now `loop-studio/0.18.2`. + ## v0.18.1 — 2026-10-06 A fix release (issue #307): the menus answer the keyboard the same way, and the phone's sheets take focus and step back one level at a time. diff --git a/README.md b/README.md index 94ca1e99..2b4336bc 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,16 @@ Additional feature-specific design documents (localization, mobile, module system, large-graph readability, simulation playback, edge routing, data import, …) live under [`docs/`](docs/). -## Latest — v0.18.1 +## Latest — v0.18.2 + +A fix release: the guided tour says each step once. + +- **Each step is announced once**: step 1 as the tour opens, every later step as Next or + Back reaches it, with its number, title and text +- **Focus stays on Next or Back** while the steps change, and ending the tour returns it + to the Help button (More on a phone) + +## v0.18.1 A fix release: the menus answer the keyboard the same way. @@ -168,16 +177,8 @@ A fix release: the Temporary session button looks like the menu buttons beside i - **The same height, corners, text size and colours** as the toolbar's menu buttons, and their hover and keyboard focus; its orange border still marks a temporary session -## v0.17.1 - -A fix release: share links use the browser's own compression. - -- **Share links are compressed with the browser's built-in Compression Streams**; the - bundled compression code is removed. Existing links still open and the link format is - unchanged -- **A browser without them** makes no link and says so, and the open diagram is kept - -See [`CHANGELOG.md`](CHANGELOG.md) for the full notes of these releases, v0.17.0 +See [`CHANGELOG.md`](CHANGELOG.md) for the full notes of these releases, v0.17.1 (share +links compressed with the browser's own Compression Streams), v0.17.0 (password-protected share links), v0.16.0 (the storage gate, temporary sessions and the Storage and privacy area), the v0.15 releases and every earlier one. diff --git a/docs/guided-tour.md b/docs/guided-tour.md index b5f182a7..ad61455a 100644 --- a/docs/guided-tour.md +++ b/docs/guided-tour.md @@ -162,11 +162,46 @@ menu mid-tour, so this is not a user-reachable flow in this slice — the E2E - `Back` / `Next` buttons; `Next` on step 6 is `Done` and ends the tour (→ `completed`, written **on the `Done` press**, not on merely reaching step 6). -- keyboard focus is **trapped** inside the popover while the tour is open; on - end, focus returns to the control that opened it (the Welcome card's - `Start tour`, or the Help menu's `Take a tour`). +- keyboard focus is **trapped** inside the popover while the tour is open. - the popover is a labelled dialog (`role="dialog"`, `aria-modal`, - `aria-labelledby` the step title); the `N / 6` position is announced. + `aria-labelledby` the `N / 6` position and the step title, + `aria-describedby` the step body). + +**Announcement and focus (issue #308, Lumi 2026-10-06).** Each step is said +**once**, and focus is never lost: + +- **Step 1** is said by the dialog's name (its `1 / 6` and its title, so the + number is not lost) and description (its body) when focus enters it, all + three once. Focus enters on **`Next`**. +- **Every later step** is said by one hidden live region inside the popover + (`aria-live="polite"`, `aria-atomic="true"`), written **once per `Next` or + `Back` press**, as `N / 6. title. body`. The sentence is its own catalog key + (`tour.nav.announce`) in every language, so the punctuation and order are + the language's own, not English joined in code. The region starts empty, so + step 1 is not said twice; a re-render that is not a step change (a language + switch) writes nothing. +- The visible `N / 6` is **not** live, and neither is the title, so neither + repeats the announcement. A step read twice (title change and live region + together) is a failure, not a pass. +- Focus stays on `Back` / `Next` while the steps change in place. On `Back` to + step 1, focus moves to `Next` **before** `Back` is disabled (`disabled`, not + `aria-disabled`); otherwise it falls to `` inside the dialog + (MEASURED on v0.18.1). +- **Every exit** (`Done`, `Escape`, the close control), first run or replay, + lands on the **Help** button, which is where a replay starts: the + overflow button when the toolbar has folded Help into it, the **More** + button on a phone. A target that is gone, hidden, inert or disabled is + skipped for the next. Only when none of them is usable, the last resort is + the top bar's first usable menu button outside the palette (opening a menu + is harmless; Enter on a palette piece would insert it). Never ``. +- **The Welcome card** is named by its title and described by its question + (`aria-describedby`); its buttons, their order and the first focus (`Skip`) + are unchanged. +- The phone tour has the same contract; its layout is unchanged. +- Merge condition: a real check with Windows Narrator in desktop Edge and + desktop Chrome. A real mobile screen reader is **not** a merge condition and + is recorded as unverified; the phone is checked by automated focus and + accessibility-tree tests (`e2e/tour-announcement.spec.ts`). - every string (titles, bodies, `Back` / `Next` / `Done` / `Skip`, the `N / 6` template) comes from the catalog (§GT8) — nothing hard-coded. @@ -463,6 +498,11 @@ and KO `satisfies MessageCatalog`, e.g.: `tour.welcome.skip` - `tour.nav.back` · `tour.nav.next` · `tour.nav.done` · `tour.nav.position` = `"{n} / {total}"` +- `tour.nav.announce` = `"{n} / {total}. {title}. {body}"` in English — the + step's spoken sentence (§GT4, issue #308), worded per language: `。` in + Japanese and Chinese, a space in Thai, the full stop elsewhere. `{title}` + and `{body}` are the step's own catalog strings. It is never shown, so it + needs no direction pinning. - `tour.desktop.{t('tour.welcome.body')}
++ {t('tour.welcome.body')} +