Skip to content

[Bpk-checkbox-card][CLOV-1271]Adding bpk-checkbox-card#4256

Open
kerrie-wu wants to merge 29 commits intomainfrom
bpk-checkbox-card
Open

[Bpk-checkbox-card][CLOV-1271]Adding bpk-checkbox-card#4256
kerrie-wu wants to merge 29 commits intomainfrom
bpk-checkbox-card

Conversation

@kerrie-wu
Copy link
Contributor

@kerrie-wu kerrie-wu commented Mar 4, 2026

Summary

Introduces bpk-component-checkbox-card — a new Backpack component providing a selectable card UI pattern currently implemented ad-hoc across Hotels, Flights, and Car Hire verticals.

What's included

Compound component API (primary)

Subcomponent Description
BpkCheckboxCard.Root <div role="checkbox"> — state provider, handles click/keyboard
BpkCheckboxCard.Control Hidden <input> for native form submission (aria-hidden)
BpkCheckboxCard.Content Content wrapper with padding
BpkCheckboxCard.Label Primary text, auto-wired to aria-labelledby
BpkCheckboxCard.Description Secondary text, auto-wired to aria-describedby
BpkCheckboxCard.Indicator Decorative check mark

Simple API

BpkCheckboxCardSimple — props-based wrapper for common icon + label + price layouts.

Variants & customisation

  • 3 variants: onCanvasDefault, onCanvasContrast, onSurfaceContrast
  • 2 sizes: sm, md
  • 2 radius options: rounded, square
  • CSS custom property theming via createCheckboxCardTheme / CHECKBOX_CARD_THEME_ATTRIBUTES
  • Controlled (checked / onCheckedChange) and uncontrolled (defaultChecked) modes

Tests & documentation

  • Unit tests, accessibility tests, form integration tests
  • Storybook stories: Basic, WithMultiContent, AllVariants, WithImage, WithIndicator, WithInlineLayout, WithSizes, WithCustomTheme, SimpleAPI, NeighbourhoodCard
  • README and specs updated

Accessibility

  • role="checkbox" with aria-checked, aria-disabled, aria-required
  • Keyboard: Space and Enter toggle selection
  • aria-labelledby / aria-describedby auto-wired from Label/Description subcomponents
  • Disabled cards use tabIndex={-1} and aria-disabled (remain in DOM for AT announcements)

Figma

https://www.figma.com/design/ITvypOGdga42nM2ipBM4uk/Bpk-2.0?node-id=90-7627&m=dev

Remember to include the following changes:

  • Ensure the PR title includes the name of the component you are changing so it's clear in the release notes for consumers of the changes in the version e.g [Clover-123][BpkButton] Updating the colour
  • README.md (If you have created a new component)
  • Component README.md
  • Tests
  • Accessibility tests
    • The following checks were performed:
      • Ability to navigate using a keyboard only
      • Zoom functionality (Deque University explanation):
        • The page SHOULD be functional AND readable when only the text is magnified to 200% of its initial size
        • Pages must reflow as zoom increases up to 400% so that content continues to be presented in only one column i.e. Content MUST NOT require scrolling in two directions (both vertically and horizontally)
      • Ability to navigate using a screen reader only
  • Storybook examples created/updated
  • For breaking changes or deprecating components/properties, migration guides added to the description of the PR. If the guide has large changes, consider creating a new Markdown page inside the component's docs folder and link it here

Copilot AI review requested due to automatic review settings March 4, 2026 08:23
@kerrie-wu kerrie-wu added the minor Non breaking change label Mar 4, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new bpk-component-checkbox-card package to Backpack, implementing a selectable “checkbox card” pattern with a compound-component API, optional theming, and Storybook examples.

Changes:

  • Introduces BpkCheckboxCard compound components (Root, Control, Content, Label, Description, Indicator) plus a BpkCheckboxCardSimple wrapper.
  • Adds styling, tests (unit/form/a11y), Storybook stories/examples, and Figma Code Connect.
  • Adds extensive specification/planning docs under specs/001-checkbox-card/ and updates eslint ignores.

Reviewed changes

