import type { FC, JSX, PropsWithChildren } from 'hono/jsx'
import { cn } from '@/lib/utils'
type Side = 'top' | 'right' | 'bottom' | 'left'
type TooltipProps = PropsWithChildren<{
class?: string
}>
type TooltipTriggerProps = JSX.IntrinsicElements['div']
type TooltipContentProps = JSX.IntrinsicElements['div'] & {
side?: Side
}
// Tooltip wrapper - contains trigger and content
export const Tooltip: FC<TooltipProps> = ({
class: className,
children,
}) => (
<div
data-slot="tooltip"
class={cn('group/tooltip relative inline-flex', className)}
>
{children}
</div>
)
// TooltipTrigger - the element that triggers the tooltip
export const TooltipTrigger: FC<TooltipTriggerProps> = ({
class: className,
children,
...props
}) => (
<div
data-tooltip-trigger
class={cn('inline-flex', className)}
{...props}
>
{children}
</div>
)
// TooltipContent - the tooltip popup content
export const TooltipContent: FC<TooltipContentProps> = ({
side = 'top',
class: className,
children,
...props
}) => (
<div
data-tooltip-content
data-side={side}
data-state="closed"
role="tooltip"
class={cn(
'absolute z-50 hidden overflow-hidden rounded-md bg-popover px-3 py-1.5 text-xs text-foreground shadow',
'whitespace-nowrap',
side === 'top' && 'bottom-full left-1/2 -translate-x-1/2 mb-2',
side === 'bottom' && 'top-full left-1/2 -translate-x-1/2 mt-2',
side === 'left' && 'right-full top-1/2 -translate-y-1/2 mr-2',
side === 'right' && 'left-full top-1/2 -translate-y-1/2 ml-2',
'group-hover/tooltip:block',
className
)}
{...props}
>
{children}
</div>
)
Installation
Initialize your project
First time only. Sets up config and installs base dependencies.
npx @kiwa-ui/cli initAdd the component
This will install the component and any dependencies it needs.
npx @kiwa-ui/cli add tooltipInstall dependencies
Add the required npm packages.
pnpm add @kiwa-ui/enhanceAdd the source file
Add this file to your project.
import type { FC, JSX, PropsWithChildren } from 'hono/jsx'
import { cn } from '@/lib/utils'
type Side = 'top' | 'right' | 'bottom' | 'left'
type TooltipProps = PropsWithChildren<{
class?: string
}>
type TooltipTriggerProps = JSX.IntrinsicElements['div']
type TooltipContentProps = JSX.IntrinsicElements['div'] & {
side?: Side
}
// Tooltip wrapper - contains trigger and content
export const Tooltip: FC<TooltipProps> = ({
class: className,
children,
}) => (
<div
data-slot="tooltip"
class={cn('group/tooltip relative inline-flex', className)}
>
{children}
</div>
)
// TooltipTrigger - the element that triggers the tooltip
export const TooltipTrigger: FC<TooltipTriggerProps> = ({
class: className,
children,
...props
}) => (
<div
data-tooltip-trigger
class={cn('inline-flex', className)}
{...props}
>
{children}
</div>
)
// TooltipContent - the tooltip popup content
export const TooltipContent: FC<TooltipContentProps> = ({
side = 'top',
class: className,
children,
...props
}) => (
<div
data-tooltip-content
data-side={side}
data-state="closed"
role="tooltip"
class={cn(
'absolute z-50 hidden overflow-hidden rounded-md bg-popover px-3 py-1.5 text-xs text-foreground shadow',
'whitespace-nowrap',
side === 'top' && 'bottom-full left-1/2 -translate-x-1/2 mb-2',
side === 'bottom' && 'top-full left-1/2 -translate-x-1/2 mt-2',
side === 'left' && 'right-full top-1/2 -translate-y-1/2 mr-2',
side === 'right' && 'left-full top-1/2 -translate-y-1/2 ml-2',
'group-hover/tooltip:block',
className
)}
{...props}
>
{children}
</div>
)
Usage
import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
<Tooltip>
<TooltipTrigger>Hover me</TooltipTrigger>
<TooltipContent side='top'>Helpful tip</TooltipContent>
</Tooltip>Interactivity
This component is SSR-first and works without client JavaScript. Add @kiwa-ui/enhance for interactive behavior like toggling, keyboard navigation, and ARIA state management.
Add to your layout
<script type="module">
import { tooltip } from '@kiwa-ui/enhance'
tooltip()
</script>Tooltips do not appear — content is hidden and there is no hover affordance. Consider providing the same information inline (e.g. via `aria-label`) if the tooltip is the only source.
Tooltips appear on hover and on keyboard focus with a short delay, wired through `aria-describedby` so screen readers announce them.