Accessible page navigation with explicit boundaries, current state, and ellipses.
app/components/nitro_kit/pagination.rbNitroKit::Pagination.new(label:) { |pagination| pagination.page; pagination.ellipsis }First, middle, and last pages expose one current item and disabled outer boundaries.
test/dummy/app/components/gallery/components/pagination_page.rbGallery::Data.pagination_boundaries.each do |pagination|
sample(pagination.label, slug: pagination.slug) do
render_boundary(pagination)
end
endCompact and long ranges use non-interactive, accessibly labelled ellipses.
test/dummy/app/components/gallery/components/pagination_page.rbrender NitroKit::Pagination.new(
id: "gallery-pagination-compact",
label: "Compact result pages"
) do |pagination|
pagination.prev(href: "/gallery/results?page=1", id: "gallery-pagination-compact-previous")
pagination.page(1, href: "/gallery/results?page=1", id: "gallery-pagination-compact-page-1")
pagination.page(2, current: true, id: "gallery-pagination-compact-page-2")
pagination.page(3, href: "/gallery/results?page=3", id: "gallery-pagination-compact-page-3")
pagination.next(href: "/gallery/results?page=3", id: "gallery-pagination-compact-next")
endtest/dummy/app/components/gallery/components/pagination_page.rbrender NitroKit::Pagination.new(
id: "gallery-pagination-long",
label: "Long audit log pages"
) do |pagination|
pagination.prev(href: "/gallery/audit?page=6", id: "gallery-pagination-long-previous")
pagination.page(1, href: "/gallery/audit?page=1", id: "gallery-pagination-long-page-1")
pagination.ellipsis(label: "Pages 2 through 5 omitted")
pagination.page(6, href: "/gallery/audit?page=6", id: "gallery-pagination-long-page-6")
pagination.page(7, current: true, id: "gallery-pagination-long-page-7")
pagination.page(8, href: "/gallery/audit?page=8", id: "gallery-pagination-long-page-8")
pagination.ellipsis(label: "Pages 9 through 23 omitted")
pagination.page(24, href: "/gallery/audit?page=24", id: "gallery-pagination-long-page-24")
pagination.next(href: "/gallery/audit?page=8", id: "gallery-pagination-long-next")
endLong navigation copy, block page labels, icon-only boundaries, and wide ranges remain semantic.
test/dummy/app/components/gallery/components/pagination_page.rbrender NitroKit::Pagination.new(
id: "gallery-pagination-labels",
label: "Archived audit record pages"
) do |pagination|
pagination.prev(
"Go back to the previous collection of archived audit records",
href: "/gallery/archive?page=4",
icon: nil,
id: "gallery-pagination-labels-previous"
)
pagination.page(href: "/gallery/archive?page=5", id: "gallery-pagination-labels-page") do
"A custom page label"
end
pagination.next(
"Continue through the archived audit record collection",
href: "/gallery/archive?page=6",
icon: nil,
id: "gallery-pagination-labels-next"
)
endtest/dummy/app/components/gallery/components/pagination_page.rbrender NitroKit::Pagination.new(
id: "gallery-pagination-pressure",
label: "Twelve visible pages"
) do |pagination|
pagination.prev(href: "/gallery/pressure?page=5", id: "gallery-pagination-pressure-previous")
(1..12).each do |page|
pagination.page(
page,
href: "/gallery/pressure?page=#{page}",
current: page == 6,
id: "gallery-pagination-pressure-page-#{page}"
)
end
pagination.next(href: "/gallery/pressure?page=7", id: "gallery-pagination-pressure-next")
endField, Input, Table, Badge, Button, and Pagination compose into searchable member results.
| Name | Role | Status |
|---|---|---|
| Ada Lovelace | Owner | Active |
| Grace Hopper | Admin | Active |
| Katherine Johnson | Member | Invited |
test/dummy/app/components/gallery/components/pagination_page.rbrender NitroKit::Field.new(
nil,
:query,
as: :search,
id: "gallery-pagination-search",
name: "search[query]",
value: "engineering",
label: "Search members",
placeholder: "Name or email",
html: { id: "gallery-pagination-search-field" }
)
render NitroKit::Table.new(
id: "gallery-pagination-results-table",
table_html: { id: "gallery-pagination-results-table-element" }
) do |table|
table.caption("Three of 128 matching members")
table.thead do
table.tr do
table.th("Name")
table.th("Role")
table.th("Status", align: :right)
end
end
table.tbody do
Gallery::Data.members.each do |member|
table.tr do
table.th(member.name, scope: :row)
table.td(member.role.to_s.humanize)
table.td(align: :right) do
render NitroKit::Badge.new(
member.status.to_s.humanize,
id: "gallery-pagination-result-status-#{member.id}",
color: member_status_color(member.status),
size: :sm
)
end
end
end
end
end
render NitroKit::Pagination.new(
id: "gallery-pagination-results",
label: "Member search result pages"
) do |pagination|
pagination.prev(id: "gallery-pagination-results-previous")
pagination.page(1, current: true, id: "gallery-pagination-results-page-1")
pagination.page(2, href: "/gallery/members?query=engineering&page=2", id: "gallery-pagination-results-page-2")
pagination.ellipsis(label: "Pages 3 through 15 omitted")
pagination.page(16, href: "/gallery/members?query=engineering&page=16", id: "gallery-pagination-results-page-16")
pagination.next(
href: "/gallery/members?query=engineering&page=2",
id: "gallery-pagination-results-next"
)
end