Copilot reviewed 36 out of 36 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
typecheck.txt Adds a checked-in typecheck output artifact (should not be committed).
specs/001-checkbox-card/tasks.md Implementation task breakdown for the component.
specs/001-checkbox-card/styling-guide.md Styling guide / token mapping / SCSS patterns.
specs/001-checkbox-card/spec.md Full requirements/specification for checkbox card.
specs/001-checkbox-card/research.md Research notes on existing patterns/components.
specs/001-checkbox-card/plan.md Implementation plan summary.
specs/001-checkbox-card/examples/variants.tsx Spec examples demonstrating visual variants.
specs/001-checkbox-card/examples/interactive-states.tsx Spec examples for interactive states and selection patterns.
specs/001-checkbox-card/examples/edge-cases.tsx Spec examples for edge cases.
specs/001-checkbox-card/examples/basic-usage.tsx Spec examples for basic usage and disabled states.
specs/001-checkbox-card/examples/accessibility.tsx Spec examples for keyboard/form accessibility scenarios.
specs/001-checkbox-card/checklists/requirements.md Spec quality checklist.
specs/001-checkbox-card/api-design.md Proposed API design and usage examples.
packages/bpk-component-checkbox-card/src/themeAttributes.ts Theme attributes + theme factory for BpkThemeProvider.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx Simple wrapper API around the compound component.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/Root.tsx Core state/ARIA handling and interaction logic.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/Label.tsx Label slot component using BpkText.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/Indicator.tsx Optional corner indicator slot.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/Description.tsx Description slot component using BpkText.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/Control.tsx Hidden checkbox input for form submission.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/Content.tsx Content wrapper slot.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/subcomponents/CheckboxCardContext.tsx React context powering the compound API.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/index.ts Re-exports compound component API.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/form-test.tsx Form integration tests.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/common-types.ts Public constants/types for variants/radius/size.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/accessibility-test.tsx jest-axe accessibility tests.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/snapshots/BpkCheckboxCard-test.tsx.snap Snapshots for compound component.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard.tsx Compound component composition/export surface.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard.module.scss CSS Modules styling for the checkbox card.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard.figma.tsx Figma Code Connect integration.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard-test.tsx Unit + interaction tests (click/keyboard/uncontrolled).
packages/bpk-component-checkbox-card/index.ts Package-level exports (compound, simple wrapper, types, theming).
packages/bpk-component-checkbox-card/README.md Component README (currently documents an outdated API).
examples/bpk-component-checkbox-card/stories.tsx Storybook stories wiring.
examples/bpk-component-checkbox-card/examples.tsx Storybook example implementations incl. theming and indicator.
.eslintignore Ignores specs/ from eslint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

disabled
/>,
);
const results = await axe(container);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the “when disabled” test, axe is called without AXE_OPTIONS, while the other cases disable the nested-interactive rule. This inconsistency can cause flaky failures (or hide real issues in other cases). Use the same AXE_OPTIONS consistently across all the tests, or document why the disabled case is intentionally different.

Suggested change
const results = await axe(container);
const results = await axe(container, AXE_OPTIONS);

Copilot uses AI. Check for mistakes.
Comment on lines +262 to +275
const computedAriaLabelledby = ariaLabelledby ?? labelId;
const computedAriaDescribedby = ariaDescribedby ?? descriptionId;

