Breadcrumbs tell users where they are in a site's hierarchy; pagination lets them move through long result sets. Both are pure CSS components with no JavaScript, and both have a small number of options that are easy to get right once you know the accessibility requirements. This lesson covers the markup, dividers, sizes, alignment and how to generate pagination from data.
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/library">Library</a></li>
<li class="breadcrumb-item active" aria-current="page">Data</li>
</ol>
</nav><nav aria-label="breadcrumb"> so screen readers announce it as a navigation landmark..active and aria-current="page" and do not make it a link.::before, so no separator characters appear in the HTML.The divider is a CSS custom property. Override it inline or on any ancestor:
<nav aria-label="breadcrumb" style="--bs-breadcrumb-divider: '>'">
<ol class="breadcrumb">...</ol>
</nav>Use an SVG for a chevron icon or an empty string to remove the divider entirely:
.breadcrumb {
--bs-breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='%236c757d'/%3E%3C/svg%3E");
}
.breadcrumb.no-divider {
--bs-breadcrumb-divider: '';
}Changing it globally is a Sass override of $breadcrumb-divider before importing Bootstrap.
Pagination uses a <ul class="pagination"> of .page-item elements, each containing a .page-link:
<nav aria-label="Article pages">
<ul class="pagination">
<li class="page-item">
<a class="page-link" href="?page=1" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="?page=1">1</a></li>
<li class="page-item active" aria-current="page"><a class="page-link" href="?page=2">2</a></li>
<li class="page-item"><a class="page-link" href="?page=3">3</a></li>
<li class="page-item">
<a class="page-link" href="?page=3" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>Guidelines:
href values so pages work without JavaScript and can be opened in a new tab..active and aria-current="page"..disabled on the item and aria-disabled="true" on the link; Bootstrap adds pointer-events: none. Prefer <span> or <button disabled> for truly non-clickable items.« must be wrapped in aria-hidden="true" with an aria-label on the link.| Class | Effect |
|---|---|
| pagination-sm | Smaller links |
| pagination-lg | Larger links |
| justify-content-center | Center the list (it is a flex container) |
| justify-content-end | Right-align |
<ul class="pagination pagination-sm justify-content-end">...</ul>Pagination links use CSS variables (--bs-pagination-active-bg, --bs-pagination-color, and others), so a themed variant is one CSS rule away:
.pagination.brand {
--bs-pagination-active-bg: #4f46e5;
--bs-pagination-active-border-color: #4f46e5;
--bs-pagination-color: #4f46e5;
}Most real pagination is rendered from a page count. A compact JavaScript helper that shows a window of pages around the current one:
function renderPagination(current, total, hrefFor) {
const items = [];
const push = (label, page, opts = {}) => {
const cls = ["page-item", opts.active && "active", opts.disabled && "disabled"].filter(Boolean).join(" ");
const aria = opts.active ? ' aria-current="page"' : opts.disabled ? ' aria-disabled="true"' : "";
items.push(`<li class="${cls}"><a class="page-link" href="${hrefFor(page)}"${aria}>${label}</a></li>`);
};
push("Previous", current - 1, { disabled: current === 1 });
for (let p = Math.max(1, current - 2); p <= Math.min(total, current + 2); p++) {
push(p, p, { active: p === current });
}
push("Next", current + 1, { disabled: current === total });
return `<nav aria-label="Pages"><ul class="pagination">${items.join("")}</ul></nav>`;
}
document.getElementById("pager").innerHTML = renderPagination(7, 20, (p) => `?page=${p}`);Server-side templates (EJS, Blade, Jinja) follow the same shape.
<nav> wrapper. Both components need it for the landmark and aria-label..disabled without aria-disabled. The style is applied but assistive technology still reports a normal link.How do you change the breadcrumb separator to `>` for a single breadcrumb?
<ol class="breadcrumb"> inside <nav aria-label="breadcrumb">; the last item gets .active and aria-current="page".--bs-breadcrumb-divider, so it can be changed per instance or globally.<ul class="pagination"> of .page-item > .page-link, with .active, .disabled and matching ARIA attributes.pagination-sm/-lg change size; flex utilities align the list; CSS variables restyle colors.Next lesson: Modals, Tooltips and Popovers - dialogs and contextual hints powered by Bootstrap's JavaScript plugins.