Skip to content

feat: set up accordion file structure, API, and a11y#6300

Merged
rise-erpelding merged 28 commits into
swc-1854/accordion-migrationfrom
swc-1857-1858-1859
May 21, 2026
Merged

feat: set up accordion file structure, API, and a11y#6300
rise-erpelding merged 28 commits into
swc-1854/accordion-migrationfrom
swc-1857-1858-1859

Conversation

@rise-erpelding
Copy link
Copy Markdown
Collaborator

@rise-erpelding rise-erpelding commented May 14, 2026

Description

Implements the 2nd-gen <swc-accordion> and <swc-accordion-item> elements through phases 2–4 of the washing-machine workflow: scaffold, public API, and APG-aligned accessibility behavior.

In this PR (part 1 of 2):

  • AccordionBase, AccordionItemBase base classes with full public API
  • SWC element registrations (swc-accordion, swc-accordion-item)
  • Storybook stories: Playground, Overview (accessibility story follows in part 2)
  • Chevron300Icon for xl item sizing
  • Placeholder accordion.css
  • Planning doc updates (migration plan, accessibility analysis)

Deferred to part 2, #6329:

  • accordion.test.ts play test suite (ARIA contract, toggle, exclusive open, disabled, keyboard, heading level)
  • 1st-gen sp-accordion / sp-accordion-item deprecation warnings and matching tests
  • Changeset

Not in this PR: S2 CSS (phase 5), full docs story matrix (phase 7), Playwright/aXe tests (phase 6), consumer migration guide.

Motivation and context

Establishes the 2nd-gen accordion contract before S2 styling. The cancellable swc-accordion-item-toggle event lets the host enforce exclusive-open without items knowing allow-multiple. Host disabled uses parentDisabled state so per-item disabled restores correctly when the accordion re-enables. The open setter is guarded after first render so imperative assignment cannot bypass disabled state.

Related issue(s)

  • fixes SWC-1857 ✅
  • fixes SWC-1858 ✅
  • fixes SWC-1859 ✅
    • fixes SWC-1487 ✅

Screenshots (if appropriate)


Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

Build and Storybook

  • yarn build passes with no accordion errors
  • Storybook: Components > Accordion — Overview and Playground load without console errors (no deprecation warnings expected here)
  • Note that Storybook controls are set up but since styling has not yet been added they may not work as intended: allow-multiple, density, size, level, quiet, disabled all change elements or attributes in the DOM but may not visually have an effect in the UI.

API and toggle

Because CSS is a placeholder, validate open/closed via DevTools (open attribute, hidden on panel).

  • Exclusive open (default): opening one item closes others
  • allow-multiple: multiple items can stay open simultaneously
  • level control updates <h2><h6> in item shadow DOM
  • Host disabled: no items can toggle; per-item disabled preserves correctly after host re-enable
  • Item disabled: header stays focusable; aria-disabled + panel inert; no toggle
  • item.open = true while host or item is disabled does not expand the item (after first render)
  • AccordionItem.focus() and click() delegate to the header button

Keyboard and a11y behavior

  • Space on a header button toggles without page scroll (SWC-1487)
  • Enter toggles via native button click behavior
  • Tab reaches all header buttons in order; disabled headers remain in tab order
  • aria-expanded is "true" on open items, "false" on closed items
  • aria-controls on the button matches id="content" on the panel
  • Panel has role="region" and aria-labelledby="header"
  • Closed panels have hidden attribute
  • Disabled items have aria-disabled="true" (no native disabled) and panel is inert
  • slot="actions" content does not propagate click/keydown to the toggle button

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

Accessibility testing checklist

  • Keyboard: Tab navigates to each header; Space toggles + prevents scroll; Enter toggles; disabled headers reachable but inert; focus moves correctly
  • Screen reader: Heading level announced correctly; expanded/collapsed state announced; disabled item is reachable and announced as dimmed/unavailable; panel content not reachable when hidden or inert

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 14, 2026

⚠️ No Changeset found

