f6ae86617c
Co-authored-by: Copilot <copilot@github.com>
6.4 KiB
6.4 KiB
Tooltip
A small text hint that appears when the user hovers over an element. Use it to label icon buttons, clarify abbreviations, or explain options that don't have visible text.
Tooltips are implemented entirely in CSS — no JavaScript required.
Quick example
new Tooltip(
text: "Delete item",
trigger: new Button("🗑", size: "icon", variant: "ghost"))
Hover over the button and the label "Delete item" appears above it.
All the options
public Tooltip(
string text,
IHtmxComponent trigger,
string position = "top")
| Parameter | What it does |
|---|---|
text |
The tooltip text. Plain text only — no HTML. |
trigger |
Any IHtmxComponent — this is the element the user hovers over. |
position |
Where the tooltip appears: "top" (default), "bottom", "left", or "right". |
Real-world examples
Icon buttons in a toolbar
new Tooltip(text: "Bold", trigger: new Button("B", size: "icon", variant: "ghost"))
new Tooltip(text: "Italic", trigger: new Button("I", size: "icon", variant: "ghost"))
new Tooltip(text: "Save", trigger: new Button("💾", size: "icon", variant: "ghost"))
Right-aligned tooltip (near the left edge of the UI)
new Tooltip(
text: "View help documentation",
trigger: new Button("?", size: "icon", variant: "outline"),
position: "right")
Below the element
new Tooltip(
text: "This cannot be undone",
trigger: new Button("Delete", variant: "destructive"),
position: "bottom")
How it works
Tooltip wraps the trigger in a <span class="group">. The tooltip text is an absolutely positioned <span> inside that wrapper with opacity-0 by default and group-hover:opacity-100 to fade it in. Because this is pure Tailwind CSS, there is no JavaScript involved and no initialisation needed for HTMX-swapped content.
Tooltip on an Avatar
new Tooltip(
text: user.DisplayName ?? "Unknown user",
trigger: new Avatar(fallback: user.Initials, src: user.AvatarUrl))
Tooltip on a disabled-looking button
new Tooltip(
text: "You need admin access",
trigger: new Button(
"Publish",
variant: "default",
hxAttrs: "disabled aria-disabled='true' tabindex='-1'"))
Tips and tricks
- The tooltip text is plain text — HTML special characters in
textwill be HTML-encoded automatically. - Tooltip position may overflow the viewport if the trigger is near an edge — test all four positions and choose the one that fits.
- Since there is no JS, the tooltip works even when JavaScript is disabled.
- The trigger receives the
groupclass implicitly — this meansgroup-hover:*utilities on any child of the trigger will also activate on hover. Keep this in mind if the trigger component uses nested group utilities. - For touch devices the hover state is never triggered — consider providing the tooltip content elsewhere (e.g. as a
descriptionon a form field) if the information is essential. - To show a tooltip on a non-interactive element (e.g. a truncated table cell), wrap the element in a
<span>via a custom slot and pass that as the trigger. - To show a tooltip on a non-interactive element (e.g. a truncated table cell), wrap the element in a
<span>via a custom slot and pass that as the trigger.
Complete page example
Templates/ActionToolbarPage.htmx
<div class="max-w-3xl mx-auto py-10">
<h1 class="text-2xl font-bold mb-6">Document editor</h1>
<div class="flex items-center gap-2 border rounded-md p-2 mb-6">
$$BoldBtn$$
$$ItalicBtn$$
$$UnderlineBtn$$
$$SepToolbar$$
$$UndoBtn$$
$$RedoBtn$$
</div>
<div class="border rounded-md p-4 min-h-64 prose">
$$EditorContent$$
</div>
</div>
Templates/ActionToolbarPage.htmx.cs
namespace Htmx.ApiDemo.Templates;
public sealed class ActionToolbarPage : ActionToolbarPageBase
{
private readonly IHtmxComponent _bold;
private readonly IHtmxComponent _italic;
private readonly IHtmxComponent _underline;
private readonly IHtmxComponent _sep;
private readonly IHtmxComponent _undo;
private readonly IHtmxComponent _redo;
private readonly byte[] _content;
public ActionToolbarPage()
{
_bold = TooltipButton("Bold", "B", "font-bold");
_italic = TooltipButton("Italic", "I", "italic");
_underline = TooltipButton("Underline", "U", "underline");
_sep = new Components.Separator(orientation: "vertical", extraClasses: "mx-1 h-6");
_undo = TooltipButton("Undo (Ctrl+Z)", "↩", "");
_redo = TooltipButton("Redo (Ctrl+Y)", "↪", "");
_content = "<p>Start typing...</p>".ToUtf8Bytes();
}
private static IHtmxComponent TooltipButton(string tip, string label, string textClass)
=> new Components.Tooltip(
content: tip,
trigger: new Components.Button(
label,
variant: "ghost",
size: "icon",
extraClasses: textClass));
protected override void RenderBoldBtn(HtmxRenderContext ctx) => _bold.Render(ctx.Next());
protected override void RenderItalicBtn(HtmxRenderContext ctx) => _italic.Render(ctx.Next());
protected override void RenderUnderlineBtn(HtmxRenderContext ctx) => _underline.Render(ctx.Next());
protected override void RenderSepToolbar(HtmxRenderContext ctx) => _sep.Render(ctx.Next());
protected override void RenderUndoBtn(HtmxRenderContext ctx) => _undo.Render(ctx.Next());
protected override void RenderRedoBtn(HtmxRenderContext ctx) => _redo.Render(ctx.Next());
protected override void RenderEditorContent(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_content);
}
GET handler
[Handler]
[MapGet("/editor")]
public static partial class GetEditorHandler
{
public record Query();
private static Task<IResult> HandleAsync(Query _, HttpContext ctx, CancellationToken ct)
=> ctx.WriteHtmxPage(new ActionToolbarPage(), title: "Document editor");
}