stream/ui
Browse the library

Comic Tooltip

A speech-bubble tooltip with a tail pointing at its trigger, that pops in with a bounce.

betaComicTooltip.tsx

Examples

Tones and sides

Hover or tab to each button.

comic-tooltip/basic.tsx
import { Bell, Settings, Share2 } from "lucide-react";
import { Button, ComicTooltip } from "@streamuilib/react";

export default function ComicTooltipBasic() {
  return (
    <div className="flex gap-5 pt-12 pb-12">
      <ComicTooltip content="Ding! New stuff.">
        <Button size="icon" variant="secondary" aria-label="Notifications">
          <Bell aria-hidden />
        </Button>
      </ComicTooltip>
      <ComicTooltip content="Share the love!" tone="lime">
        <Button size="icon" variant="secondary" aria-label="Share">
          <Share2 aria-hidden />
        </Button>
      </ComicTooltip>
      <ComicTooltip content="Tweak things" tone="pink" side="bottom">
        <Button size="icon" variant="secondary" aria-label="Settings">
          <Settings aria-hidden />
        </Button>
      </ComicTooltip>
    </div>
  );
}

Installation

bun add @streamuilib/react
Open in v0 (opens in a new tab)
usage.tsx
import { ComicTooltip } from "@streamuilib/react";

<ComicTooltip content="Share the love!">
  <button aria-label="Share">...</button>
</ComicTooltip>

API

PropTypeDefaultDescription
contentReactNode—The hint. Keep it short.
childrenReactElement—One focusable trigger, e.g. an icon button.
tone"paper" | "lime" | "pink" | "blue" | "orange""paper"Bubble fill.
side"top" | "bottom""top"Where the bubble sits.
delaynumber150Hover delay in ms. Focus shows it right away.

Accessibility

  • Built on Tooltip: it shows on hover and keyboard focus, and hides on Esc, blur, or pointer leave (WCAG 1.4.13).
  • The trigger gets aria-describedby, so screen readers read the hint after its name.
  • The pop-in stops when the user prefers reduced motion.

Source

packages/react/src/ComicTooltip.tsx
"use client";

import { cn } from "./cn";
import { Tooltip, type TooltipProps } from "./Tooltip";

export type ComicTooltipProps = TooltipProps & {
  /** Bubble fill. */
  tone?: "paper" | "lime" | "pink" | "blue" | "orange";
};

const fills = {
  paper: "bg-paper-raised text-ink [--sui-tip:var(--paper-raised)]",
  lime: "bg-lime text-on-bright [--sui-tip:var(--lime)]",
  pink: "bg-pink text-on-bright [--sui-tip:var(--pink)]",
  blue: "bg-blue text-on-bright [--sui-tip:var(--blue)]",
  orange: "bg-orange text-on-bright [--sui-tip:var(--orange)]",
};

/**
 * A comic-book tooltip: a speech bubble with a tail pointing at its trigger,
 * that pops in with a little bounce. The same behavior as Tooltip: hover and
 * keyboard focus show it, Esc, blur, or pointer leave hide it (WCAG 1.4.13),
 * and the trigger gets aria-describedby. Motion stops for reduced motion.
 */
export function ComicTooltip({ tone = "paper", side = "top", className, ...props }: ComicTooltipProps) {
  return (
    <Tooltip
      side={side}
      className={cn(
        "sui-comic-tip rounded-xl border-[3px] px-3 py-1.5 font-sans text-sm font-bold shadow-[3px_3px_0_var(--ink)]",
        side === "top" ? "sui-comic-tip-top mb-4" : "sui-comic-tip-bottom mt-4",
        fills[tone],
        className,
      )}
      {...props}
    />
  );
}