a-pager
A headless pagination element with slotted prev/next controls and CSS part styling.
The a-pager element renders numbered page links and manages pagination logic. It is intentionally unstyled — all visual design comes from the outside via CSS ::part() selectors and your own slotted controls.
Usage
<a-pager page="1" count="20" url="/results/{page}">
<button slot="prev">← Prev</button>
<button slot="next">Next →</button>
</a-pager>
<script>
document.querySelector('a-pager').addEventListener('change', (e) => {
e.currentTarget.setAttribute('page', e.detail.page);
});
</script>
URL generation
By default, page links append ?page=N to the current URL. Provide a url template to override this:
<!-- /results/1, /results/2, ... -->
<a-pager url="/results/{page}" count="20"></a-pager>
<!-- /search?q=cats&page=3 -->
<a-pager url="/search" query="q=cats" count="20"></a-pager>
Prev / next slots
The prev and next slots accept any element. Clicks inside them are intercepted and fire a change event — no extra JavaScript needed on your buttons.
<a-pager page="3" count="10">
<!-- anchor tags work too — navigation is prevented and replaced by the change event -->
<a href="/page/2" slot="prev">← Previous</a>
<a href="/page/4" slot="next">Next →</a>
</a-pager>
Styling
Page number links
Page links live in the shadow DOM and are exposed via CSS parts:
| Part | Description |
|---|---|
page | Every page number link |
page active | The currently selected page link |
ellipsis | The … truncation indicator |
pages | The <nav> wrapping all page links |
a-pager::part(pages) { display: flex; gap: 4px; }
a-pager::part(page) { padding: 0.25rem 0.5rem; border: 1px solid #ccc; }
a-pager::part(page active) { background: blue; color: white; }
a-pager::part(ellipsis) { color: #999; }
Boundary state
The at-start and at-end attributes are automatically toggled on the host element when the pager is at the first or last page. Use them to disable or hide your slotted controls:
a-pager[at-start] [slot="prev"],
a-pager[at-end] [slot="next"] {
opacity: 0.4;
pointer-events: none;
}
Events
pager.addEventListener('change', (e: CustomEvent<{ page: number, url: string }>) => {
// Update the page attribute to reflect the new page
pager.setAttribute('page', String(e.detail.page));
// Or navigate to the generated URL
// window.location.href = e.detail.url;
});
Calling e.preventDefault() cancels both the browser navigation (if the page links are anchors) and the change event propagation.
Accessibility
- The page link list is wrapped in
<nav aria-label="Pagination">. - The active page link has
aria-current="page". - Prev/next controls are fully slotted, so their native semantics (button role, focus behaviour) are preserved.
PagerElement
A headless pagination element. Manages page state and renders numbered page links. Accepts slotted prev/next controls — clicks on them are intercepted to fire
events so the host can update the
attribute.
Example
<a-pager page="3" count="10" url="/results/{page}">
<button slot="prev">← Prev</button>
<button slot="next">Next →</button>
</a-pager> Attributes
| Name | Type | Default value | Description |
|---|---|---|---|
count
|
number | 1 |
Total number of pages. |
page
|
number | 1 |
Current page (1-indexed). |
query
|
string | undefined |
Additional query string params appended to every generated page URL. |
url
|
string | undefined |
URL template with placeholder. Falls back to on the current URL. |
Events
| Name | Description |
|---|---|
change
|
Fired when navigating to a page. |