Every disabled Save in the hub says why

Onboarding module pages: each disabled button's tooltip names its own gate

5 Oct 2026 · branch fix/onboarding-hub-disabled-tooltips-p2 · captured against propflow-stage · at a0cbb37b115+local

Data note. Every name and balance below comes from propflow-stage, which is anonymized by construction (ADR-0097/0110 — phones in the impossible +1000 NPA, emails @example.test). The identical capture against prod would carry real tenants' names and balances and is not safe to attach anywhere, which is why the generator refuses to run against one.

1 · Before (main): a disabled Save, no explanation

On main, hovering the ring list's Save shows the "no" cursor and nothing else. The button is disabled because nothing has changed yet, but the page never says so. (The before server runs main without parts 1 and 2.)

Asserted in the captured DOM — Calls Clara can't help with forward to

Before (main): a disabled Save, no explanation
Provenance. Captured from commit 5ad31bedc31, read from the worktree the server was started from (/Users/miniclaw/code/PropFlow/wt-tips-before) — the server could not report its own commit, so this is the weaker of the two sources. That commit is already merged into origin/main.
Sections 2–8

2 · After, desktop light: unchanged draft

The same hover now says why: the draft is what is already stored. The reason comes from the same picksChanged check that disables the button.

Asserted in the captured DOM — Nothing to save yet: change the answer first

After, desktop light: unchanged draft

3 · After, desktop dark: the office line

The catch-all line's Save waits for a number. Its tooltip names that gate (line.trim() === '') rather than a generic message.

Asserted in the captured DOM — Type the office line first

After, desktop dark: the office line

4 · After, keyboard: Tab reaches the disabled Save

A disabled button takes no focus, so its wrapper is the focus stop. The capture clicks the office-line field, presses Tab once, and the tooltip shows with no mouse involved.

Asserted in the captured DOM — Type the office line first

After, keyboard: Tab reaches the disabled Save

5 · After, phone light: a tap opens it

At 390px a tap on the disabled Save opens the same reason.

Asserted in the captured DOM — Type the office line first

After, phone light: a tap opens it

6 · After, phone dark

The same tap in dark mode.

Asserted in the captured DOM — Nothing to save yet: change the answer first

After, phone dark

7 · After: quick-answer chips keep their row

The quick-answer chips each sit in a wrapper now. The row still wraps the way it did, and the pet rules' Save explains itself on hover.

Asserted in the captured DOM — No pets · Cats and dogs, up to 2 · Nothing to save yet: change the answer first

After: quick-answer chips keep their row

8 · After: a renewal first answer names what is missing

With nothing stored yet, the offer terms' Save waits for a whole first answer: a new-rent rule picked and every field filled (firstAnswerWhole). The tooltip says exactly that.

Asserted in the captured DOM — Pick a new-rent rule and fill in every field first

After: a renewal first answer names what is missing
PropFlow Docs