You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
New operators — especially non-technical farm workers filling out Daily Entry on a barn phone — currently learn the app with no in-app guidance beyond the static Help page. A self-guided, in-app tour of the core egg loop (Daily Entry → Submit → Egg Lots → Stock → Sales) would shorten that ramp without requiring a human walkthrough.
This is currently a feature idea being scoped, not committed work — filing to track the idea and the design decisions it needs, not to lock in an implementation.
Don't implement this before the lazy-loading work lands. Reasoning:
Bundle discipline: the SPA's main chunk is already past Vite's size-warning threshold (see SPA: route-based code splitting — all 25 pages ship eagerly in one chunk #595). A tour library — even the lightest option — should not land as more always-eager weight on top of that; it needs to be dynamically imported behind a "Take a tour" trigger so only users who actually use it pay for it.
SPA: route-based code splitting — all 25 pages ship eagerly in one chunk #595 establishes this codebase's first React.lazy()/dynamic-import pattern (Suspense boundaries, PWA precache verification, coverage-threshold behavior under lazy-loaded code). This feature should reuse that pattern rather than being the one to invent it.
Library research (already done)
Measured actual npm package sizes (min+gzip, peer deps excluded) for the common options:
Recommendation: driver.js, dynamically imported — smallest, no transitive dependency surprises, and being framework-agnostic means it doesn't fight React's render cycle for step positioning. Open to a custom-built alternative if driver.js's DOM-highlighting approach turns out to conflict with the app's inert-based accessibility handling (see below) — the size gap between "smallest library" and "custom" is small enough that this shouldn't be decided on bundle size alone.
Constraints this must satisfy (from repo conventions — not optional)
i18n: every tour-step string must route through src/i18n's en/es/tl catalogs — the SPA E2E suite enforces "never hardcode English" via the app's own translation catalogs, and scripts/i18n-scan.mjs should catch a missed key.
Accessibility: the app has deliberate inert-based modal/overlay handling (PWA/shell: verify the inert-swallowed announcements with a real screen reader #501, still pending a real screen-reader verification pass). A coachmark/spotlight overlay is exactly the kind of UI that can silently break that — Playwright's ariaSnapshot/getByRole/isVisible do not model inert (per docs/decisions/277-spa-e2e.md), so this needs verification through the CDP-based tools/simulation/ui/src/ax.ts approach, not the usual Playwright accessibility queries.
RBAC: Owner/Manager/Worker/Sales see different screens and permissions; a one-size-fits-all tour will reference UI a Worker can't see. Needs at least a decision on whether v1 ships role-agnostic (covering only the universally-visible core loop) or role-scoped from the start.
Never trap the user. Skip/dismiss must always be available and the tour must be re-launchable on demand (natural home: the Help page, alongside the planned in-app glossary from F18: In-app help — user guide + glossary in the SPA #71).
Open product questions (deliberately not decided here)
Auto-suggest on first login (conceptually a sibling to Report first-run 'no accounts yet' from the login endpoint instead of a polled endpoint #361's first-run discoverability work) vs. opt-in only from Help. Given the target users are on a clock in a barn, auto-launching an unrequested tour could be actively unwelcome — leaning toward opt-in-only for v1, but this is a product call.
Ship one role-agnostic core-loop tour first, or scope multiple role-specific tours from day one.
Whether this belongs in Phase 1.5 (where it's being filed, alongside other carryover UX items) or should wait for Phase 1.6 (multi-farm tenancy, epic EPIC: Phase 1.6 — Multi-farm tenancy #530) to settle, since a tour's content may need to account for farm-switching UI that EPIC: Phase 1.6 — Multi-farm tenancy #530 is actively adding.
Proposed initial scope (small slice, if greenlit)
driver.js behind a dynamic import(), triggered from a new "Take a tour" entry on the Help page.
One tour: the core egg loop (Daily Entry → Submit → Egg Lots → Stock → Sales) — the app's Phase 1.0 MVP flow, so it's role-agnostic-enough to ship first.
Step copy in en/es/tl.
Vitest coverage for trigger/dismiss/step-advance logic; an ax.ts-based accessibility check that the overlay doesn't break inert handling elsewhere on the page.
Acceptance criteria
A user can start, step through, skip, and re-launch the tour without it blocking any other interaction.
Tour code is not in the SPA's initial eager payload (network tab / build output shows it as a separate chunk, only fetched on trigger).
Tour copy renders in en/es/tl with no missing-key fallback.
Motivation
New operators — especially non-technical farm workers filling out Daily Entry on a barn phone — currently learn the app with no in-app guidance beyond the static Help page. A self-guided, in-app tour of the core egg loop (Daily Entry → Submit → Egg Lots → Stock → Sales) would shorten that ramp without requiring a human walkthrough.
This is currently a feature idea being scoped, not committed work — filing to track the idea and the design decisions it needs, not to lock in an implementation.
Depends on #595
Don't implement this before the lazy-loading work lands. Reasoning:
React.lazy()/dynamic-import pattern (Suspense boundaries, PWA precache verification, coverage-threshold behavior under lazy-loaded code). This feature should reuse that pattern rather than being the one to invent it.Library research (already done)
Measured actual npm package sizes (min+gzip, peer deps excluded) for the common options:
scroll,scrollparent,@floating-ui/react-dom, …)deepmerge-ts,@floating-ui/dom(bundled)hintssubmodule)Recommendation: driver.js, dynamically imported — smallest, no transitive dependency surprises, and being framework-agnostic means it doesn't fight React's render cycle for step positioning. Open to a custom-built alternative if driver.js's DOM-highlighting approach turns out to conflict with the app's
inert-based accessibility handling (see below) — the size gap between "smallest library" and "custom" is small enough that this shouldn't be decided on bundle size alone.Constraints this must satisfy (from repo conventions — not optional)
src/i18n's en/es/tl catalogs — the SPA E2E suite enforces "never hardcode English" via the app's own translation catalogs, andscripts/i18n-scan.mjsshould catch a missed key.inert-based modal/overlay handling (PWA/shell: verify the inert-swallowed announcements with a real screen reader #501, still pending a real screen-reader verification pass). A coachmark/spotlight overlay is exactly the kind of UI that can silently break that — Playwright'sariaSnapshot/getByRole/isVisibledo not modelinert(perdocs/decisions/277-spa-e2e.md), so this needs verification through the CDP-basedtools/simulation/ui/src/ax.tsapproach, not the usual Playwright accessibility queries.Open product questions (deliberately not decided here)
Proposed initial scope (small slice, if greenlit)
import(), triggered from a new "Take a tour" entry on the Help page.ax.ts-based accessibility check that the overlay doesn't breakinerthandling elsewhere on the page.Acceptance criteria
inert/accessibility-tree contract PWA/shell: verify the inert-swallowed announcements with a real screen reader #501 established.