Let’s make github-styled hovercards/tooltips that load content only when you hover on an element, using lazy-loaded turbo frames and a bit of CSS:

github hovercards example

1. Initial setup

# config/seeds.rb
3.times do
  Person.create(name: Faker::Name.first_name,
                surname: Faker::Name.last_name,
                address: Faker::Address.full_address,
                phone: Faker::PhoneNumber.cell_phone)
bundle add faker
rails g scaffold person name surname address phone
rails db:migrate
rails db:seed

2. CSS: display hidden area on hover

/* app/assets/stylesheets/application.css */
.hoverWrapper #hoverContent {
  display: none;
  position: absolute;
  background-color: black;
  color: white;
  padding: 10px;
  border-radius: 4px

.hoverWrapper:hover #hoverContent {
  display: block;

hoverContent is hidden by default.

hoverContent is visible when hovering hoverWrapper.

<span class="hoverWrapper">
  Hover me...
  <div id="hoverContent">
    Hidden content

How it works:

CSS - hover to display

3. Lazy-Loaded Turbo Streams

Add a route for the hovercard

# config/routes.rb
  root to: redirect("/people")
  resources :people do
    member do
      get :hovercard

Add a controller action

class PeopleController < ApplicationController
  def hovercard
    @person = Person.find(params[:id])
  • Add a template that will contain some info
  • Wrap the template into a turbo_frame with an ID that is unique to this @person
  • target: "_top" - for links inside the turbo_frame to work
# app/views/people/hovercard.html.erb
<%= turbo_frame_tag dom_id(@person, :hovercard), target: "_top" do %>
  <%= link_to "Show this person", @person %>
  <%= @person.id %>
  <%= @person.address %>
  <%= @person.phone %>
<% end %>
  • Finally, add a turbo_frame_tag that would lead to hovercard_person_path in the hidden area of the HTML.
  • A turbo_stream with loading: :lazy is loaded only when it becomes visible on the screen.
  • role="button", aria-describedby="id", role="tooltip" are just some fancy HTML tags that help the browser read your HTML in a better way. You can do without them.
# app/views/people/_person.html.erb
  <div class="hoverWrapper">
    <span role="button" aria-describedby="<%= dom_id(person, :hovercard) %>">
    <div id="hoverContent">
      <%= turbo_frame_tag dom_id(person, :hovercard), target: "_top", role: "tooltip", src: hovercard_person_path(person), loading: :lazy do %>
      <% end %>

Now, when you hover on Details..., the <div id="hoverContent"> will become visible and will load the template app/views/people/hovercard.html.erb.

Final result:

turbo frame hovercards

That’s it!

Inspired by Steve Polio’s post