Tooltip

Display contextual information on hover or focus to provide supplementary hints and explanations.

Key Features

  • Four Positions - Top, right, bottom, and left placement options
  • All Tailwind Colors - Dark, light, semantic colors, plus ANY Tailwind color (red, blue, purple, pink, etc.)
  • Three Sizes - Small, base, and large for different contexts
  • Arrow Indicator - CSS-based arrow pointing to trigger element
  • Two Trigger Modes - Hover (default) or click to show
  • Configurable Delay - Optional delay before showing tooltip
  • Max Width Support - Enable text wrapping for long content
  • Full Accessibility - ARIA support, keyboard navigation, screen reader descriptions
  • Smooth Animation - 150ms fade transition for polished UX
  • Dark Mode - Full dark mode support with color inversion

Basic Usage

Wrap any content in a tooltip to show contextual information on hover.

Basic Usage
<%= rui_tooltip(text: "This is a tooltip") do %>
  <span>Hover over me</span>
<% end %>

Positions

Tooltips can appear on any side of the trigger element. The default is :top.

<%= rui_tooltip(text: "Top tooltip", position: :top) do %>
  <span>Top</span>
<% end %>

<%= rui_tooltip(text: "Right tooltip", position: :right) do %>
  <span>Right</span>
<% end %>

<%= rui_tooltip(text: "Bottom tooltip", position: :bottom) do %>
  <span>Bottom</span>
<% end %>

<%= rui_tooltip(text: "Left tooltip", position: :left) do %>
  <span>Left</span>
<% end %>

Colors

Tooltip supports ALL Tailwind colors via ColorBuilderHelper, plus special tooltip-specific colors.

Special Tooltip Colors

Semantic Colors

All Tailwind Colors

Use any Tailwind color name directly. Bright colors (amber, lime, yellow, orange) automatically use dark text for readability.


<%= rui_tooltip(text: "Dark tooltip", color: :dark) do %>...<% end %>
<%= rui_tooltip(text: "Light tooltip", color: :light) do %>...<% end %>

<%= rui_tooltip(text: "Primary", color: :primary) do %>...<% end %>
<%= rui_tooltip(text: "Success", color: :success) do %>...<% end %>

<%= rui_tooltip(text: "Red tooltip", color: :red) do %>...<% end %>
<%= rui_tooltip(text: "Purple tooltip", color: :purple) do %>...<% end %>
<%= rui_tooltip(text: "Amber tooltip", color: :amber) do %>...<% end %>

Sizes

Three size options are available. The default is :base.

<%= rui_tooltip(text: "Small tooltip", size: :sm) do %>...<% end %>
<%= rui_tooltip(text: "Base tooltip", size: :base) do %>...<% end %>
<%= rui_tooltip(text: "Large tooltip", size: :lg) do %>...<% end %>

Arrow

By default, tooltips show an arrow pointing to the trigger. You can disable it with arrow: false.

<%= rui_tooltip(text: "With arrow", arrow: true) do %>...<% end %>
<%= rui_tooltip(text: "No arrow", arrow: false) do %>...<% end %>

Trigger Modes

Tooltips can be triggered by hover (default) or click.

<%= rui_tooltip(text: "Hover to see", trigger: :hover) do %>...<% end %>
<%= rui_tooltip(text: "Click triggered!", trigger: :click) do %>...<% end %>

Note: Click-triggered tooltips can be dismissed by clicking outside or pressing Escape.

Show Delay

Add a delay (in milliseconds) before the tooltip appears. Useful for preventing accidental triggers.

<%= rui_tooltip(text: "No delay", delay: 0) do %>...<% end %>
<%= rui_tooltip(text: "200ms delay", delay: 200) do %>...<% end %>
<%= rui_tooltip(text: "500ms delay", delay: 500) do %>...<% end %>

Hide Delay

Add a delay (in milliseconds) before the tooltip disappears. This prevents flickering when moving between adjacent elements.

<%= rui_tooltip(text: "No hide delay", hide_delay: 0) do %>...<% end %>
<%= rui_tooltip(text: "200ms hide delay", hide_delay: 200) do %>...<% end %>
<%= rui_tooltip(text: "Both delays", delay: 100, hide_delay: 300) do %>...<% end %>

Max Width

By default, tooltips don't wrap text. Use max_width to enable wrapping for longer content.

<%= rui_tooltip(
  text: "This is a longer tooltip that will wrap to multiple lines",
  max_width: "200px"
) do %>
  <span>Long Tooltip</span>
<% end %>

With Other Components

Tooltips work great with other RapidRailsUI components like buttons, icons, and badges.


<%= rui_tooltip(text: "Save your changes", position: :bottom) do %>
  <%= rui_button("Save", color: :emerald) %>
<% end %>

<%= rui_tooltip(text: "Get help") do %>
  <%= rui_icon(:help_circle, size: :lg) %>
<% end %>

<%= rui_tooltip(text: "3 new notifications") do %>
  <%= rui_badge(text: "New", color: :blue) %>
<% end %>

<%= rui_tooltip(text: "Settings", position: :top, arrow: false) do %>
  <%= rui_button(color: :primary, size: :sm, shape: :circle) do |btn| %>
    <%= btn.with_icon(:settings) %>
  <% end %>
<% end %>

Two-line tooltips

Pass subtitle: alongside text: to render a visible secondary line below the main tooltip body. Useful for icon-only buttons where the icon alone is ambiguous, settings labels that need a one-line explanation, or shortcut hints attached to action buttons. Requires v0.48.0+.

subtitle: vs description:. subtitle: renders visibly below text: for all users. description: is screen-reader only (sr-only) for extended assistive context. They are independent and can be used together.

Side by side


