Skip to main content

Drawer

Slide-out panel with overlay, cookie persistence, keyboard shortcut, and dialog accessibility. Server-rendered ERB with a small Stimulus controller.

Usage

<%= render "components/drawer/provider", default_open: drawer_open? do %>
  <%= render "components/drawer", state: drawer_state do %>
    <%= render "components/drawer/header" do %>
      <h2 class="text-lg font-semibold">Drawer Title</h2>
    <% end %>

    <%= render "components/drawer/content" do %>
      Drawer content
    <% end %>

    <%= render "components/drawer/footer" do %>
      Footer actions
    <% end %>
  <% end %>
<% end %>

Examples

With Trigger

The trigger can live anywhere on the page — it finds the drawer through a Stimulus outlet and mirrors its state with aria-expanded.

<%= render "components/drawer/trigger" do %>Open Drawer<% end %>

<%= render "components/drawer/provider" do %>
  <%= render "components/drawer" do %>
    <!-- content -->
  <% end %>
<% end %>

Left Side Drawer

<%= render "components/drawer", side: :left do %>
  <!-- content -->
<% end %>

Keyboard & Accessibility

  • Cmd/Ctrl + D toggles the drawer (configurable via keyboard_shortcut).
  • Escape closes it; clicking the backdrop closes it.
  • The panel is a role="dialog" with aria-modal and a configurable aria_label. Focus moves into the panel on open and returns to the previously focused element on close.
  • While closed, the panel is aria-hidden and inert, so the off-screen content is invisible to assistive technology and unreachable by keyboard.

API Reference

Provider

Parameter Type Default Description
id String "drawer-provider" Element ID for stable morph matching
default_open Boolean false Initial open state
cookie_name String "drawer_state" Cookie for persistence
keyboard_shortcut String "d" Toggle shortcut (Cmd/Ctrl + key)
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Drawer

Parameter Type Default Description
id String auto Element ID (deterministic, derived from side)
state Symbol :closed :open or :closed
side Symbol :right :left or :right
aria_label String "Drawer" Accessible name for the dialog panel
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Trigger

Parameter Type Default Description
icon_name Symbol nil Optional icon for the toggle button
variant Symbol :default Button variant
size Symbol :default Button size
css_classes String "" Additional CSS classes
html_options Hash {} Additional HTML attributes

Other Parts

Partial Description
drawer/header Top section with title and built-in close button
drawer/content Scrollable middle section
drawer/footer Bottom section for actions
drawer/close Close button (X icon)

Helper Methods

Method Description
drawer_state(cookie_name) Returns :open or :closed from the cookie
drawer_open?(cookie_name) Returns true if open
drawer_closed?(cookie_name) Returns true if closed

Turbo Drive

The drawer controller integrates with Turbo Drive to keep state correct across navigations:

  • Cache teardown: the drawer closes and the backdrop hides before Turbo caches the page.
  • Morph awareness: with turbo_refresh_method_tag :morph, the drawer re-reads its cookie so client state survives the morph.
  • Persistence: open/closed state lives in a cookie and survives full page loads.