{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tooltip",
  "title": "Tooltip",
  "description": "A whisper in parentheses above the thing it names.",
  "dependencies": [
    "@radix-ui/react-tooltip"
  ],
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/tooltip.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } from \"@/lib/utils\"\n\n/** Holds a still pointer for 400ms before whispering; focus shows it at once. */\nfunction TooltipProvider({ delayDuration = 400, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n  return <TooltipPrimitive.Provider data-slot=\"tooltip-provider\" delayDuration={delayDuration} {...props} />\n}\n\nfunction Tooltip(props: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n  return (\n    <TooltipProvider>\n      <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n    </TooltipProvider>\n  )\n}\n\nfunction TooltipTrigger(props: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n  return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />\n}\n\ntype TooltipContentProps = React.ComponentProps<typeof TooltipPrimitive.Content> & { variant?: \"whisper\" | \"initials\" | \"beside\" }\n\n/**\n * A whisper. Keep it to a few words; it names, it doesn't explain. whisper (the default): in\n * parentheses above the thing it names. initials: the same, with the letters the control shows\n * marked in the words they come from (wrap each in <b>). beside: on the control's own line, after it,\n * on a hairline.\n */\nfunction TooltipContent(props: TooltipContentProps) {\n  return (\n    <TooltipPrimitive.Portal>\n      <Placed {...props} />\n    </TooltipPrimitive.Portal>\n  )\n}\n\n// Beside reads on from the control, so it sits at the line's end: the right, or the left on a\n// right-to-left page. The portal mounts this only once open, in the browser, so the direction is read\n// fresh each time and can't disagree with the server's (closed, empty) render.\n// ponytail: reads the page's direction, not the trigger's; pass side for a mixed-direction page.\nfunction Placed({ className, variant = \"whisper\", side, sideOffset = variant === \"beside\" ? 10 : 8, ...props }: TooltipContentProps) {\n  const rtl = variant === \"beside\" && !side && getComputedStyle(document.body).direction === \"rtl\"\n  return (\n    <TooltipPrimitive.Content\n      data-slot=\"tooltip-content\"\n      data-variant={variant === \"whisper\" ? undefined : variant}\n      side={side ?? (variant === \"beside\" ? (rtl ? \"left\" : \"right\") : \"top\")}\n      sideOffset={sideOffset}\n      className={cn(\"ot-tip-text\", className)}\n      {...props}\n    />\n  )\n}\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/tooltip.css",
      "content": "@layer components {\n  /* ── ot-tip ── tooltip: a whisper in parentheses above the thing it names. The parentheses are set\n     thin and taller than the words, as 20(25) sets them round its figures, so the whisper reads as\n     an aside held apart from the page. Radix positions it (side top, 8px off) and holds it for a\n     still pointer; focus shows it at once and Escape puts it away. */\n  .ot-tip-text {\n    z-index: 50; width: max-content; max-width: 32ch; padding: 0.1em 0.35em; background: var(--ot-paper);\n    font-family: var(--ot-voice); font-size: var(--ot-pp); line-height: var(--ot-pp-lh); color: var(--ot-graphite); text-align: center; white-space: normal;\n    animation: ot-tip-in var(--ot-moderato) var(--ot-exhale);\n  }\n  .ot-tip-text::before { content: \"(\" / \"\"; }\n  .ot-tip-text::after { content: \")\" / \"\"; }\n  .ot-tip-text::before, .ot-tip-text::after { font-size: 1.6em; font-weight: 200; line-height: 0; vertical-align: -0.12em; color: var(--ot-ink); }\n  .ot-tip-text::before { margin-inline-end: 0.08em; }\n  .ot-tip-text::after { margin-inline-start: 0.08em; }\n  .ot-tip-text[data-state=\"closed\"] { animation: ot-tip-out var(--ot-allegro) var(--ot-breath) forwards; }\n  @keyframes ot-tip-in { from { opacity: 0; translate: 0 0.3em; } }\n  @keyframes ot-tip-out { to { opacity: 0; } }\n\n  /* ── initials ── for a key or an abbreviation: the letters the control shows are found in the\n     whisper's words and reversed out of the ink, as \"the uncreative\" reverses part of its word, so\n     you can see where G, or CMYK, comes from. Mark each with <b>. The initials land first and the\n     words are written in around them. */\n  .ot-tip-text[data-variant=\"initials\"] b { display: inline-block; padding-inline: 0.1em; font-weight: 700; background: var(--ot-ink); color: var(--ot-paper); }\n  .ot-tip-text[data-variant=\"initials\"][data-state=\"delayed-open\"],\n  .ot-tip-text[data-variant=\"initials\"][data-state=\"instant-open\"] { animation: ot-tip-write var(--ot-moderato) var(--ot-exhale) var(--ot-allegro) backwards; }\n  .ot-tip-text[data-variant=\"initials\"]:is([data-state=\"delayed-open\"], [data-state=\"instant-open\"]) b { animation: ot-tip-land var(--ot-moderato) var(--ot-spiccato) backwards; }\n  @keyframes ot-tip-write { from { color: transparent; } }\n  @keyframes ot-tip-land { from { opacity: 0; scale: 0.4; } }\n\n  /* ── beside ── the whisper on the control's own line, after it, on a hairline that runs from the\n     control: the small notes of \"It has to be design.\" hung beside the headline. No parentheses: the\n     line holds it apart. The line always faces the control, whichever side Radix settles on. */\n  .ot-tip-text[data-variant=\"beside\"] { display: flex; align-items: center; gap: var(--ot-space-2); max-width: 28ch; padding-inline: 0 0.35em; text-align: start; }\n  .ot-tip-text[data-variant=\"beside\"]::after { content: none; }\n  .ot-tip-text[data-variant=\"beside\"]::before { content: \"\"; flex: none; width: var(--ot-space-5); height: var(--ot-hairline); margin: 0; background: var(--ot-ink); }\n  .ot-tip-text[data-variant=\"beside\"][data-side=\"left\"] { flex-direction: row-reverse; padding-inline: 0.35em 0; }\n  .ot-tip-text[data-variant=\"beside\"][data-side=\"left\"]:is([dir=\"rtl\"], [dir=\"rtl\"] *) { flex-direction: row; padding-inline: 0.35em 0; }\n  .ot-tip-text[data-variant=\"beside\"][data-side=\"right\"]:is([dir=\"rtl\"], [dir=\"rtl\"] *) { flex-direction: row-reverse; padding-inline: 0 0.35em; }\n  .ot-tip-text[data-variant=\"beside\"]:is([data-side=\"top\"], [data-side=\"bottom\"])::before { display: none; } /* flipped above or below: no line to run along */\n  /* Reach: the hairline draws out from the control, then the words arrive at its end. */\n  .ot-tip-text[data-variant=\"beside\"]:is([data-state=\"delayed-open\"], [data-state=\"instant-open\"]) { animation: ot-tip-write var(--ot-moderato) var(--ot-exhale) var(--ot-allegro) backwards; }\n  .ot-tip-text[data-variant=\"beside\"]:is([data-state=\"delayed-open\"], [data-state=\"instant-open\"])::before { animation: ot-tip-reach var(--ot-moderato) var(--ot-exhale) backwards; }\n  .ot-tip-text[data-variant=\"beside\"][data-side=\"right\"]::before { transform-origin: 0 50%; }\n  .ot-tip-text[data-variant=\"beside\"][data-side=\"left\"]::before { transform-origin: 100% 50%; }\n  @keyframes ot-tip-reach { from { scale: 0 1; } }\n\n  @media (forced-colors: active) {\n    .ot-tip-text[data-variant=\"beside\"]::before { forced-color-adjust: none; background: CanvasText; }\n    .ot-tip-text[data-variant=\"initials\"] b { forced-color-adjust: none; background: CanvasText; color: Canvas; }\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/tooltip.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/tooltip.css\"": {}
  },
  "meta": {
    "movement": "IX",
    "underneath": "radix",
    "contract": "ot-tip"
  },
  "type": "registry:ui"
}
