0027. Help mode: point at anything to see what it is and what to do
- Status: Accepted
- Date: 2026-09-29
Context
On Learn, the owner said it would be good to have training, but there should be a mode on each page that you can switch on, and when you point at something it says what it is and what to do. Having that on every page would help people understand what is going on. An earlier design sketched it: a “?” button beside Report, one script, one registry that reuses the glossary, keys carried by the shared parts, and a build check for missing entries. Decision 0026 had just put every page on the same shared parts, which is what makes “every page” affordable: help written once for a filter bar, a band or a side panel is help on every page that uses one.
Decision
- A Help switch in the top bar, the round ? beside Report. It sets
html[data-help="on"]. The ? key toggles it (not while typing), Escape turns it off (after anything else Escape would close), and the choice is remembered per window (session storage, put back on every page swap by the Shell’s boot script, so it survives the client router). - Anything can carry
data-help="<key>". With help on, everything that has help gets a subtle dotted outline, so people can see what they can ask about. Hover or keyboard focus shows a small card in the Peek look, from its origin, with tokens only: a heading, “What it is”, “What to do” and a “Learn more” link into Learn. On a touch screen the first tap on something with help explains it and the second acts (a link or button inside a big block that has help, such as a link in a side panel, still acts on the first tap). Peek’s own hover cards step aside for anything that has help while help is on, and a filter pill opens on click, not hover, so the two do not fight. - One delegated listener and one registry.
src/components/HelpMode.astro(rendered by the Shell) holds the listener, the card and the outlines.src/lib/help.mjsmaps keys to{ name, what, do, learn }. The build turns it into/help/index.json, fetched the first time help goes on, so pages do not each carry every card. A key with a colon is a family (number:Rooms,place:work,filter.facet:region): it uses its own entry, else its family’s, and inherits the family’s “what to do”.term:<id>is any glossary word, built from the glossary itself, so a term has one definition everywhere.page.<section>is the page itself, from the Shell’ssection; the band’s title area asks for it withdata-help="page".data-help-namegives a thing its own heading (a tab, a facet, a number). - The shared parts carry their own keys: the Shell (the path, Search, Help, Report, colour mode, Settings, View as, each place, each tab, the page),
PageBand,KeyNumbers(one entry per figure label),SimTag,FilterBar(find, each facet, switches, Clear, views, Sort, the count),DetailTabs,SidePanel,Sectionand Show all,EmptyState,PhaseRail,RoomKey,Raci,LiveBarand History, and the Integrate checklist.Section,SidePanelandFilterBartake ahelp(orviewsHelp) prop for a block with words of its own. Pages add keys only for widgets they alone have: Home, the Work overview, the Schedule, Integrate, an incident, a room, a unit and the Team page. - A build-time check.
tests/help.test.mjslists everydata-helpkey used insrc/(and, whendist/exists, in the built pages) that has no registry entry, and fails if there is one. It also fails for a registry key nothing uses, a page section, place or tab without its own card, a “Learn more” that does not resolve, and an em dash in the words. - Learn gains a first lesson, “Switch on help”, with a real try (switch it on, then point at three different things), and a glossary word, Help. Every card that has a lesson or a glossary entry to learn from links to it.
- Who a change is recorded as, in Everyone (overview). Nobody in particular is signed in there, so a change made there (a tick, an assignment, a proposed change, a report) is recorded as a clearly labelled demo admin, “Demo admin (Everyone view)”, never as “everyone” or as the first person on the list. One helper,
window.rsActorId(), gives the person a change belongs to;rsWhoId()still says who the window is viewing as. Everyone is also in the Settings person picker, for phones. rsFilter.set(key, values)on theFilterBarsets a facet to exactly those choices (or several at once with an object), so the Work overview’s matrix uses it instead of clicking the options.
Consequences
- A new page is help-ready as soon as it uses the shared parts; the checklist in
docs/standards/page-anatomy.mdadds one line for a widget of its own. Rule P9 in the platform chapter says so. - The dotted outlines are only there while help is on, and the cards use the existing Peek tokens and motion, so reduced motion is respected (the card does not animate, and outlines change at once).
- Cards on client-drawn content (the Integrate checklist rows, the unit page’s cards, the Schedule’s chips) carry keys written into their templates. A page that draws new widgets in a script has to put
data-helpin the markup it writes. - In help mode, a page’s own hover previews for things that also have a help key (the Overview matrix’s counts, the Schedule’s chips) give way to the help card; switching help off brings them back.
- Help is words in one file. If a card is wrong, or a page says something the card contradicts, the fix is the entry in
src/lib/help.mjs. - Not done: dialogs that open in the browser’s top layer (Settings, Report, Search) do not show cards, because the card sits below them. Their own labels and the lessons cover them for now.