<%= rui_tooltip(text: "Settings") do %>
  <%= rui_icon(:settings) %>
<% end %>

<%= rui_tooltip(text: "Settings", subtitle: "Open application settings") do %>
  <%= rui_icon(:settings) %>
<% end %>

Icon button with shortcut hint

<%= rui_tooltip(text: "Save", subtitle: "⌘S", position: :bottom) do %>
  <%= rui_button(color: :emerald, shape: :circle) do |btn| %>
    <% btn.with_icon(:save) %>
  <% end %>
<% end %>

Combining visible subtitle and sr-only description

You can pair both. Sighted users see text + subtitle; screen readers also announce the richer description for extra context.

<%= rui_tooltip(
  text: "Sync",
  subtitle: "Last synced 2 min ago",
  description: "Synchronizes local changes with the server. Use when you suspect data is stale."
) do %>
  <%= rui_icon(:refresh_cw) %>
<% end %>

Accessibility

The Tooltip component follows WAI-ARIA best practices for tooltip widgets.

ARIA Attributes

  • role="tooltip" on the tooltip content
  • aria-describedby links trigger to tooltip
  • aria-expanded indicates visibility state
  • aria-hidden managed by JavaScript

Keyboard Navigation

  • Tab focuses trigger element
  • Focus events show tooltip for keyboard users
  • Escape hides the tooltip

Semantic HTML

  • Trigger elements have tabindex="0" for focusability
  • Tooltip content is hidden from screen readers when not visible

Screen Reader Support

  • Tooltip text is announced when focused
  • Use description parameter for extended context

Screen Reader Description Example

Add extra context for screen readers with the description parameter.

<%= rui_tooltip(
  text: "Settings",
  description: "Opens the application settings panel where you can customize preferences"
) do %>
  <%= rui_icon(:settings) %>
<% end %>

JavaScript API

The tooltip uses the popup Stimulus controller for show/hide behavior with configurable delays.

Stimulus Actions

Trigger tooltip behavior from any element:

Action Description
popup#show Show the tooltip (with configured delay)
popup#hide Hide the tooltip (with configured delay)
popup#toggle Toggle the tooltip open/closed state
popup#escape Close tooltip on Escape key and return focus to trigger
Using Stimulus Actions

Tooltip content
Click outside to close

Custom Events

Listen for tooltip lifecycle events:

Event When Detail
popup:show Before tooltip shows (cancelable) { trigger }
popup:shown After tooltip is fully visible { trigger }
popup:hide Before tooltip hides (cancelable) { trigger }
popup:hidden After tooltip is fully hidden { trigger }
Listening to Events
// Listen for tooltip events
const tooltip = document.querySelector('[data-controller="popup"]')

// Prevent tooltip from showing conditionally
tooltip.addEventListener('popup:show', (event) => {
  if (someCondition) {
    event.preventDefault() // Tooltip won't show
  }
})

// Track visibility
tooltip.addEventListener('popup:shown', () => {
  console.log('Tooltip is now visible')
})

tooltip.addEventListener('popup:hidden', () => {
  console.log('Tooltip is now hidden')
})

Programmatic Control

Control the tooltip directly via Stimulus controller:

Programmatic Control
// Get the controller instance
const element = document.querySelector('[data-controller="popup"]')
const controller = this.application.getControllerForElementAndIdentifier(element, 'popup')

// Programmatic control
controller.show()   // Show with configured delay
controller.hide()   // Hide with configured delay
controller.toggle() // Toggle state

// Direct state manipulation
controller.openValue = true  // Show immediately
controller.openValue = false // Hide immediately

API Reference

rui_tooltip

Display contextual information on hover or focus with customizable positioning and timing

Parameter Type Default Description
text* String Tooltip text content (main visible line)
subtitle String Visible secondary line rendered below text (v0.48.0+)
description String Screen-reader-only extended context (sr-only, not visible)
id String auto-generated Custom ID for the tooltip element

Appearance

Visual styling options

Parameter Type Default Description
position Symbol :top Position relative to trigger
:top :right :bottom :left
color Symbol :dark Color scheme - special (:dark, :light), semantic (:primary, :success, :warning, :danger, :info), or any Tailwind color
size Symbol :base Tooltip size
:sm :base :lg
arrow Boolean true Show arrow pointing to trigger element

Behavior

Interaction and timing options

Parameter Type Default Description
trigger Symbol :hover How to trigger the tooltip
:hover :click
delay Integer 0 Show delay in milliseconds before tooltip appears
hide_delay Integer 0 Hide delay in milliseconds (prevents flickering)
max_width String Max width for text wrapping (e.g., '200px', '15rem')

Custom Styling

Override default styles with custom classes

Parameter Type Default Description
class String Custom classes for the wrapper element

JavaScript & Namespacing

Tooltip ships a Stimulus controller registered under the identifier popup. If you set config.stimulus_namespace = "rui" ( see guide ), every emitted Stimulus attribute is prefixed: the identifier becomes rui--popup.

💡 Working with your own Stimulus controllers? Read Stimulus Namespace to keep the gem's controllers from colliding with yours.

JS registration (app/javascript/controllers/index.js)

Without namespace
import PopupController from "./rapid_rails_ui/popup_controller"
application.register("popup", PopupController)
With config.stimulus_namespace = "rui"
import PopupController from "./rapid_rails_ui/popup_controller"
application.register("rui--popup", PopupController)

Custom trigger button

Without namespace
<button data-action="popup#open">Trigger</button>
With namespace (either form works)
<button data-action="rui--popup#open">Trigger</button>

<button data-action="<%= stimulus_action("<%= controller_id %>", "click->#open") %>">Trigger</button>

Related Components