Dropdown

Native popover menus with typed triggers, entries, placement, and keyboard state.

Source
app/components/nitro_kit/dropdown.rb
API
NitroKit::Dropdown.new(id:) { |menu| menu.trigger; menu.item }

Placements

Anchor positioning follows the trigger when supported and safely centers otherwise.

Placement matrix

Bottom start
Bottom end
Top start
Top end
Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/dropdown_page.rb
NitroKit::Dropdown::PLACEMENTS.each do |placement|
  sample(placement.to_s.humanize, slug: placement.to_s) do
    render_menu("gallery-dropdown-#{placement}", placement:) do |menu|
      menu.item("Open profile", href: "/gallery/profile")
      menu.item("Copy account ID")
    end
  end
end

Menu anatomy

Titles, links, buttons, separators, disabled items, and destructive intent remain native.

Account actions

Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/dropdown_page.rb
render NitroKit::Dropdown.new(id: "gallery-dropdown-account") do |menu|
  menu.trigger("Account actions", variant: :primary)
  menu.title("Workspace")
  menu.item("Workspace settings", href: "/gallery/settings")
  menu.item("Duplicate workspace")
  menu.item("Export audit log", disabled: true)
  menu.separator
  menu.title("Danger zone")
  menu.item("Delete workspace", variant: :destructive)
end

Availability and pressure

A disabled trigger and long labels exercise non-interactive and constrained states.

Edge states

Disabled trigger
Long content
Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/dropdown_page.rb
sample("Disabled trigger", slug: "disabled-trigger") do
  render NitroKit::Dropdown.new(id: "gallery-dropdown-disabled") do |menu|
    menu.trigger("Actions unavailable", disabled: true)
    menu.item("Unavailable action", disabled: true)
  end
end
sample("Long content", slug: "long-content") do
  render NitroKit::Dropdown.new(id: "gallery-dropdown-long") do |menu|
    menu.trigger("Deployment actions")
    menu.item("Promote the currently verified release to production")
    menu.item("Restore the previously healthy production release", variant: :destructive)
  end
end

Record composition

Card, Badge, and Dropdown compose into a realistic deployment record.

Production deployment

Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/dropdown_page.rb
render NitroKit::Card.new(id: "gallery-dropdown-deployment-card") do |card|
  card.title("Release 2026.07.13", level: 3)
  card.body do
    render NitroKit::Badge.new(
      "Healthy",
      id: "gallery-dropdown-deployment-status",
      color: :success,
      size: :sm
    )
    p { "Serving all workspaces from fra1 and iad1." }
  end
  card.footer do
    render NitroKit::Dropdown.new(id: "gallery-dropdown-deployment") do |menu|
      menu.trigger("Release actions", variant: :ghost)
      menu.item("View deployment", href: "/gallery/deployments/2026-07-13")
      menu.item("Copy release identifier")
      menu.separator
      menu.item("Roll back release", variant: :destructive)
    end
  end
end