diff --git a/frontend/src/lib/components/Pagination.svelte b/frontend/src/lib/components/Pagination.svelte
index 0edf0fb7..01659e9e 100644
--- a/frontend/src/lib/components/Pagination.svelte
+++ b/frontend/src/lib/components/Pagination.svelte
@@ -16,8 +16,10 @@ const { page, totalPages, makeHref, ariaLabel }: Props = $props();
const hasPrev = $derived(page > 0);
const hasNext = $derived(page < totalPages - 1);
-const linkBase =
- 'inline-flex min-h-[44px] min-w-[44px] items-center justify-center gap-1.5 rounded-sm border border-line bg-white px-4 py-2 font-sans text-sm font-bold text-ink transition-colors hover:bg-surface focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-2 focus-visible:outline-none aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:opacity-40';
+const controlBase =
+ 'inline-flex min-h-[44px] min-w-[44px] items-center justify-center gap-1.5 rounded-sm border border-line bg-white px-4 py-2 font-sans text-sm font-bold text-ink';
+const linkBase = `${controlBase} transition-colors hover:bg-surface focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-2 focus-visible:outline-none`;
+const disabledBase = `${controlBase} cursor-not-allowed opacity-40`;
{#if totalPages > 1}
@@ -25,16 +27,30 @@ const linkBase =
aria-label={ariaLabel ?? m.pagination_nav_label()}
class="mt-6 flex flex-col items-center gap-3 sm:flex-row sm:justify-between"
>
-
- «
- {m.pagination_prev()}
-
+
+ {#if hasPrev}
+
+ «
+ {m.pagination_prev()}
+
+ {:else}
+
+ «
+ {m.pagination_prev()}
+
+ {/if}
-
- {m.pagination_next()}
- »
-
+ {#if hasNext}
+
+ {m.pagination_next()}
+ »
+
+ {:else}
+
+ {m.pagination_next()}
+ »
+
+ {/if}
{/if}
diff --git a/frontend/src/lib/components/Pagination.svelte.spec.ts b/frontend/src/lib/components/Pagination.svelte.spec.ts
index 68af1558..90bf8532 100644
--- a/frontend/src/lib/components/Pagination.svelte.spec.ts
+++ b/frontend/src/lib/components/Pagination.svelte.spec.ts
@@ -33,12 +33,14 @@ describe('Pagination', () => {
await expect.element(prev).toHaveAttribute('href', '/documents?page=3');
});
- it('disables prev on page 0 (no href, aria-disabled="true")', async () => {
+ it('renders disabled prev as an aria-hidden non-link so screen readers skip it', async () => {
render(Pagination, { page: 0, totalPages: 3, makeHref });
const prev = page.getByTestId('pagination-prev');
- await expect.element(prev).toHaveAttribute('aria-disabled', 'true');
+ // Not a link — no href, no role=link
await expect.element(prev).not.toHaveAttribute('href');
+ // Hidden from assistive tech — AT shouldn't read "Previous, link, disabled"
+ await expect.element(prev).toHaveAttribute('aria-hidden', 'true');
});
it('renders next as a link pointing at page + 1 when not on last page', async () => {
@@ -48,12 +50,12 @@ describe('Pagination', () => {
await expect.element(next).toHaveAttribute('href', '/documents?page=1');
});
- it('disables next on the last page (no href, aria-disabled="true")', async () => {
+ it('renders disabled next as an aria-hidden non-link on the last page', async () => {
render(Pagination, { page: 2, totalPages: 3, makeHref });
const next = page.getByTestId('pagination-next');
- await expect.element(next).toHaveAttribute('aria-disabled', 'true');
await expect.element(next).not.toHaveAttribute('href');
+ await expect.element(next).toHaveAttribute('aria-hidden', 'true');
});
it('calls makeHref with p-1 and p+1', async () => {