Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

Pagination

tier 2 — classes

Moving through a long result set, and saying where the current page sits.

Pages are addresses Use <a href> for every page that exists, not <button> — a page of results should be linkable, openable in a new tab and reachable with the back button. Only the disabled ends are buttons or spans.

Pagination

Wrap it in <nav aria-label="Pagination"> and put aria-current="page" on the current one. The class colours it; aria-current is what is announced. .page-count pushes the range to the far end, which is where the reader looks to find out how much is left.

<nav class="pagination" aria-label="Pagination">
    <span class="page-link" aria-disabled="true"><i class="ri-arrow-left-s-line"></i> Previous</span>
    <a class="page-link" href="#" aria-label="Page 1">1</a>
    <a class="page-link" href="#" aria-current="page" aria-label="Page 2">2</a>
    <a class="page-link" href="#" aria-label="Page 3">3</a>
    <span class="page-gap" aria-hidden="true">&hellip;</span>
    <a class="page-link" href="#" aria-label="Page 61">61</a>
    <a class="page-link" href="#">Next <i class="ri-arrow-right-s-line"></i></a>
    <span class="page-count">21&ndash;40 of 1,204</span>
</nav>

When the page is view state

Where the page number is view state rather than a location — a Blazor Server table whose page, sort and filters live in the circuit — use <button class="page-link" type="button"> throughout, with disabled on the ends rather than aria-disabled. Judge it by whether the page has an address: if a reader could reasonably send someone a link to page 4, use the anchors above.

<nav class="pagination" aria-label="Pagination">
    <button class="page-link" type="button" disabled>
        <i class="ri-arrow-left-s-line"></i> Previous
    </button>
    <button class="page-link" type="button" aria-current="page">1</button>
    <button class="page-link" type="button">2</button>
    <button class="page-link" type="button">3</button>
    <span class="page-gap" aria-hidden="true">…</span>
    <button class="page-link" type="button">61</button>
    <button class="page-link" type="button">
        Next <i class="ri-arrow-right-s-line"></i>
    </button>
    <span class="page-count">1–20 of 1 211</span>
</nav>

Which numbers to show, from C#

Live — page through it. SednaPager.Window(current, total) returns the slots to render — page numbers and .page-gaps — so the markup stays a foreach. The count of slots is the same on every page, so the buttons do not shift under the pointer, and a gap never hides a single page. edge and around widen the ends and the neighbourhood.

<nav class="pagination" aria-label="Pagination">
    <button class="page-link" type="button" disabled="@(_page == 1)" @onclick="@(() => _page--)">
        <i class="ri-arrow-left-s-line"></i> Previous
    </button>

    @foreach (var slot in SednaPager.Window(_page, Pages))
    {
        if (slot.IsGap)
        {
            <span class="page-gap" aria-hidden="true">…</span>
            continue;
        }
        var number = slot.Number;
        <button class="page-link" type="button" aria-current="@(slot.IsCurrent ? "page" : null)"
                @onclick="@(() => _page = number)">@number</button>
    }

    <button class="page-link" type="button" disabled="@(_page == Pages)" @onclick="@(() => _page++)">
        Next <i class="ri-arrow-right-s-line"></i>
    </button>
    <span class="page-count">Page @_page of @Pages</span>
</nav>

@code {
    private const int Pages = 61;

    // The current page is the app's state; SednaPager only decides which numbers to show.
    private int _page = 1;
}

For where a page sits when the sidebar cannot show it — a record three levels inside a section; if the sidebar already highlights the page, a breadcrumb earns nothing. The last item is the current page: mark it aria-current="page" and leave it a <span>, not a link to where you already are.

<nav class="breadcrumb" aria-label="Breadcrumb">
    <a class="breadcrumb-item" href="#"><i class="ri-home-4-line"></i> Console</a>
    <a class="breadcrumb-item" href="#">Orders</a>
    <a class="breadcrumb-item" href="#">EU-Central</a>
    <span class="breadcrumb-item" aria-current="page">ORD-4209</span>
</nav>

Prev, next and the range

Enough for a short list, and for a pager that has to live inside a toolbar row. .page-count is what makes it usable without numbers, and the disabled ends are <span aria-disabled="true">, not links to nowhere.

<div class="sedna-col sedna-gap-2">
    <nav class="pagination" aria-label="Order pages">
        <a class="page-link" href="#"><i class="ri-arrow-left-s-line"></i> Previous</a>
        <span class="page-count">1,181&ndash;1,204 of 1,204</span>
        <span class="page-link" aria-disabled="true">Next <i class="ri-arrow-right-s-line"></i></span>
    </nav>
    <div class="toolbar">
        <div class="toolbar-filters">
            <label class="form-check"><input type="checkbox" /> Only mine</label>
        </div>
        <nav class="pagination" aria-label="Order pages">
            <span class="page-link" aria-disabled="true"><i class="ri-arrow-left-s-line"></i></span>
            <a class="page-link" href="#" aria-current="page" aria-label="Page 1">1</a>
            <a class="page-link" href="#" aria-label="Page 2">2</a>
            <a class="page-link" href="#" aria-label="Page 3">3</a>
            <a class="page-link" href="#" aria-label="Next page"><i class="ri-arrow-right-s-line"></i></a>
        </nav>
    </div>
</div>

Above the page head

Where a breadcrumb belongs: above the heading, so the trail is read before the title it leads to, with .page-head carrying the title, the supporting line and the actions that apply to the whole page. One <h1> per page and it is this one — the breadcrumb is navigation, not a heading level.

ORD-4209

Five lines, three reserved. Raised 31 July at 09:14.

<div>
    <nav class="breadcrumb" aria-label="Breadcrumb">
        <a class="breadcrumb-item" href="#"><i class="ri-home-4-line"></i> Console</a>
        <a class="breadcrumb-item" href="#">Orders</a>
        <a class="breadcrumb-item" href="#">EU-Central</a>
        <span class="breadcrumb-item" aria-current="page">ORD-4209</span>
    </nav>
    <div class="page-head">
        <div class="page-head-text">
            <h1>ORD-4209</h1>
            <p class="lede">Five lines, three reserved. Raised 31 July at 09:14.</p>
        </div>
        <div class="page-head-actions">
            <button class="btn" type="button"><i class="ri-printer-line"></i> Print</button>
            <button class="btn btn-go" type="button"><i class="ri-send-plane-line"></i> Dispatch</button>
        </div>
    </div>
</div>