# Tooltip Component
Contextual information displayed on hover, focus, or click. Built with Floating UI for intelligent positioning and auto-flipping.
## Features
- 12 placement positions (top, bottom, left, right with -start/-end variants)
- Smart auto-positioning and flipping
- Three text sizes (small, regular, large)
- Multiple animation styles (fade, origin, combined)
- Configurable delay before showing
- Optional arrow indicator
- Touch-friendly click trigger support
- Global state management (only one tooltip visible at a time)
- Works inside modals and dialogs
## Implementation Options
| Format | Location | Best For |
| ------ | -------- | -------- |
| **Plain ERB** | `app/views/components/tooltip/` | Full control, copy-paste |
| **Shared Partials** | `app/views/shared/components/tooltip/` | Reusable partials, data-driven |
| **ViewComponent** | `app/components/tooltip/` | Block-style content, testing |
---
## Stimulus Controller
**Requires:** Floating UI library (`@floating-ui/dom`)
### Values
| Value | Type | Default | Description |
| ----- | ---- | ------- | ----------- |
| `placement` | String | `"top"` | Tooltip position |
| `offset` | Number | `8` | Distance from trigger element in pixels |
| `maxWidth` | Number | `200` | Maximum tooltip width in pixels |
| `delay` | Number | `0` | Delay before showing (milliseconds) |
| `size` | String | `"regular"` | Text size: `"small"`, `"regular"`, `"large"` |
| `animation` | String | `"fade"` | Animation: `"fade"`, `"origin"`, `"fade origin"`, `"none"` |
| `trigger` | String | `"auto"` | Trigger events: `"mouseenter"`, `"focus"`, `"click"`, `"auto"` |
### Data Attributes
| Attribute | Description |
| --------- | ----------- |
| `data-tooltip-content` | The tooltip text content (required) |
| `data-tooltip-arrow` | Set to `"false"` to hide the arrow |
### Placement Values
```
top-start top top-end
left-start [ ] right-start
left [elem] right
left-end [ ] right-end
bottom-start bottom bottom-end
```
---
## Plain ERB
Copy the code and add `data-controller="tooltip"` with `data-tooltip-content` to any element.
### Basic Example
```erb
<button
data-controller="tooltip"
data-tooltip-content="This is a simple tooltip"
class="px-4 py-2 bg-neutral-100 rounded-lg">
Hover me
</button>
```
### With Options
```erb
<button
data-controller="tooltip"
data-tooltip-content="Tooltip on bottom with delay"
data-tooltip-placement-value="bottom"
data-tooltip-delay-value="300"
data-tooltip-arrow="false"
class="px-4 py-2 bg-neutral-100 rounded-lg">
Custom Tooltip
</button>
```
### Icon Button Example
```erb
<button
data-controller="tooltip"
data-tooltip-content="Settings"
data-tooltip-placement-value="bottom"
class="p-2 rounded-lg hover:bg-neutral-100">
<svg class="size-5"><!-- icon --></svg>
</button>
```
---
## Shared Partials
### Basic Usage
```erb
<%= render "shared/components/tooltip/tooltip", text: "Helpful info" do %>
<button class="btn">Hover me</button>
<% end %>
```
### With Options
```erb
<%= render "shared/components/tooltip/tooltip",
text: "This tooltip appears below after a delay",
placement: "bottom",
delay: 300,
max_width: 250,
size: "small",
animation: "fade origin"
do %>
<button class="btn">Custom Options</button>
<% end %>
```
### Options
| Local | Type | Default | Description |
| ----- | ---- | ------- | ----------- |
| `text` | String | required | Tooltip text content |
| `placement` | String | `"top"` | Position (see placement values above) |
| `offset` | Integer | `8` | Distance from element in pixels |
| `max_width` | Integer | `200` | Maximum width in pixels |
| `delay` | Integer | `0` | Show delay in milliseconds |
| `size` | String | `"regular"` | `"small"`, `"regular"`, `"large"` |
| `animation` | String | `"fade"` | `"fade"`, `"origin"`, `"fade origin"`, `"none"` |
| `trigger` | String | `"auto"` | `"mouseenter"`, `"focus"`, `"click"`, `"auto"` |
| `arrow` | Boolean | `true` | Show arrow indicator |
| `classes` | String | `nil` | Additional wrapper classes |
| `tag` | String | `"span"` | HTML tag for wrapper |
---
## ViewComponent
### Basic Usage
```erb
<%= render Tooltip::Component.new(text: "Helpful info") do %>
<button class="btn">Hover me</button>
<% end %>
```
### With Options
```erb
<%= render Tooltip::Component.new(
text: "This tooltip appears below after a delay",
placement: "bottom",
delay: 300,
max_width: 250,
size: :small,
animation: "fade origin"
) do %>
<button class="btn">Custom Options</button>
<% end %>
```
### Component Options
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `text` | String | required | Tooltip text content |
| `placement` | String | `"top"` | Position |
| `offset` | Integer | `8` | Distance from element in pixels |
| `max_width` | Integer | `200` | Maximum width in pixels |
| `delay` | Integer | `0` | Show delay in milliseconds |
| `size` | Symbol | `:regular` | `:small`, `:regular`, `:large` |
| `animation` | String | `"fade"` | `"fade"`, `"origin"`, `"fade origin"`, `"none"` |
| `trigger` | String | `"auto"` | `"mouseenter"`, `"focus"`, `"click"`, `"auto"` |
| `arrow` | Boolean | `true` | Show arrow indicator |
| `classes` | String | `nil` | Additional wrapper classes |
| `tag` | String | `"span"` | HTML tag for wrapper |
---
## Trigger Modes
| Trigger | Description |
| ------- | ----------- |
| `auto` | Auto-detects: hover+focus on desktop, click on touch devices |
| `mouseenter` | Show on hover |
| `focus` | Show on focus (keyboard navigation) |
| `click` | Show/hide on click (toggle mode) |
| `mouseenter focus` | Show on hover or focus |
---
## Animations
| Animation | Description |
| --------- | ----------- |
| `fade` | Opacity transition (default) |
| `origin` | Scale from origin point |
| `fade origin` | Combined fade and scale |
| `none` | No animation, instant show/hide |
---
## Accessibility
- Tooltips are announced to screen readers via `aria-labelledby`
- Focus trigger ensures keyboard users can access tooltip content
- Auto mode provides appropriate interaction for touch devices
- Tooltips hide when trigger element scrolls out of view
---
## Common Patterns
### Help Icon
```erb
<%= render "shared/components/tooltip/tooltip",
text: "Learn more about this feature",
placement: "right"
do %>
<button type="button" class="cursor-help size-5 rounded-full border text-xs">?</button>
<% end %>
```
### Table Action Buttons
```erb
<div class="flex gap-1">
<%= render "shared/components/tooltip/tooltip", text: "Edit", delay: 300 do %>
<button class="p-1 hover:bg-neutral-100 rounded">
<svg class="size-4"><!-- edit icon --></svg>
</button>
<% end %>
<%= render "shared/components/tooltip/tooltip", text: "Delete", delay: 300 do %>
<button class="p-1 hover:bg-red-100 text-red-600 rounded">
<svg class="size-4"><!-- delete icon --></svg>
</button>
<% end %>
</div>
```
### Truncated Text
```erb
<%= render "shared/components/tooltip/tooltip",
text: @item.full_description,
max_width: 300
do %>
<span class="truncate max-w-xs block"><%= @item.full_description %></span>
<% end %>
```
### Status Indicator
```erb
<%= render "shared/components/tooltip/tooltip",
text: "System operational",
classes: "inline-flex items-center gap-2"
do %>
<div class="size-2 rounded-full bg-green-500"></div>
<span>Online</span>
<% end %>
```
---
## Troubleshooting
**Tooltip not appearing:** Ensure `data-tooltip-content` has a value and the Stimulus controller is loaded.
**Positioning issues:** Check that Floating UI is properly imported. The tooltip auto-flips when near viewport edges.
**Multiple tooltips showing:** The global state manager should handle this automatically. Ensure you're not creating multiple tooltip instances on the same element.
**Not working in modals:** The controller automatically detects open dialogs and appends the tooltip to the dialog element.
---
## AI Instructions
### Choose An Implementation
- **Vanilla / plain ERB**: Use when you want full markup control or need to adapt the example directly inside a page.
- **shared partial**: Use when you want a reusable partial with locals and a consistent render call across views.
- **ViewComponent**: Use when you want a Ruby API, slots, stronger encapsulation, or repeated composition in multiple places.
### Quick Reference
- **Vanilla examples**: `app/views/components/tooltip/`
- **Shared partial files**: `app/views/shared/components/tooltip/`
- **shared partial**: `render "shared/components/tooltip/tooltip"`
- **ViewComponent**: `render Tooltip::Component.new(...)`
- **ViewComponent files**: `app/components/tooltip/`
### Implementation Checklist
- Pick one implementation path first, then stay consistent within that example.
- Use only documented locals, initializer arguments, variants, and slot names.
- Copy the base example before adding app-specific styling or behavior.
- If the component is interactive, keep the documented Stimulus controller, targets, values, and ARIA wiring intact.
- Keep variant names, enum values, and option formats exactly as documented.
### Common Patterns
- **Vanilla / plain ERB**: Best for one-off pages, direct markup edits, and quick adaptations of the shipped examples.
- **shared partial**: Best for reusable Rails view partials driven by locals or collections.
- **ViewComponent**: Best for reusable component implementations that benefit from Ruby-side defaults, slots, or testable composition.
### Common Mistakes
- Do not mix shared partial locals with ViewComponent initializer arguments in the same example.
- Do not invent undocumented variants, sizes, slot names, or helper methods.
- Do not remove required wrapper elements, IDs, or data attributes when copying the component.
- Do not swap the documented render path for a guessed partial or component name.