Context
packages/loopover-ui-kit/src/components/pagination.tsx's PaginationLink (and the PaginationPrevious/PaginationNext components built on it) renders an <a> element and sets aria-current/aria-disabled via the isActive/consumer-supplied props, but never a real HTML disabled attribute — anchors have no such attribute. Every route in apps/loopover-miner-ui that uses it (run-history.tsx, ledgers.tsx, portfolio.tsx, ranked-candidates.tsx, attempts.tsx) sets aria-disabled={safePage === 0} / aria-disabled={safePage >= pageCount - 1} on the boundary PaginationPrevious/PaginationNext, but pagination.tsx applies no styling at all keyed off aria-disabled — the link stays fully opaque, still shows the ghost/outline hover state, and remains keyboard-focusable and "clickable" (the click handlers in each route already no-op safely via Math.max/Math.min clamping, so this is not a functional break, but there is no visual or interaction cue that the control is at a boundary).
This repo already has a working, precedented pattern for exactly this — styling an aria-disabled state on a non-<button> element via Tailwind's aria-disabled: variant — in two other ui-kit components:
packages/loopover-ui-kit/src/components/sidebar.tsx's sidebarMenuButtonVariants and SidebarMenuSubButton: ... aria-disabled:pointer-events-none aria-disabled:opacity-50 ...
packages/loopover-ui-kit/src/components/calendar.tsx: ... aria-disabled:opacity-50 (two call sites)
pagination.tsx has no equivalent.
Requirements
- Add
aria-disabled:pointer-events-none aria-disabled:opacity-50 (or the closest match to pagination.tsx's own existing buttonVariants-derived class list) to PaginationLink's className composition in packages/loopover-ui-kit/src/components/pagination.tsx, so that any consumer passing aria-disabled="true" (as all five miner-ui routes already do at page boundaries) gets a real visual/interaction-blocking effect — not just a screen-reader-only signal.
- Apply this at the
PaginationLink level (not duplicated separately into PaginationPrevious/PaginationNext), since both delegate to it.
- Do not change
PaginationLink's isActive/aria-current behavior, or any consumer's existing aria-disabled={...} prop usage — this is a pagination.tsx-only styling fix.
Deliverables
Test Coverage Requirements
packages/loopover-ui-kit is not in the root vitest.config.ts's coverage.include and is not Codecov-gated — still add the test above per this package's own "suite must run and pass" acceptance bar (packages/loopover-ui-kit/vitest.config.ts).
Expected Outcome
A PaginationPrevious/PaginationNext/PaginationLink marked aria-disabled="true" (as already happens at every miner-ui route's page boundary) visually dims and stops accepting pointer interaction, matching the same aria-disabled: styling convention sidebar.tsx and calendar.tsx already use elsewhere in @loopover/ui-kit.
Links & Resources
packages/loopover-ui-kit/src/components/sidebar.tsx (sidebarMenuButtonVariants, SidebarMenuSubButton — the aria-disabled: pattern to mirror)
packages/loopover-ui-kit/src/components/calendar.tsx (a second existing instance of the same pattern)
apps/loopover-miner-ui/src/routes/run-history.tsx, ledgers.tsx, portfolio.tsx, ranked-candidates.tsx, attempts.tsx (the five consumers already setting aria-disabled with no visual effect today)
Context
packages/loopover-ui-kit/src/components/pagination.tsx'sPaginationLink(and thePaginationPrevious/PaginationNextcomponents built on it) renders an<a>element and setsaria-current/aria-disabledvia theisActive/consumer-supplied props, but never a real HTMLdisabledattribute — anchors have no such attribute. Every route inapps/loopover-miner-uithat uses it (run-history.tsx,ledgers.tsx,portfolio.tsx,ranked-candidates.tsx,attempts.tsx) setsaria-disabled={safePage === 0}/aria-disabled={safePage >= pageCount - 1}on the boundaryPaginationPrevious/PaginationNext, butpagination.tsxapplies no styling at all keyed offaria-disabled— the link stays fully opaque, still shows the ghost/outline hover state, and remains keyboard-focusable and "clickable" (the click handlers in each route already no-op safely viaMath.max/Math.minclamping, so this is not a functional break, but there is no visual or interaction cue that the control is at a boundary).This repo already has a working, precedented pattern for exactly this — styling an
aria-disabledstate on a non-<button>element via Tailwind'saria-disabled:variant — in two otherui-kitcomponents:packages/loopover-ui-kit/src/components/sidebar.tsx'ssidebarMenuButtonVariantsandSidebarMenuSubButton:... aria-disabled:pointer-events-none aria-disabled:opacity-50 ...packages/loopover-ui-kit/src/components/calendar.tsx:... aria-disabled:opacity-50(two call sites)pagination.tsxhas no equivalent.Requirements
aria-disabled:pointer-events-none aria-disabled:opacity-50(or the closest match topagination.tsx's own existingbuttonVariants-derived class list) toPaginationLink's className composition inpackages/loopover-ui-kit/src/components/pagination.tsx, so that any consumer passingaria-disabled="true"(as all five miner-ui routes already do at page boundaries) gets a real visual/interaction-blocking effect — not just a screen-reader-only signal.PaginationLinklevel (not duplicated separately intoPaginationPrevious/PaginationNext), since both delegate to it.PaginationLink'sisActive/aria-currentbehavior, or any consumer's existingaria-disabled={...}prop usage — this is apagination.tsx-only styling fix.Deliverables
aria-disabled:pointer-events-none aria-disabled:opacity-50(or equivalent) added toPaginationLink's class composition inpackages/loopover-ui-kit/src/components/pagination.tsxPaginationLinkrendered witharia-disabled="true"carries the disabled-styling classes and one rendered without it does notTest Coverage Requirements
packages/loopover-ui-kitis not in the rootvitest.config.ts'scoverage.includeand is not Codecov-gated — still add the test above per this package's own "suite must run and pass" acceptance bar (packages/loopover-ui-kit/vitest.config.ts).Expected Outcome
A
PaginationPrevious/PaginationNext/PaginationLinkmarkedaria-disabled="true"(as already happens at every miner-ui route's page boundary) visually dims and stops accepting pointer interaction, matching the samearia-disabled:styling conventionsidebar.tsxandcalendar.tsxalready use elsewhere in@loopover/ui-kit.Links & Resources
packages/loopover-ui-kit/src/components/sidebar.tsx(sidebarMenuButtonVariants,SidebarMenuSubButton— thearia-disabled:pattern to mirror)packages/loopover-ui-kit/src/components/calendar.tsx(a second existing instance of the same pattern)apps/loopover-miner-ui/src/routes/run-history.tsx,ledgers.tsx,portfolio.tsx,ranked-candidates.tsx,attempts.tsx(the five consumers already settingaria-disabledwith no visual effect today)