Skip to content

[WIP] Add BpkCollapsible component as a standalone package#4457

Closed
Claude wants to merge 1 commit into
mainfrom
claude/add-bpk-collapsible-component
Closed

[WIP] Add BpkCollapsible component as a standalone package#4457
Claude wants to merge 1 commit into
mainfrom
claude/add-bpk-collapsible-component

Conversation

@Claude
Copy link
Copy Markdown

@Claude Claude AI commented May 7, 2026

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.


This section details on the original issue you should resolve

<issue_title>BpkCollapsible</issue_title>
<issue_description>## Context
A new low-level Backpack primitive for expand/collapse content, built on top of Ark UI's Collapsible. Ark UI is already used in Backpack V2 components (BpkSegmentedControlV2, BpkCheckboxV2, etc.), so the dependency is established. This component will be a thin Backpack-styled wrapper that exposes Ark's compound API while applying Backpack tokens, typography, icons, and a11y guarantees.

Figma: Bpk 2.0 Ark — Collapsible
Underlying primitive: @ark-ui/react/collapsible

Decisions

  • Ships as a standalone component package (bpk-component-collapsible).
  • Refactoring BpkAccordion to compose BpkCollapsible is out of scope and will be tracked in a separate follow-up issue.

Anatomy (mirrors Ark UI's compound parts)

  • BpkCollapsible.Root — manages open/closed state
  • BpkCollapsible.Trigger — clickable header row (button)
  • BpkCollapsible.Indicator — chevron, rotates on open
  • BpkCollapsible.Content — animated expandable region

The Figma "trigger row" composition (leading icon + title + label + chevron) is built by consumers from these parts, ensuring full flexibility.

Proposed API

Root — passes through all Ark Collapsible.Root props plus Backpack-specific style:

type BpkCollapsibleRootProps = {
  // Pass-through to Ark Collapsible.Root
  open?: boolean;
  defaultOpen?: boolean;
  onOpenChange?: (details: { open: boolean }) => void;
  onExitComplete?: () => void;
  disabled?: boolean;
  lazyMount?: boolean;
  unmountOnExit?: boolean;
  collapsedHeight?: string | number;
  ids?: Partial<{ root: string; trigger: string; content: string }>;

  // Backpack additions
  style?: 'default' | 'onContrast';   // surface variant from Figma
  className?: string;
  children: ReactNode;
};

Trigger / Indicator / Content — thin styled wrappers around Ark parts; accept className, children, and forward refs.

Export shape

// index.ts
export const BpkCollapsible = {
  Root: BpkCollapsibleRoot,
  Trigger: BpkCollapsibleTrigger,
  Indicator: BpkCollapsibleIndicator,
  Content: BpkCollapsibleContent,
};

Usage example

<BpkCollapsible.Root defaultOpen={false}>
  <BpkCollapsible.Trigger>
    <AirportsIcon />
    <BpkText textStyle="heading5">Title</BpkText>
    <BpkText textStyle="label2">Label</BpkText>
    <BpkCollapsible.Indicator>
      <ChevronDownIcon />
    </BpkCollapsible.Indicator>
  </BpkCollapsible.Trigger>
  <BpkCollapsible.Content>
    Contents go here.
  </BpkCollapsible.Content>
</BpkCollapsible.Root>

Accessibility (inherited from Ark, verified for Backpack)

  • Ark wires aria-expanded, aria-controls, role="region" automatically
  • Ark sets inert on collapsed content so interactive descendants leave the tab order
  • Ark distinguishes open (intent) from visible (in DOM) for animation
  • Backpack additions: Backpack focus ring on the Trigger; aria-hidden on the Indicator; correct RTL handling (chevron position, leading icon)

Animation

  • Drive transitions from Ark's [data-state="open|closed"] and the --height CSS variable Ark exposes on Content
  • Use Backpack duration/easing tokens (bpk-duration-base, standard easing)
  • Indicator rotation 0° → 180°
  • Respect prefers-reduced-motion: reduce — disable transitions

Variants

  • style="default" — standard surface (text-primary-day)
  • style="onContrast" — for use on dark/contrast surfaces (text-on-dark-day); applied to Trigger, Indicator, and Content text

Composition

  • Drops inside BpkCard without extra wrappers (Figma shows nested-in-Card examples)
  • No imposed padding/border — inherits parent surface
  • "Show more" pattern supported via Ark's collapsedHeight

Scope

  • Package bpk-component-collapsible scaffolded per Backpack conventions
  • Compound exports: BpkCollapsible.Root, .Trigger, .Indicator, .Content
  • Pass-through of all Ark Root props (open, defaultOpen, onOpenChange, disabled, lazyMount, unmountOnExit, collapsedHeight, ids)
  • SCSS modules using Backpack tokens & typography mixins (no raw colours/sizes)
  • default and onContrast style variants via root context
  • Animation driven by Ark data-state + --height, with reduced-motion fallback
  • RTL support
  • Storybook: closed, open, with/without icon, with/without label, on contrast, inside BpkCard, controlled, lazyMount, unmountOnExit, collapsedHeight (show more), long content
  • Unit tests (RTL): toggle on click, Enter/Space, controlled mode, disabled, ARIA wiring, reduced-motion path
  • README with usage examples

Out of scope

  • Multi-section grouping (continue to use BpkAccordion)
  • Refactoring BpkAccordion to compose BpkCollapsible (separate follow-up issue)
  • Server-driven async content loading</issue_description>

Comments on the Issue (you are @claude[agent] in this section)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BpkCollapsible

2 participants