Components
Tooltip
A hover/focus-activated tooltip, wrapping Morphos's Tooltip primitive.
Installation
npx kosmesis add tooltippnpm dlx kosmesis add tooltipyarn dlx kosmesis add tooltipbunx kosmesis add tooltipInstall the following dependencies:
npm install @morphos/overlayspnpm add @morphos/overlaysyarn add @morphos/overlaysbun add @morphos/overlaysCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { TooltipContent as MorphosTooltipContent, type TooltipContentProps as MorphosTooltipContentProps } from "@morphos/overlays";import { cn } from "@/lib/utils";/** * `Tooltip` and `TooltipTrigger` are re-exported directly from `@morphos/overlays`. `Tooltip`'s * `render()` is a no-op Fragment and it's always instantiated directly * (`@State() tooltip = new Tooltip()`) rather than mounted via JSX, so wrapping it in a new * component class here would break `.isOpen`/`.show()`/`.hide()`. `TooltipTrigger` adds no * default styling of its own (it's just an accessible hover/focus target), so it's re-exported * as-is too. */export { Tooltip, TooltipTrigger, type TooltipProps, type TooltipTriggerProps,} from "@morphos/overlays";export type TooltipContentProps = MorphosTooltipContentProps;@Component()export class TooltipContent extends StatelessComponent<TooltipContentProps> { render() { const { class: cls, ...rest } = this.props; return ( <MorphosTooltipContent class={cn( "z-50 w-fit text-balance rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground shadow-md", "data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95", cls, )} {...rest} /> ); }}Install the following dependencies:
npm install @morphos/overlays @praxisjs/csspnpm add @morphos/overlays @praxisjs/cssyarn add @morphos/overlays @praxisjs/cssbun add @morphos/overlays @praxisjs/cssCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, keyframes, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { TooltipContent as MorphosTooltipContent, type TooltipContentProps as MorphosTooltipContentProps } from "@morphos/overlays";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);const popIn = keyframes("kosmesis-pop-in", { from: { opacity: "0", transform: "scale(0.95)" }, to: { opacity: "1", transform: "scale(1)" },});class TooltipStyles extends Stylesheet { $content = this.css({ position: "relative", zIndex: 50, width: "fit-content", borderRadius: `calc(${t.radius} - 2px)`, backgroundColor: t.primary, padding: "0.375rem 0.75rem", fontSize: "0.75rem", color: t.primaryForeground, boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)", }).on("&[data-open]", { animation: `${popIn} 100ms ease-out` });}/** * `Tooltip` and `TooltipTrigger` are re-exported directly from `@morphos/overlays`. `Tooltip`'s * `render()` is a no-op Fragment and it's always instantiated directly * (`@State() tooltip = new Tooltip()`) rather than mounted via JSX, so wrapping it in a new * component class here would break `.isOpen`/`.show()`/`.hide()`. `TooltipTrigger` adds no * default styling of its own (it's just an accessible hover/focus target), so it's re-exported * as-is too. */export { Tooltip, TooltipTrigger, type TooltipProps, type TooltipTriggerProps } from "@morphos/overlays";export type TooltipContentProps = MorphosTooltipContentProps;@Component()export class TooltipContent extends StatelessComponent<TooltipContentProps> { @Styled(TooltipStyles) $s!: TooltipStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosTooltipContent class={cx(this.$s.$content, cls)} {...rest} />; }}Examples
Usage
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
@Component()
class SaveButton extends StatefulComponent {
@State() tooltip = new Tooltip();
render() {
return (
<>
<TooltipTrigger tooltip={this.tooltip}>
<Button variant="outline">Save</Button>
</TooltipTrigger>
<TooltipContent tooltip={this.tooltip}>Save changes (⌘S)</TooltipContent>
</>
);
}
}Tooltip itself is never mounted via JSX — it's instantiated directly and passed to
TooltipTrigger/TooltipContent. Since TooltipTrigger renders a plain <span>, it's safe to
nest a <Button> inside it (unlike PopoverTrigger/DialogTrigger, which render their own
<button>).