Latest commit: 354f1d4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@rise-erpelding rise-erpelding added Status:WIP PR is a work in progress or draft Component:Accordion 2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. labels May 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6300

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@rise-erpelding rise-erpelding changed the title chore: scaffold core package feat: set up accordion file structure, API, and a11y May 14, 2026
Comment thread 2nd-gen/packages/core/components/accordion/Accordion.types.ts Outdated
Comment thread 2nd-gen/packages/core/components/accordion/Accordion.base.ts Outdated
Comment thread 2nd-gen/packages/core/components/accordion/AccordionBase.ts Outdated
Comment thread 2nd-gen/packages/core/components/accordion/AccordionBase.ts Outdated
Comment thread 2nd-gen/packages/core/components/accordion/Accordion.base.ts Outdated
Comment thread 2nd-gen/packages/core/components/accordion/AccordionBase.ts Outdated
Comment thread 2nd-gen/packages/swc/components/accordion/AccordionItem.ts Outdated
Comment thread 2nd-gen/packages/swc/components/accordion/AccordionItem.ts Outdated
Comment thread 2nd-gen/packages/swc/components/accordion/AccordionItem.ts Outdated
@rise-erpelding rise-erpelding force-pushed the swc-1854/accordion-migration branch from 432f56c to 057a70a Compare May 20, 2026 13:33
@rise-erpelding rise-erpelding added Status:Ready for review PR ready for review or re-review. and removed Status:WIP PR is a work in progress or draft labels May 20, 2026
@rise-erpelding rise-erpelding marked this pull request as ready for review May 20, 2026 13:49
@rise-erpelding rise-erpelding requested a review from a team as a code owner May 20, 2026 13:49
Copy link
Copy Markdown
Contributor

@miwha-adobe miwha-adobe left a comment

Choose a reason for hiding this comment

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

lgtm! way to go ✨

Comment thread 2nd-gen/packages/swc/components/accordion/AccordionItem.ts Outdated
Comment thread 2nd-gen/packages/swc/components/accordion/AccordionItem.ts
return;
}
this.open = !this.open;
const event = new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need to emit dedicated open vs close events or at least use a CustomEvent so we can pass the actual open or close state to detail?

I think the system pattern is dedicated events, perhaps even swc-open, swc-after-open, swc-close, swc-after-close but I'm not entirely sure those are all necessary here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I guess the question is, do we need something like swc-open, swc-after-open, swc-close, swc-after-close? Do we need to differentiate between open and close? 🤔

It seems like we used those open/close events in overlay components in 1st-gen, but not in accordion. Are they needed in overlay because of the open/close animations and the time they take or because of something else, like how they can be triggered by hover/focus/other things rather than a click? And if we added some animation to the accordion panel would that change anything?

If we do need open/close events in 2nd-gen, then it seems like a CustomEvent would be a better way to go. If not, using Event and not passing any detail simplifies the implementation compared to its 1st-gen version.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It feels like if we don't at least add detail to say the new/current state post-toggle, that we are putting the burden of "which is it?" on the consumer.

Sometimes folks try to force things like fetching data into an accordion which they'd only want to do on open, for example.

React's version does include some animation, and we would probably want to wait for any transition time to do the after-open just because a user might "cancel" the opening before that completes, so after-open becomes a bit more definitive signal.

You could pattern it after what I have in Tooltip, which also guards against duplicate events when there are more than one transitioning property. (And now makes me think since those are pretty generic maybe we should be sharing among any "visibility toggling" type of components)

}
}

private syncActionsContainerVisibility(event: Event): void {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have ObserveSlotPresence that is probably preferred here (see Badge base and the getter for hasIcon()). However, I'm not sure the check is necessary, depending on changes I suggested for modifying the layout away from a flex container in the style PR. Poooosssssssibly comment out and then check in styling if it's really needed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see events added to the div later so maybe that's why, but see comment there :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point, using our pattern seems easier to reason about, going to remove syncActionsContainerVisibility in favor of ObserveSlotPresence. In that case, should we still worry about what styling is doing?

Comment on lines +138 to +139
@click=${this.stopActionsContainerPropagation}
@keydown=${this.stopActionsContainerPropagation}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would you mind enlightening me on why this is needed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's intended to prevent a click or keydown on the direct actions section from bubbling up to the accordion and unintentionally toggling the accordion item.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, there is an additional check in Accordion.base to make sure the toggle event came from an accordion item. Plus, this div is outside the button, so I'm not sure how that bubble would occur? I'm probably missing some JS knowledge to understand!

I'm just pressing on this in hopes we can remove the need to check if this slot has content, and just allow either flex or grid behavior to eliminate holding space for it rather than toggle hidden.

@rise-erpelding rise-erpelding requested a review from 5t3ph May 21, 2026 17:54
Copy link
Copy Markdown
Contributor

@5t3ph 5t3ph left a comment

Choose a reason for hiding this comment

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

Updates to handle events and simplify the handling for the actions area look good!

We can re-evaluate the need to check slot presence in the styling PR. We also might need to update event handling to be aware of transitions if those are added in styling.

@rise-erpelding rise-erpelding merged commit f741213 into swc-1854/accordion-migration May 21, 2026
26 checks passed
@rise-erpelding rise-erpelding deleted the swc-1857-1858-1859 branch May 21, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. Component:Accordion Status:Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants