COMPONENTS/OVERLAYS

Tooltip.

A few helpful words.

In practice

Wrap the tree with TooltipProvider. Tooltips supplement an accessible name; they do not replace labels.

Installation

Install Coal once. Components and TypeScript declarations are included. React 19 and React DOM 19 are peer dependencies.

Terminal · local package
npm install http://localhost:3100/downloads/chlohal-coal-ui-0.5.0.tgz

This installs the local package. It is not yet published on npm. See installation for stylesheet setup and integration.

Usage

example.tsx
"use client";
import * as React from "react";
import "@chlohal/coal-ui/styles.css";
import { Bell } from "lucide-react";
import { Button, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "@chlohal/coal-ui";
export default function Example() {
    return (<TooltipProvider>
          <Tooltip>
            <TooltipTrigger render={<Button size="icon" variant="outline" aria-label="Notifications"/>}>
              <Bell size={16}/>
            </TooltipTrigger>
            <TooltipContent>Your notifications</TooltipContent>
          </Tooltip>
        </TooltipProvider>);
}

Source

The original Coal implementation. Use the package export to integrate it.

packages/react/src/tooltip.tsx
"use client";
import * as React from "react";
import { Action, cn, type ActionProps } from "./internal.js";
import {
  FloatingRoot,
  FloatingContent,
  useFloating,
  type FloatingContentProps,
  type FloatingProps,
} from "./floating.js";
export function TooltipProvider({ children }: { children: React.ReactNode }) {
  return <>{children}</>;
}
export function Tooltip(props: FloatingProps) {
  return <FloatingRoot {...props} />;
}
export function TooltipTrigger({
  onFocus,
  onBlur,
  onPointerEnter,
  onPointerLeave,
  onKeyDown,
  ...props
}: ActionProps) {
  const c = useFloating();
  return (
    <Action
      {...props}
      ref={(node) => {
        c.anchor.current = node;
      }}
      aria-describedby={c.open ? c.id : undefined}
      onFocus={(e) => {
        onFocus?.(e);
        if (!e.defaultPrevented) {
          c.cancelClose();
          c.set(true);
        }
      }}
      onBlur={(e) => {
        onBlur?.(e);
        c.scheduleClose();
      }}
      onPointerEnter={(e) => {
        onPointerEnter?.(e);
        if (e.pointerType !== "touch") {
          c.cancelClose();
          c.set(true);
        }
      }}
      onPointerLeave={(e) => {
        onPointerLeave?.(e);
        c.scheduleClose();
      }}
      onKeyDown={(e) => {
        onKeyDown?.(e);
        if (e.key === "Escape") {
          e.preventDefault();
          c.set(false);
        }
      }}
    />
  );
}
export function TooltipContent({
  className,
  onPointerEnter,
  onPointerLeave,
  ...props
}: FloatingContentProps) {
  const c = useFloating();
  return (
    <FloatingContent
      onPointerEnter={(e) => {
        c.cancelClose();
        onPointerEnter?.(e);
      }}
      onPointerLeave={(e) => {
        c.scheduleClose();
        onPointerLeave?.(e);
      }}
      role="tooltip"
      autoFocus={false}
      className={cn("coal-tooltip", className)}
      {...props}
    />
  );
}

Accessibility checklist

  • Provide an accessible name for controls, including icon-only buttons.
  • Check keyboard navigation, visible focus and disabled states in your application.
  • Do not rely on color alone to communicate status.