return (
<CheckboxCardContext.Provider value={contextValue}>
<div
role="checkbox"
aria-checked={checked}
aria-disabled={disabled || undefined}
tabIndex={disabled ? -1 : 0}
aria-label={ariaLabel}
aria-labelledby={ariaLabel ? undefined : computedAriaLabelledby}
aria-describedby={computedAriaDescribedby}
className={rootClassName}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aria-describedby and (when aria-label isn’t provided) aria-labelledby default to generated IDs even when no corresponding Description/Label element is rendered. This produces invalid ARIA IDREFs (and an unlabeled checkbox in the “empty content” case). Consider only setting aria-describedby/aria-labelledby when the consumer explicitly provides them, or introduce a mechanism to know whether the Label/Description slots are present (e.g., register via context/useEffect) before wiring up these attributes; otherwise require aria-label for cards without a visible label.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +14

> backpack@0.0.1 typecheck
> tsc

examples/bpk-component-checkbox-card/examples.tsx(22,30): error TS7016: Could not find a declaration file for module '../../packages/bpk-theming'. '/Users/gert-janvercauteren/conductor/workspaces/backpack/copenhagen/packages/bpk-theming/index.js' implicitly has an 'any' type.
examples/bpk-component-checkbox-card/stories.tsx(28,8): error TS2307: Cannot find module './new-api-examples' or its corresponding type declarations.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard-test.tsx(82,41): error TS2339: Property 'withBackground' does not exist on type '{ readonly onCanvasDefault: "on-canvas-default"; readonly onCanvasContrast: "on-canvas-contrast"; readonly onSurfaceContrast: "on-surface-contrast"; }'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard-test.tsx(94,41): error TS2339: Property 'noBackground' does not exist on type '{ readonly onCanvasDefault: "on-canvas-default"; readonly onCanvasContrast: "on-canvas-contrast"; readonly onSurfaceContrast: "on-surface-contrast"; }'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCardPrice.tsx(19,22): error TS7016: Could not find a declaration file for module '../../../bpk-component-price'. '/Users/gert-janvercauteren/conductor/workspaces/backpack/copenhagen/packages/bpk-component-price/index.js' implicitly has an 'any' type.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(156,32): error TS2322: Type '{ children: (false | "" | 0 | Element | null | undefined)[]; orientation: string; align: string; gap: string; }' is not assignable to type 'IntrinsicAttributes & BpkCheckboxCardContentProps'.
Property 'orientation' does not exist on type 'IntrinsicAttributes & BpkCheckboxCardContentProps'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(157,19): error TS2741: Property 'icon' is missing in type '{ children: ReactElement<any, string | JSXElementConstructor<any>>; }' but required in type 'BpkCheckboxCardIconProps'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(167,20): error TS2741: Property 'price' is missing in type '{ children: string | number | true | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal; }' but required in type 'BpkCheckboxCardPriceProps'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(169,24): error TS2339: Property 'Indicator' does not exist on type '(({ children, checked: controlledChecked, defaultChecked, onCheckedChange, disabled, required, name, value, variant, radius, width, height, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, }: BpkCheckboxCardRootProps) => Element) & { Root: ({ children, checked: control...'.
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds typecheck.txt containing local tsc output/errors. This looks like a build artifact rather than a source file, and committing it risks masking the real expectation that the repo should typecheck cleanly. Please remove this file from the PR (and ensure typecheck output is handled by CI rather than checked in).

Copilot uses AI. Check for mistakes.
@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

skyscanner-backpack-bot bot commented Mar 4, 2026

Browser support

If this is a visual change, make sure you've tested it in multiple browsers.

Generated by 🚫 dangerJS against 553e81e

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@kerrie-wu kerrie-wu force-pushed the bpk-checkbox-card branch from c1e435c to 5ed5af9 Compare March 5, 2026 07:37
@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4256 to see this build running in a browser.

@RichardSyq
Copy link
Contributor

Code review

Found 2 issues:

  1. typecheck.txt is a build artifact that should not be committed. It contains raw tsc output including a TypeScript error referencing a local absolute path from a developer's machine (/Users/gert-janvercauteren/...).

backpack/typecheck.txt

Lines 1 to 14 in 553e81e

> backpack@0.0.1 typecheck
> tsc
examples/bpk-component-checkbox-card/examples.tsx(22,30): error TS7016: Could not find a declaration file for module '../../packages/bpk-theming'. '/Users/gert-janvercauteren/conductor/workspaces/backpack/copenhagen/packages/bpk-theming/index.js' implicitly has an 'any' type.
examples/bpk-component-checkbox-card/stories.tsx(28,8): error TS2307: Cannot find module './new-api-examples' or its corresponding type declarations.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard-test.tsx(82,41): error TS2339: Property 'withBackground' does not exist on type '{ readonly onCanvasDefault: "on-canvas-default"; readonly onCanvasContrast: "on-canvas-contrast"; readonly onSurfaceContrast: "on-surface-contrast"; }'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCard-test.tsx(94,41): error TS2339: Property 'noBackground' does not exist on type '{ readonly onCanvasDefault: "on-canvas-default"; readonly onCanvasContrast: "on-canvas-contrast"; readonly onSurfaceContrast: "on-surface-contrast"; }'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCard/BpkCheckboxCardPrice.tsx(19,22): error TS7016: Could not find a declaration file for module '../../../bpk-component-price'. '/Users/gert-janvercauteren/conductor/workspaces/backpack/copenhagen/packages/bpk-component-price/index.js' implicitly has an 'any' type.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(156,32): error TS2322: Type '{ children: (false | "" | 0 | Element | null | undefined)[]; orientation: string; align: string; gap: string; }' is not assignable to type 'IntrinsicAttributes & BpkCheckboxCardContentProps'.
Property 'orientation' does not exist on type 'IntrinsicAttributes & BpkCheckboxCardContentProps'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(157,19): error TS2741: Property 'icon' is missing in type '{ children: ReactElement<any, string | JSXElementConstructor<any>>; }' but required in type 'BpkCheckboxCardIconProps'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(167,20): error TS2741: Property 'price' is missing in type '{ children: string | number | true | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal; }' but required in type 'BpkCheckboxCardPriceProps'.
packages/bpk-component-checkbox-card/src/BpkCheckboxCardSimple/BpkCheckboxCardSimple.tsx(169,24): error TS2339: Property 'Indicator' does not exist on type '(({ children, checked: controlledChecked, defaultChecked, onCheckedChange, disabled, required, name, value, variant, radius, width, height, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, }: BpkCheckboxCardRootProps) => Element) & { Root: ({ children, checked: control...'.

  1. The specs/001-checkbox-card/ directory (~12 files of planning docs, task checklists, and working notes) appears to be development scaffolding that was not intended to be merged. The addition of specs to .eslintignore further indicates these files were never meant as permanent repo content.

coverage
specs
/packages/bpk-component-flare/src/__generated__

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

export default {
title: 'bpk-component-checkbox-card',
component: BpkCheckboxCard,
decorators: [(Story: () => JSX.Element) => <BpkProvider><Story /></BpkProvider>],
Copy link
Contributor

@RichardSyq Richard-Shen (RichardSyq) Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could change this to (Story: any)?

@RichardSyq
Copy link
Contributor

image

It seems this component isn't in the corresponding location in Figma? Could you tell me where I can find it? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor Non breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants