···88import * as Y from 'yjs';
99import { importKey } from '../lib/crypto.js';
1010import { EncryptedProvider } from '../lib/provider.js';
1111+import { setupTooltips } from '../lib/tooltips.js';
1112import {
1213 createWhiteboard, addShape, removeShape, removeShapes, moveShape, moveShapes,
1314 resizeShape, setShapeLabel, addArrow, removeArrow, toggleSnap, setZoom,
···21132114async function init() {
21142115 ensureArrowheadMarker();
21152116 await initCrypto();
21172117+ setupTooltips();
2116211821172119 // Push initial state to history
21182120 pushHistory();
+1-1
src/docs/index.html
···476476 function updateIcon() {
477477 var theme = getEffectiveTheme();
478478 toggle.textContent = theme === 'dark' ? '\u263E' : '\u2600';
479479- toggle.title = theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode';
479479+ toggle.setAttribute('data-tooltip', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');
480480 }
481481 toggle.addEventListener('click', function() {
482482 var current = getEffectiveTheme();
+4
src/docs/main.ts
···88import * as Y from 'yjs';
99import { Editor } from '@tiptap/core';
1010import StarterKit from '@tiptap/starter-kit';
1111+import { setupTooltips } from '../lib/tooltips.js';
1112import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
1213import { common, createLowlight } from 'lowlight';
1314import Underline from '@tiptap/extension-underline';
···27492750// Initial load of comments from yjs
27502751setTimeout(loadCommentsFromYjs, 500);
2751275227532753+// Styled tooltips
27542754+setupTooltips();
27552755+
+2
src/forms/main.ts
···88import * as Y from 'yjs';
99import { importKey, encryptString, decryptString } from '../lib/crypto.js';
1010import { EncryptedProvider } from '../lib/provider.js';
1111+import { setupTooltips } from '../lib/tooltips.js';
1112import {
1213 createForm,
1314 addQuestion,
···584585// --- Initialize ---
585586async function init() {
586587 await initCrypto();
588588+ setupTooltips();
587589588590 if (cryptoKey) {
589591 const provider = new EncryptedProvider(ydoc, docId, cryptoKey);
+1-1
src/index.html
···196196 function updateIcon() {
197197 var theme = getEffectiveTheme();
198198 toggle.textContent = theme === 'dark' ? '\u263E' : '\u2600';
199199- toggle.title = theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode';
199199+ toggle.setAttribute('data-tooltip', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');
200200 }
201201 toggle.addEventListener('click', function() {
202202 var current = getEffectiveTheme();
+35
src/lib/tooltips.ts
···11+/**
22+ * Styled tooltip system — converts title attributes to data-tooltip
33+ * for CSS-rendered tooltips instead of browser-native ones.
44+ *
55+ * Call setupTooltips() once after DOM is ready.
66+ */
77+88+const TOOLTIP_CONTAINERS = [
99+ '.toolbar', '.gdocs-toolbar', '.app-topbar',
1010+ '.diagrams-toolbar', '.slides-toolbar', '.form-builder-toolbar',
1111+];
1212+1313+/** Convert title → data-tooltip on all buttons/selects within toolbar containers */
1414+export function setupTooltips(): void {
1515+ const selector = TOOLTIP_CONTAINERS.map(c => `${c} [title]`).join(', ');
1616+ const elements = document.querySelectorAll(selector);
1717+1818+ for (const el of elements) {
1919+ const title = el.getAttribute('title');
2020+ if (title) {
2121+ el.setAttribute('data-tooltip', title);
2222+ // Keep aria-label for screen readers, remove title to prevent double tooltip
2323+ if (!el.getAttribute('aria-label')) {
2424+ el.setAttribute('aria-label', title);
2525+ }
2626+ el.removeAttribute('title');
2727+ }
2828+ }
2929+3030+ // Position topbar tooltips below (they're at the top of the page)
3131+ const topbarEls = document.querySelectorAll('.app-topbar [data-tooltip]');
3232+ for (const el of topbarEls) {
3333+ el.setAttribute('data-tooltip-pos', 'bottom');
3434+ }
3535+}
+1-1
src/sheets/index.html
···399399 function updateIcon() {
400400 var theme = getEffectiveTheme();
401401 toggle.textContent = theme === 'dark' ? '\u263E' : '\u2600';
402402- toggle.title = theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode';
402402+ toggle.setAttribute('data-tooltip', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');
403403 }
404404 toggle.addEventListener('click', function() {
405405 var current = getEffectiveTheme();