Data section

An owned section heading around exactly one caller-populated Table or typed EmptyState.

Source
DataSection owns title → actions → content ordering. Tables, rows, EmptyState copy, and adjacent pagination remain caller-owned.
API
Supply the required title and optional description through constructor text or matching compound methods. Provide exactly one table(Table) or empty_state(EmptyState level: 3). actions accepts at most one ButtonGroup.

Tables and empty alternatives

Exactly one typed Table or level-three EmptyState with optional grouped actions.

Minimal table

ViewportFull width
Rubytest/dummy/app/components/gallery/blocks/data_section_page.rb
render NitroKit::DataSection.new(title: "Members", id: "gallery-data-section-minimal") do |section|
  section.table(member_table(id: "gallery-data-section-minimal-table", count: 1)) do |table|
    render_member_rows(table, 1)
  end
end

Actions and complete table

ViewportFull width
Rubytest/dummy/app/components/gallery/blocks/data_section_page.rb
render NitroKit::DataSection.new(
  title: "Workspace members",
  description: "Active, invited, and suspended access across every project.",
  id: "gallery-data-section-complete"
) do |section|
  section.actions NitroKit::ButtonGroup.new(label: "Member data actions") do |actions|
    actions.button("Export CSV", href: "#export")
    actions.button("Invite teammate", href: "#invite", variant: :primary)
  end
  section.table(member_table(id: "gallery-data-section-complete-table", count: 4)) do |table|
    render_member_rows(table, 4)
  end
end

Empty alternative

ViewportFull width
Rubytest/dummy/app/components/gallery/blocks/data_section_page.rb
render NitroKit::DataSection.new(
  title: "API credentials",
  description: "Credentials are scoped by environment and owner.",
  id: "gallery-data-section-empty"
) do |section|
  section.empty_state NitroKit::EmptyState.new(
    title: "No API credentials",
    description: "Create one when an integration is ready to authenticate.",
    level: 3,
    id: "gallery-data-section-empty-state"
  ) do |empty|
    empty.icon NitroKit::Icon.new(:key_round)
    empty.action NitroKit::Button.new("Create credential", href: "#create", variant: :primary)
  end
end

Dense long table

ViewportFull width
Rubytest/dummy/app/components/gallery/blocks/data_section_page.rb
render NitroKit::Container.new(size: :xl, id: "gallery-data-section-dense-container") do
  render NitroKit::DataSection.new(
    title: "International workspace access inventory",
    description: "A deliberately dense table remains caller-owned while the section owns only its heading, actions, and content ordering.",
    id: "gallery-data-section-dense"
  ) do |section|
    section.actions NitroKit::ButtonGroup.new(label: "Inventory actions") do |actions|
      actions.button("Download complete access inventory", href: "#download")
    end
    section.table(member_table(id: "gallery-data-section-dense-table", count: 12)) do |table|
      render_member_rows(table, 12, long: true)
    end
  end
end