This document outlines the steps to create Tooltip
component styled withTailwind CSS and using some npm dependency libraries.
Node.js
and npm
installed on your machine.Tailwind CSS
installed in your project.CVA(class-variance-authority)
is a utility for managing CSS class names based on various conditions.clsx
is a tiny utility for constructing className strings conditionally.1// tooltip.helpers.ts
2import { cva } from "class-variance-authority";
3
4export const tooltipVariants = cva(
5 [
6 "invisible opacity-0 text-sm absolute whitespace-nowrap bg-neutral-700 text-white text-center rounded-sm px-2 py-1 z-10",
7 "transition-opacity duration-150 group-hover:visible group-hover:opacity-100",
8 "after:content-[''] after:absolute after:border-[5px] after:border-solid",
9 ],
10 {
11 variants: {
12 position: {
13 top: [
14 "bottom-[calc(100%+6px)] left-1/2",
15 "after:top-full after:left-1/2 after:-ml-[5px] after:border-t-neutral-700 after:border-b-transparent after:border-x-transparent",
16 ],
17 right: [
18 "left-[calc(100%+6px)] top-1/2",
19 "after:right-full after:top-1/2 after:-mt-[5px] after:border-r-neutral-700 after:border-l-transparent after:border-y-transparent",
20 ],
21 bottom: [
22 "top-[calc(100%+6px)] left-1/2",
23 "after:bottom-full after:left-1/2 after:-ml-[5px] after:border-b-neutral-700 after:border-t-transparent after:border-x-transparent",
24 ],
25 left: [
26 "right-[calc(100%+6px)] top-1/2",
27 "after:left-full after:top-1/2 after:-mt-[5px] after:border-l-neutral-700 after:border-r-transparent after:border-y-transparent",
28 ],
29 },
30 },
31 },
32);
33
1// tooltip.component.tsx
2import clsx from "clsx";
3import { useCallback } from "react";
4import { tooltipVariants } from "./tooltip.helpers";
5
6export interface TooltipProps {
7 title: string;
8 position?: "top" | "right" | "bottom" | "left";
9 tooltipClassName?: string;
10 className?: string;
11 width?: string | number;
12 children: React.ReactNode;
13}
14
15const Tooltip: React.FC<TooltipProps> = ({
16 title,
17 position = "top",
18 tooltipClassName,
19 className,
20 width,
21 children,
22}) => {
23 const refCallback = useCallback(
24 (el: HTMLDivElement | null) => {
25 if (!el) return;
26
27 switch (position) {
28 case "top":
29 case "bottom":
30 el.style.marginTop = "0px";
31 el.style.marginLeft = `-${el.clientWidth / 2}px`;
32 break;
33 case "left":
34 case "right":
35 el.style.marginLeft = "0px";
36 el.style.marginTop = `-${el.clientHeight / 2}px`;
37 break;
38 default:
39 break;
40 }
41 },
42 [position, title],
43 );
44
45 return (
46 <div className={cn("group relative w-fit", className)}>
47 {children}
48 <div
49 ref={refCallback}
50 style={{ width: width || "auto" }}
51 className={cn(tooltipVariants({ position }), tooltipClassName)}
52 >
53 {title}
54 </div>
55 </div>
56 );
57};
58
59export default Tooltip;
60