feat(tiles): a hover card on the Tiles button says what a right-click does

Owner feedback 1: "give me the hover info to right click over the tile
button to adjust it". The only hint was the native title, which the
browser shows late, small and unstyled.

The Tiles button now has its own hover card, under it in the count
menu's panel style:

- "Tiles · N" with the remembered count (live: a pick in the menu
  changes it while the card is up), "Click: open the grid" ("close the
  grid" while it is open), "Right-click: choose 2, 4 or 6 tiles", and
  when the count does not fit the window what opens instead ("This
  window fits 4 tiles: a click opens 4"; open, what fits). Shown from
  the keyboard it adds "Shift+F10: the same menu from the keyboard".
- Shows 300 ms after a pointer that hovers rests on the button, or after
  a keyboard focus (:focus-visible); never for a touch pointer or a
  device that cannot hover (plus a CSS @media (hover: none) backstop),
  never on a hidden button, never while the count menu is open. A short
  fade on opacity and transform, none under reduced motion; it takes no
  pointer.
- Hides on pointer leave, blur, Escape, a scroll and a resize, and on a
  press, a click or a right-click on the button (capture phase, so it is
  gone before the menu or the grid opens, and it stays gone while the
  pointer rests there). openTileCountMenu hides it too, and the focus
  the menu's Escape puts back on the button brings no card back.
- It replaces the button's native title (two tooltips never stack); the
  aria-label stays and aria-describedby points at the card, which always
  exists and is kept current, its keyboard line included while hidden, so
  screen readers hear the same text without a hover. Text is diffed
  against the last English, as the rest of the grid chrome, so the zh-CN
  translator is not fought on every refresh.
- zh-CN for every line (平铺 · N, 单击, 右键单击, Shift+F10, the fits
  note); no other header button changes (its styles are .tile-hint only).

Tests: tile-grid-hint.test.ts (install and aria wiring, the delay, each
show and hide path, content per state and count, the menu rule, the
translator, the CSS); the count menu and i18n tests read the accessible
name instead of the removed title, and the i18n harvest covers the card
(F10 joins the key names that stay Latin). The vm harness gains
removeAttribute.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-10-08 17:33:26 +02:00
parent 4bb333e9bc
commit d2a1d14eac
8 changed files with 687 additions and 16 deletions
+6 -3
View File
@@ -123,17 +123,20 @@ describe('the Tiles button', () => {
app.openTileGrid(IDS.slice(0, 3));
expect(btn.getAttribute('aria-pressed')).toBe('true');
expect(btn.classList.contains('tiles-open')).toBe(true);
expect(btn.title).toBe('Tiles: back to a single session (right-click for how many tiles)');
expect(btn.getAttribute('aria-label')).toBe('Tiles: back to a single session (right-click for how many tiles)');
app.toggleTileGrid();
expect(app._tilesOwnTerminal()).toBe(false);
expect(btn.getAttribute('aria-pressed')).toBe('false');
expect(btn.title).toBe('Tiles: show several sessions side by side (right-click for how many)');
expect(btn.getAttribute('aria-label')).toBe('Tiles: show several sessions side by side (right-click for how many)');
// No native title (the hover card says it, tile-grid-hint.test.ts).
expect(btn.title).toBe('');
expect(menu()).toBeNull();
});
it('right-click opens the count menu, the click still toggles (one function with Ctrl+Shift+G)', () => {
expect(html).toContain('onclick="app.toggleTileGrid()" oncontextmenu="app.openTileCountMenu(event)"');
expect(html).toContain('title="Tiles: show several sessions side by side (right-click for how many)"');
expect(html).toContain('aria-label="Tiles: show several sessions side by side (right-click for how many)"');
expect(html).toContain('aria-describedby="tileGridHint"');
});
});