feat(Pagination): add Pagination component aligned with Cyber skin#1555
Open
AlexandraGallipoliRodrigues wants to merge 19 commits into
Open
feat(Pagination): add Pagination component aligned with Cyber skin#1555AlexandraGallipoliRodrigues wants to merge 19 commits into
AlexandraGallipoliRodrigues wants to merge 19 commits into
Conversation
Adds a new Pagination component with desktop and mobile layouts, following the Mistica design specs. - Page navigation with Previous/Next controls (hidden at boundaries) - Configurable visible page window via `dynamicCount` - Optional ellipsis truncation for large page counts - Controlled and uncontrolled modes (`currentPage` / `defaultPage`) - iconOnly mode for compact layouts - Customisable navigation labels (i18n) - Hover state uses neutral `backgroundContainerHover` with 1.06 scale - Current page uses `brandLow` and `textActivated` - Filled chevrons match the Figma stroke weight - Numeric labels use medium weight for visual parity with Figma - Includes playroom snippets and unit tests Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…EB-2441-create-pagination-component
- Use textBrand instead of textLink for Previous/Next so the Cyber skin (where textLink is purple/accent) renders the blue from secondary - Expose Cyber theme in playroom/themes.tsx so it can be selected - Enforce 48x48px touch target on mobile per Figma accessibility spec (the visible 32px circle stays centered inside the larger hit area) - Localise aria-labels via text-tokens (paginationLabel, paginationPrevPage, paginationNextPage, paginationGoToPage) following the Carousel pattern; navLeftLabel / navRightLabel / aria-label props still override the tokens - Update tests to assert the localised aria-labels (default locale es-ES) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Size stats
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new Pagination component to mistica-web, aligned with the Cyber skin while remaining compatible with the standard skin contract, including localized accessibility labels, Playroom support, and unit tests.
Changes:
- Added
Paginationcomponent (controlled/uncontrolled) plusgetPaginationItems()helper. - Introduced new i18n text tokens for pagination aria-labels and actions.
- Added Playroom themes/snippets and Jest tests covering core pagination behaviors.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/text-tokens.tsx |
Adds pagination-related localized text tokens. |
src/pagination.tsx |
Implements the Pagination component and getPaginationItems() logic. |
src/pagination.css.ts |
Adds Vanilla Extract styles for pagination layout, states, and touch targets. |
src/index.tsx |
Exposes Pagination from the public entrypoint. |
src/__tests__/pagination-test.tsx |
Adds unit tests for rendering, interactions, disabled/controlled behavior, and helper edge cases. |
playroom/themes.tsx |
Exposes Cyber theme configs for Playroom preview. |
playroom/snippets.tsx |
Adds Playroom snippets for default and iconOnly pagination. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+231
to
+233
| if (totalPages <= 1) { | ||
| return null; | ||
| } |
| return ( | ||
| <li key={`ellipsis-${index}`} className={styles.pageListItem}> | ||
| <span className={styles.ellipsis} aria-hidden="true"> | ||
| <PaginationLabel weight="medium">...</PaginationLabel> |
|
Deploy preview for mistica-web ready!
Deployed with vercel-action |
|
Accessibility report ℹ️ You can run this locally by executing |
The lint rule testing-library/no-node-access flagged the use of `.closest()` to verify the current page. Switched to asserting that the current page is absent from the buttons accessible tree (it's rendered as a non-interactive span) while sibling pages are buttons. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines
+226
to
+233
| [mq.supportsHover]: { | ||
| selectors: { | ||
| '&:hover': { | ||
| color: skinVars.colors.textBrand, | ||
| }, | ||
| }, | ||
| }, | ||
| }, |
Comment on lines
+250
to
+253
|
|
||
| const activePage = clamp(isControlled ? currentPage : internalPage, 1, totalPages); | ||
|
|
||
| const goToPage = (page: number) => { |
Comment on lines
+152
to
+156
| '&:active:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1)', | ||
| backgroundColor: skinVars.colors.brandLow, | ||
| }, |
Comment on lines
+171
to
+175
| '&:active:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1)', | ||
| backgroundColor: skinVars.colors.brandLow, | ||
| }, |
| } | ||
|
|
||
| if (item.current) { | ||
| // Rendered as a focuseable button with aria-disabled so VO |
Comment on lines
+152
to
+156
| '&:active:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1)', | ||
| backgroundColor: skinVars.colors.brandLow, | ||
| }, |
Comment on lines
+171
to
+175
| '&:active:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1)', | ||
| backgroundColor: skinVars.colors.brandLow, | ||
| }, |
Comment on lines
+88
to
+92
| * 48px on mobile to give a comfortable thumb target. WCAG 2.2 Target Size | ||
| * (Minimum) is satisfied through the spacing exception: 32px circles with a | ||
| * 4px gap between centers (36px apart) easily inscribe non-overlapping 24px | ||
| * circles, so the rule passes even though the literal target width is below | ||
| * 24×24 only in width. |
Comment on lines
+423
to
+427
| export const paginationLabel: TextToken = { | ||
| es: 'Paginación', | ||
| en: 'Pagination', | ||
| de: 'Paginierung', | ||
| pt: 'Paginação', |
If both hideNavigationControls and hidePageList are true, the component still renders an empty <nav> landmark (no buttons / list items). This produces a navigation region with no actionable content; returning null in this configuration avoids confusing/empty landmarks. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
changed import Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Comment on lines
+152
to
+156
| '&:active:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1)', | ||
| backgroundColor: skinVars.colors.brandLow, | ||
| }, |
Comment on lines
+88
to
+92
| * 48px on mobile to give a comfortable thumb target. WCAG 2.2 Target Size | ||
| * (Minimum) is satisfied through the spacing exception: 32px circles with a | ||
| * 4px gap between centers (36px apart) easily inscribe non-overlapping 24px | ||
| * circles, so the rule passes even though the literal target width is below | ||
| * 24×24 only in width. |
| } | ||
|
|
||
| if (item.current) { | ||
| // Rendered as a focuseable button with aria-disabled so VO |
| export {default as HorizontalScroll} from './horizontal-scroll'; | ||
| export {default as Stepper} from './stepper'; | ||
| export {ProgressBar, ProgressBarStepped} from './progress-bar'; | ||
| export {default as Pagination} from './pagination'; |
Comment on lines
+166
to
+170
| '&:hover:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1.06)', | ||
| backgroundColor: skinVars.colors.neutralLow, | ||
| }, |
Comment on lines
+171
to
+175
| '&:active:before': { | ||
| opacity: 1, | ||
| transform: 'translate(-50%, -50%) scale(1)', | ||
| backgroundColor: skinVars.colors.brandLow, | ||
| }, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new
Paginationcomponent to mistica-web, designed for the Cyber project but built against the standard skin contract so it works across all skins. Implements the Figma anatomy, types, behaviour, animation and "Compact View" specs.Ticket: WEB-2441
What's included
Component
Paginationwith controlled and uncontrolled modes (currentPage/defaultPage+onChange).hideNavigationControlsandhidePageListallow rendering only the page list or only the navigation arrows.dynamicCountcontrols the size of the middle visible window;showEllipsistoggles truncation.mode="iconOnly"renders the navigation buttons as chevrons only, even on desktop.disableddisables all interactive elements.getPaginationItems()helper exported for unit tests / consumers that need the resolved item list.Styling
::before) inside a 32×48 (mobile) / 32×32 (desktop) interactive container. The Figma "Interaction Area" spec calls for 48×48 on mobile, but a literal 48px width breaks the layout on a 375px screen for the denser1 … N N+1 N+2 … LASTconfigurations. The component instead satisfies WCAG 2.2 Target Size (Minimum) via the spacing exception: 32px buttons with a 4px gap leave 36px between centers, enough to inscribe non-overlapping 24px touch circles around every interactive element. A long-form comment inpagination.css.tsdocuments this trade-off.neutralLowbackground atscale(1.06)(32 → ~34px). Pressed usesbackgroundContainerPressed. Current page usesbrandLow+textActivatedtext, scale 1.0. Duration 0.2sease-in-out, matching the Figma "Animation" spec.textLink. After the Cyber skin overhaul (master merge)textLink → palette.brand = #0066FF, so the labels render in the blue specified by the Figma without changes to the skin contract.IconChevronLeftRegular/IconChevronRightRegularatsize={20}to match the Figma weight and dimensions.mediumweight to match the Figma typography.hidePageListis set, the container gap becomes 16px to honour the "Previous and Next only" Figma layout.Accessibility
<nav aria-label>landmark with a localised default ("Paginación" / "Pagination" / "Paginierung" / "Paginação").aria-current="page"on the active page (rendered as a non-interactive span, not a button).aria-hidden="true"on the…ellipsis.src/text-tokens.tsx:paginationLabelpaginationPrevPagepaginationNextPagepaginationGoToPage(with1$splaceholder)aria-label,navLeftLabel,navRightLabelprops or via the theme'stextsoverrides, following the same pattern asCarousel.Tooling
playroom/snippets.tsx(Pagination,Pagination iconOnly).playroom/themes.tsxasCommunity_Cyber/Community_Cyber_iOS(light + iOS), so they group visually under theCommunity_prefix in the flat theme selector.src/__stories__/pagination-story.tsx.src/__tests__/pagination-test.tsx(nav landmark, single-page no-render, page click, Next click, disabled, controlled mode + 3getPaginationItemsedge cases).src/__screenshot_tests__/pagination-screenshot-test.tsxcovering 8 scenarios × 2 viewports (Default, FirstPage, LastPage, WithEllipsis, NavOnlyResponsive, PagesOnly, IconOnlyControls, NextChapterLink).Screenshots / verification
Run
yarn playroom, switch to the Cyber skin in the selector, and load thePaginationsnippet. Verify:brandLow) with dark-blue text.Test plan
yarn jest src/__tests__/pagination-test.tsxpasses (9 tests).yarn tsc --noEmitclean.A11y tests
IOS-a11y-test.MP4
Android-a11y-test.mp4
🤖 Generated with Claude Code