'use client' import * as React from 'react' import { Info } from 'lucide-react' import { Button } from './button' import { FloatingTooltip } from './floating-tooltip' import { cn } from '../../utils/cn' export interface InfoHintProps { children: React.ReactNode /** What this hint is ABOUT — 15 buttons all named "More information" name nothing. */ label?: string side?: 'top' | 'right' | 'bottom' | 'left' className?: string } /** * The "what does this mean?" affordance — an ⓘ icon that reveals a definition * on hover. * * Config-heavy screens carry vocabulary that is obvious to whoever built the * feature and opaque to everyone else — `judged`, `deterministic`, `observe`, * `error`. A term nobody can define is a term nobody configures correctly, so * every one of them gets a hint rather than a wiki page somebody has to go find. * * This is a thin cap over `FloatingTooltip`, not a second tooltip: it fixes the * icon, the trigger element and the sizing so every hint looks and behaves the * same. All the hover/portal/placement behaviour is the shared component's. */ export function InfoHint({ children, label, side = 'top', className }: InfoHintProps) { const describedById = React.useId() return ( {/* `icon-inline` is a Button variant, not a className stack. The hint sits inside a line of badges, so `icon-sm`'s 32px target would swamp a 10px badge — and shrinking it at the call site is exactly the override the house rule forbids. */} {/* The tooltip opens on HOVER only — `FloatingTooltip` composes `useHover`/`useDismiss`/`useRole` and no `useFocus`, so a keyboard user tabbing here would otherwise reach a button that reveals nothing. This visually-hidden copy is the same text, exposed via `aria-describedby`, so assistive tech reads the definition without depending on hover. */} {children} ) }