+
+ Tooltip System Demo
+
+
+ Hover or focus the buttons below to see tooltips. Anomaly detection is active — rapidly
+ toggling a tooltip or keeping it open for >10 s will log an anomaly.
+
+
+ {/* Placement showcase */}
+
+
+ Placements
+
+
+ {PLACEMENTS.map((placement) => (
+ onOpen(`placement-${placement}-${type}`)}
+ >
+ onOpen(`placement-${placement}`)}
+ onBlur={() => onClose(`placement-${placement}`)}
+ onMouseEnter={() => onOpen(`placement-${placement}`)}
+ onMouseLeave={() => onClose(`placement-${placement}`)}
+ >
+ {placement}
+
+
+ ))}
+
+
+
+ {/* Rich content tooltip */}
+
+
+ Rich Content
+
+
+ Tip: This tooltip supports{' '}
+ rich React content .
+
+ }
+ placement="right"
+ delayMs={100}
+ onAnomaly={(type) => onOpen(`rich-${type}`)}
+ >
+ onOpen('rich')}
+ onBlur={() => onClose('rich')}
+ onMouseEnter={() => onOpen('rich')}
+ onMouseLeave={() => onClose('rich')}
+ >
+ Hover for rich tooltip
+
+
+
+
+ {/* Disabled tooltip */}
+
+
+ Disabled State
+
+
+
+ Disabled tooltip
+
+
+
+
+ {/* Anomaly log */}
+
+
+
+ Anomaly Log
+
+ {anomalies.length > 0 && (
+
+ Clear
+
+ )}
+
+
+ {anomalies.length === 0 ? (
+
+ No anomalies detected yet. Try rapidly toggling a tooltip!
+
+ ) : (
+
+ {anomalies.map((a, i) => (
+
+
+ [{a.type}]
+ {' '}
+
+ tooltip: {a.tooltipId}
+
+ {a.detail && (
+ — {a.detail}
+ )}
+
+ {new Date(a.timestamp).toLocaleTimeString()}
+
+
+ ))}
+
+ )}
+
+
+ );
+}
diff --git a/src/components/index.ts b/src/components/index.ts
index bc5bbf7d..7cf3a05e 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -19,3 +19,5 @@ export { ShareModal } from './ShareModal';
export * from './ui/Table';
export { BulkImporter } from './BulkImporter';
export type { BulkImporterProps, TargetFieldDef } from './BulkImporter';
+export { Tooltip } from './ui/Tooltip';
+export type { TooltipProps, TooltipPlacement } from './ui/Tooltip';
diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx
new file mode 100644
index 00000000..108bd358
--- /dev/null
+++ b/src/components/ui/Tooltip.tsx
@@ -0,0 +1,131 @@
+'use client';
+
+/**
+ * Tooltip Component
+ * Accessible, reusable tooltip with anomaly detection integration.
+ * Follows WAI-ARIA tooltip pattern (role="tooltip", aria-describedby).
+ */
+
+import React, { useState, useRef, useId, useCallback } from 'react';
+
+export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
+
+export interface TooltipProps {
+ /** The content shown inside the tooltip */
+ content: React.ReactNode;
+ /** The element that triggers the tooltip */
+ children: React.ReactElement;
+ /** Placement relative to the trigger */
+ placement?: TooltipPlacement;
+ /** Delay in ms before showing (default 200) */
+ delayMs?: number;
+ /** Whether the tooltip is disabled */
+ disabled?: boolean;
+ /** Optional extra class for the tooltip bubble */
+ className?: string;
+ /** Called when an anomaly is detected (e.g. rapid open/close) */
+ onAnomaly?: (type: string) => void;
+}
+
+const PLACEMENT_CLASSES: Record