From 432f56c0a98cad35cf9f5a543bbb20bfda97e0ea Mon Sep 17 00:00:00 2001 From: rise-erpelding <54716846+rise-erpelding@users.noreply.github.com> Date: Thu, 14 May 2026 15:29:26 -0500 Subject: [PATCH 01/56] docs(accordion): refine planning docs (#6269) Co-authored-by: Nikki Massaro --- .../03_components/README.md | 1 - .../accessibility-migration-analysis.md | 62 ++++- .../03_components/accordion/migration-plan.md | 38 +-- ...endering-and-styling-migration-analysis.md | 225 ------------------ 4 files changed, 77 insertions(+), 249 deletions(-) delete mode 100644 CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/rendering-and-styling-migration-analysis.md diff --git a/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md b/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md index b9bfa27ea3..467337fe38 100644 --- a/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md +++ b/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md @@ -21,7 +21,6 @@ - Accordion - [Accordion accessibility migration analysis](accordion/accessibility-migration-analysis.md) - [Accordion migration plan](accordion/migration-plan.md) - - [Accordion migration roadmap](accordion/rendering-and-styling-migration-analysis.md) - Action Button - [Action button migration roadmap](action-button/rendering-and-styling-migration-analysis.md) - Action Group diff --git a/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/accessibility-migration-analysis.md b/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/accessibility-migration-analysis.md index daf1ef1006..1ff4994bb1 100644 --- a/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/accessibility-migration-analysis.md +++ b/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/accessibility-migration-analysis.md @@ -23,6 +23,7 @@ - [Related 1st-gen accessibility (Jira)](#related-1st-gen-accessibility-jira) - [Recommendations: `` / ``](#recommendations-swc-accordion--swc-accordion-item) - [ARIA roles, states, and properties](#aria-roles-states-and-properties) + - [Header label, optional actions, and Figma vs React Spectrum](#header-label-optional-actions-and-figma-vs-react-spectrum) - [Shadow DOM and cross-root ARIA Issues](#shadow-dom-and-cross-root-aria-issues) - [Accessibility tree expectations](#accessibility-tree-expectations) - [Keyboard and focus](#keyboard-and-focus) @@ -37,14 +38,16 @@ ## Overview -This doc explains how **`swc-accordion`** and **`swc-accordion-item`** should behave for **accessibility**. The target is **WCAG 2.2 Level AA**. It complements the accordion **rendering-and-styling** migration doc and reflects the **accordion** pattern in the [ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/), Deque’s **single** vs **multiselect** examples, and [Heydon Pickering’s collapsible sections](https://inclusive-components.design/collapsible-sections/) guidance (**` +
+
+ ``` + + Any content inside `` — including a sibling div to the button — can bleed into the heading's accessible name, so the actions container must live outside the heading element entirely. **Tab** order stays **disclosure** → **slotted actions** → **panel** when expanded, matching intent from the design thread. +- **Slots:** one for the **section label** (projected into the disclosure **` + +
The bellows is the expandable section in the middle of the accordion.
+
+``` + +- If **`aria-describedby`** targets live in **light DOM** and the disclosure **` + + +
@@ -182,7 +188,7 @@ Inherited: `SizedMixin(Focusable)` — `tabIndex` / `focus` / `blur` / `click` d |---|---| | **Upstream 2nd-gen components** | Accordion does not require another incomplete 2nd-gen composite; it uses **core** + **base** + **icons** patterns. Follow the [badge migration reference](../../02_workstreams/02_2nd-gen-component-migration/02_step-by-step/01_washing-machine-workflow.md#reference-badge-migration) for core/SWC layout. | | **Cross-component API alignment** | **`level`** / heading naming should stay aligned with **illustrated message** and **card** when those specs exist ([accessibility migration analysis](./accessibility-migration-analysis.md)). **`quiet`** / host **`disabled`** naming should match other 2nd-gen components that expose the same Spectrum concepts ([React Spectrum alignment considerations](#react-spectrum-alignment-considerations)). | -| **Step 1 (analyze rendering and styling)** | [Rendering roadmap](./rendering-and-styling-migration-analysis.md) is **in progress** — finish S2 selector and token pass with **spectrum-css** `spectrum-two` beside this repo before treating Step 1 as complete. | +| **Step 1 (analyze rendering and styling)** | Complete — key findings and DOM changes are captured in this plan. | --- @@ -227,18 +233,20 @@ No 2nd-gen package yet — this section records **planned** decisions from analy |---|---|---| | Accordion | `allow-multiple` (or aligned name), **public** **`level`** (`2`–`6`), **`density`**, `size` | **`level`** is the **only** author-facing control for heading depth for all items. **`size`** propagates to assigned items (same as 1st-gen). See **`density`** row. | | Accordion — `density` | Reflected string **`compact`** \| **`regular`** \| **`spacious`** | Align with [React Spectrum **`density`**](https://react-spectrum.adobe.com/Accordion) and S2: **`regular`** is the default spacing (1st-gen **omitted** / legacy default maps here). **TypeScript** and docs should list **all three** values even though **`regular`** is default. **Dev warning** when the attribute is **omitted** is **recommended** (same spirit as Badge **`variant`**) so authors stay explicit—confirm at API freeze. Host-only (1st-gen does **not** assign **`density`** on **`AccordionItem`** in script). | -| Accordion — `quiet` | Boolean; reflected attribute **`quiet`** | Parity with [React Spectrum **`isQuiet`**](https://react-spectrum.adobe.com/Accordion). **Accordion host only** — propagate effective quiet styling to assigned items internally. **Do not** expose **per-item** **`quiet`**: quiet removes dividers between rows; mixing styles per item is **visually chaotic** and **contradicts** Figma usage guidance ([rendering-and-styling migration analysis — Figma](./rendering-and-styling-migration-analysis.md#figma--s2-web-desktop-scale)). | +| Accordion — `quiet` | Boolean; reflected attribute **`quiet`** | Parity with [React Spectrum **`isQuiet`**](https://react-spectrum.adobe.com/Accordion). **Accordion host only** — propagate effective quiet styling to assigned items internally. **Do not** expose **per-item** **`quiet`**: mixing default and quiet items is **visually incompatible**; the quiet hover state uses rounded corners, which creates corner gaps when placed inside a default accordion that uses dividers. Prefer one style family per accordion instance. | | Accordion — `disabled` | Boolean; reflected attribute **`disabled`** | Parity with RS **`isDisabled`** on **`Accordion`**: **accordion-wide** disable — every item non-interactive (no expand/collapse), same **a11y** posture as item-level disable ([accessibility migration analysis](./accessibility-migration-analysis.md): header **`aria-disabled`**, panel **`inert`**). When the host is **`disabled`**, that gate **wins** over per-item **`disabled`** being false. When the host clears **`disabled`**, each item’s own **`disabled`** applies again unchanged. For **visual** disabled state on descendants, prefer **container queries** or host-driven styling so you do **not** reflect host **`disabled`** onto every child **solely** for CSS—only use per-item flags where behavior or a11y requires it. | | Item | `open`, `disabled` | Same semantics as today unless renamed for consistency. **No** public **`quiet`** on the item. | | Item (implementation) | **`protected` `heading`** (`2`–`6`) | **Not** public API—not reflected, not set by consumers. Parent **`level`** assigns **`heading`** on each slotted item (core/SWC lifecycle). | | Heading text | Slotted (see [Shadow DOM output](#shadow-dom-output-rendered-html)) | **Rationale:** a string **`label`** cannot mirror phrasing content (``, ``) into the header’s accessible name the way slotted light DOM can; matches [accessibility migration analysis](./accessibility-migration-analysis.md). **Breaking** vs 1st-gen **`label`**: **clean break** — 2nd-gen does **not** expose **`label`**; authors migrate markup to the heading slot only. | | Events | Renamed toggle event | Exact string TBD. | +| Direct actions (item header affordances) | `slot="actions"` on `swc-accordion-item` (working name — not frozen); open-ended, any content may be slotted | (spectrum-css container class `.spectrum-Accordion-itemDirectActions`) rendered as a **sibling to the `` element** (not inside it); placing it inside `` would bleed its text content into the heading's accessible name. `slotchange` observer hides the container when empty. `stopPropagation` on the container prevents slot clicks from toggling the accordion. Specific supported content (`swc-action-button`, `swc-switch`) are open questions; see [Open — API and scope](#open--api-and-scope). | +| `noInlinePadding` modifier | Not a public attribute | The spectrum-css modifier class `.spectrum-Accordion--noInlinePadding` controls inline padding removal (new in S2). **Not** exposed as an API attribute — expose the relevant padding via `--swc-accordion-*` custom properties so consumers can remove or adjust inline padding directly. | ### React Spectrum alignment considerations [React Spectrum S2 — Accordion](https://react-spectrum.adobe.com/Accordion) exposes **`isQuiet`** and **`isDisabled`** on the **`Accordion`** root (and **`isQuiet`** / **`isDisabled`** on **`AccordionItem`**). 1st-gen **`sp-accordion`** has **neither** host **`quiet`** nor host **`disabled`**; only **`sp-accordion-item`** supports **`disabled`**. For 2nd-gen, treat the following as **API planning targets** so Spectrum authors can mirror React examples in markup: -1. **`quiet`:** Map RS **`isQuiet`** to boolean **`quiet`** on **`swc-accordion`** (Spectrum 2 CSS uses the same name). Propagate styling to items **internally**; **do not** add a public **per-item** **`quiet`** API ([rendering-and-styling migration analysis](./rendering-and-styling-migration-analysis.md#figma--s2-web-desktop-scale)). +1. **`quiet`:** Map RS **`isQuiet`** to boolean **`quiet`** on **`swc-accordion`** (Spectrum 2 CSS uses the same name). Propagate styling to items **internally**; **do not** add a public **per-item** **`quiet`** API. 2. **Accordion-wide `disabled`:** Map RS **`isDisabled`** on **`Accordion`** to **`disabled`** on **`swc-accordion`**. Implementation should drive the same behavior as “every item disabled” without authors having to set **`disabled`** on each item: block toggles, apply quiet/disabled visuals per design, and apply the **disabled item** a11y matrix on **every** header/panel pair. **Controlled-mode** authors who set **`open`** on items should not be able to expand while the host stays **`disabled`** (treat like item-level guardrails today, extended to the whole set). @@ -269,7 +277,6 @@ For **`allow-multiple` false**, the parent must keep **at most one** item **`ope | Document | Use | |---|---| | [Accessibility migration analysis](./accessibility-migration-analysis.md) | WCAG 2.2 AA target, disabled matrix, keyboard, testing expectations. | -| [Rendering-and-styling migration analysis](./rendering-and-styling-migration-analysis.md) | S2 CSS and token mapping — **in progress**; complete S2 sections before Phase 4. | | [Washing machine workflow](../../02_workstreams/02_2nd-gen-component-migration/02_step-by-step/01_washing-machine-workflow.md) | Phase order and quality gates. | --- @@ -294,7 +301,7 @@ Gates align with [01_washing-machine-workflow.md](../../02_workstreams/02_2nd-ge ### Preparation (this ticket) - [ ] Preparation inputs tracked (Figma, epic id, S2 rendering pass) per team process—no separate contributor prep doc for accordion -- [ ] This plan + accessibility analysis + expanded rendering roadmap reviewed +- [ ] This plan + accessibility analysis reviewed - [ ] Breaking changes and consumer migration notes agreed - [ ] Open questions in [Blockers and open questions](#blockers-and-open-questions) resolved or ticketed - [ ] Plan reviewed by at least one other engineer @@ -313,7 +320,7 @@ Gates align with [01_washing-machine-workflow.md](../../02_workstreams/02_2nd-ge ### Styling - [ ] S2 CSS integrated; stylelint clean -- [ ] Document token / `--mod-*` → S2 (or `--swc-*`) mapping for consumers; include **`density` × `size`** matrix ([rendering-and-styling migration analysis](./rendering-and-styling-migration-analysis.md#summary-of-changes)) +- [ ] Document token / `--mod-*` → S2 (or `--swc-*`) mapping for consumers; include **`density` × `size`** matrix ### Accessibility @@ -329,7 +336,8 @@ Gates align with [01_washing-machine-workflow.md](../../02_workstreams/02_2nd-ge ### Documentation -- [ ] JSDoc, usage docs, Storybook stories (include a spacing / **custom properties** story for “no inline padding” style parity—**no** **`show paddings`**-style attribute; see [rendering roadmap — Figma](./rendering-and-styling-migration-analysis.md#figma--s2-web-desktop-scale)) +- [ ] JSDoc, usage docs, Storybook stories (include a spacing / **custom properties** story for “no inline padding” style parity; **no** `noInlinePadding`-style attribute) +- [ ] Add a Storybook story demonstrating inline padding customization via `--swc-accordion-*` custom properties (no `noInlinePadding`-style attribute) - [ ] Do not document arrow-key navigation between headers for 2nd-gen (contrast with legacy README) ### Review @@ -348,11 +356,12 @@ Gates align with [01_washing-machine-workflow.md](../../02_workstreams/02_2nd-ge | **Standalone item** | **Direction:** **`swc-accordion-item`** without a parent stays **supported** with reasonable defaults (**`protected` `heading`** defaults to **`3`**, same as today’s accordion default)—matches existing tests and story patterns. Ticket any change if product requires parent-only usage. | | Heading slot content | Text-only vs inline phrasing (``, ``) in heading slot. | | Toggle event | Exact `swc-*` event name. | -| Rendering doc | Who expands [rendering-and-styling migration analysis](./rendering-and-styling-migration-analysis.md) with S2 paths before Phase 4? | | Chevron / disclosure icon | Prefer **`swc-icon`** internally; finalize icon asset/name against S2. | | Accordion host **`disabled`** | Confirm **controlled** **`open`** cannot expand while host **`disabled`**; prefer **container-query** / host styling for descendant disabled visuals ([Public API](#public-api) **`disabled`** note). | | **`density`** dev warning | Confirm **omit-attribute** warning ships with accordion (recommended; same spirit as Badge **`variant`**). | -| **`RadioController`** scope | Ship **inside** `AccordionBase` first vs extract to **core** for **radio group**, **`role="menuitemradio"`** menus, **tabs**, and/or coordinate with a **refactor** of **`FocusgroupNavigationController`** (split “selection flags” vs “focus roving”)? **Depends on** menu / radio / tabs migration timing and whether teams want one shared **selection-sync** API vs local loops per component. | +| **Direct actions — disabled state** | **Decided:** Do **not** propagate `disabled` to slotted actions content. The actions slot may hold affordances whose purpose is precisely to explain or resolve why the item is disabled (e.g., an “Upgrade” button, or an action button attached to a popover describing deprecation). Coupling disablement would remove those affordances exactly when they are most needed. | +| **Direct actions — content constraints** | Slot is open-ended by design. Confirm whether to add a dev-mode warning for unsupported content types when `swc-action-button` and `swc-switch` are available, or leave fully unrestricted. | +| **`RadioController`** scope | Ship **inside** `AccordionBase` first vs extract to **core** for **radio group**, **`role=”menuitemradio”`** menus, **tabs**, and/or coordinate with a **refactor** of **`FocusgroupNavigationController`** (split “selection flags” vs “focus roving”)? **Depends on** menu / radio / tabs migration timing and whether teams want one shared **selection-sync** API vs local loops per component. | **Not a blocker:** Missing 2nd-gen package before implementation starts is expected. @@ -370,7 +379,6 @@ Gates align with [01_washing-machine-workflow.md](../../02_workstreams/02_2nd-ge - [Washing machine workflow](../../02_workstreams/02_2nd-gen-component-migration/02_step-by-step/01_washing-machine-workflow.md) - [Migration project planning (epics / tickets)](../../02_workstreams/02_2nd-gen-component-migration/03_migration-project-planning.md) - [Accessibility migration analysis](./accessibility-migration-analysis.md) -- [Rendering and styling migration analysis](./rendering-and-styling-migration-analysis.md) - [1st-gen source — `Accordion.ts`](../../../../1st-gen/packages/accordion/src/Accordion.ts) - [1st-gen source — `AccordionItem.ts`](../../../../1st-gen/packages/accordion/src/AccordionItem.ts) - [1st-gen tests directory](../../../../1st-gen/packages/accordion/test/) diff --git a/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/rendering-and-styling-migration-analysis.md b/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/rendering-and-styling-migration-analysis.md deleted file mode 100644 index 5f32eaa712..0000000000 --- a/CONTRIBUTOR-DOCS/03_project-planning/03_components/accordion/rendering-and-styling-migration-analysis.md +++ /dev/null @@ -1,225 +0,0 @@ - - -[CONTRIBUTOR-DOCS](../../../README.md) / [Project planning](../../README.md) / [Components](../README.md) / Accordion / Accordion migration roadmap - - - -# Accordion migration roadmap - - - -
-In this doc - -- [Component specifications](#component-specifications) - - [CSS (1st-gen / Spectrum 1)](#css-1st-gen--spectrum-1) - - [Figma — S2 Web (desktop scale)](#figma--s2-web-desktop-scale) - - [SWC (1st-gen)](#swc-1st-gen) -- [Comparison](#comparison) - - [DOM structure changes](#dom-structure-changes) - - [CSS => SWC mapping](#css--swc-mapping) -- [Summary of changes](#summary-of-changes) -- [Resources](#resources) - -
- - - -Spectrum 2 CSS-to-SWC migration notes for **`swc-accordion`** and **`swc-accordion-item`**. For accessibility behavior (APG accordion pattern, headings, keyboard), see [Accordion accessibility migration analysis](./accessibility-migration-analysis.md). - -**Status:** Phase 1 prep — 1st-gen inventory captured below. **Next:** With **spectrum-css** checked out on **`spectrum-two`** beside this repo, complete selector extraction from component `metadata.json`, three-way DOM comparison, and token mapping per [Analyze rendering and styling](../../02_workstreams/02_2nd-gen-component-migration/02_step-by-step/01_analyze-rendering-and-styling/README.md). - ---- - -## Component specifications - -### CSS (1st-gen / Spectrum 1) - -1st-gen packages import generated Spectrum CSS: - -- [`1st-gen/packages/accordion/src/spectrum-accordion.css`](../../../../1st-gen/packages/accordion/src/spectrum-accordion.css) — host tokens, size/density variants, `--spectrum-logical-rotation` for disclosure. -- [`1st-gen/packages/accordion/src/spectrum-accordion-item.css`](../../../../1st-gen/packages/accordion/src/spectrum-accordion-item.css) — `:host`, `#heading`, `#header`, `#content`, `.iconContainer`, state variants (`[open]`, `[disabled]`, hover, focus-visible). - -
-Modifier highlights (`--mod-*` / `--spectrum-accordion-*`) - -Representative tokens consumed in 1st-gen (not exhaustive — extract full list from Spectrum CSS when completing this doc): - -- `--mod-accordion-item-width`, `--mod-accordion-item-height`, `--mod-accordion-min-block-size` -- `--mod-accordion-divider-color`, `--mod-accordion-divider-thickness` -- `--mod-accordion-disclosure-indicator-height`, `--mod-accordion-edge-to-disclosure-indicator-space`, `--mod-accordion-disclosure-indicator-to-text-space`, `--mod-accordion-edge-to-text-space` -- `--mod-accordion-item-header-*` (padding, font, line-height, colors) -- `--mod-accordion-item-content-*` (padding, font, color) -- `--mod-accordion-background-color-*`, `--mod-accordion-corner-radius`, `--mod-accordion-focus-indicator-*` -- System overrides in [`accordion-overrides.css`](../../../../1st-gen/packages/accordion/src/accordion-overrides.css) - -
- -**TBD (Spectrum 2):** Mirror this section from `spectrum-css` **`spectrum-two`** accordion component (`metadata.json`, `index.css`, stories template) when the sibling checkout is available. Use [Figma — S2 Web (desktop scale)](#figma--s2-web-desktop-scale) below as the **design** source until that extraction is complete; reconcile any delta with `metadata.json` line by line (do not assume Figma and CSS stay in lockstep without verification). - -### Figma — S2 Web (desktop scale) - -**Canonical link (dev mode):** [S2 — Web (Desktop scale) — Accordion](https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Web--Desktop-scale-?node-id=39469-5419&p=f&m=dev) (`node-id=39469-5419`). - -The following is transcribed from the **published Accordion** page in that file (export reviewed May 2025; doc stamp **Last updated May 16, 2025** / Kami F.). Treat Figma as **visual and variant inventory**; **coded** selectors and tokens still come from **spectrum-css** `spectrum-two` when available. - -#### Definition - -An accordion shows a list of items that can be expanded or collapsed to reveal more content. The pattern can support **zero, one, or multiple** expanded items at a time (aligns quantity-of-open with product API such as [`allowsMultipleExpanded`](https://react-spectrum.adobe.com/Accordion), not with “quiet vs default” styling). - -
-Accordion (root) — properties in Figma - -| Figma property | Role (from doc copy) | -|---|---| -| **Quiet** | Boolean — change appearance / communication of status | -| **Variant** | Variant control on the accordion set (see item matrix for spacing/style family) | - -Figma defaults shown in the property table include **Quiet = False** for the accordion-level control where listed. - -
- -
-Accordion item — properties in Figma - -| Figma property | Role (from doc copy) | -|---|---| -| **State** | **Open** (and interactive states shown in grids — **Hover**, **Down**, **Disabled**) | -| **Quiet** | Boolean — aligns with accordion “quiet” visual family | -| **Density** | **Compact**, **Regular**, **Spacious** — “change density” between items | -| **Show paddings** | Toggle — show/hide padding guides (design-tool only; corresponds to **no inline padding** style in Spectrum CSS — **not** a public SWC prop) | -| **Show direct actions** | Toggle — show/hide **direct actions** region (optional chrome next to title) | -| **Show switch** | Toggle — show/hide **switch** in the header row | -| **Show action button** | Toggle — show/hide **action button** | -| **@ Title** | Text — title string | -| **Instance swap** | Local component swaps where applicable | - -**Migration implication:** Optional header affordances (**direct actions**, **switch**, **action button**) match the direction of richer headers in React Spectrum S2 (**`AccordionItemHeader`**, action controls). 1st-gen SWC uses a single header label + chevron only; **2nd-gen** may need **named slots** or internal structure once S2 template and a11y review land (see [migration plan](./migration-plan.md) and [accessibility analysis](./accessibility-migration-analysis.md)). - -**Density note:** Figma places **Density** on the **item** component with three steps (**Compact** / **Regular** / **Spacious**). 1st-gen reflects **`density`** on **`sp-accordion`** only (`compact` \| `spacious` \| unset). Reconcile host vs item during Step 1 when comparing `metadata.json` to this file — [migration plan — `density`](./migration-plan.md#public-api) defines reflected **`regular`** and full typing parity with React Spectrum. - -**Show paddings:** Treat as documentation and **CSS custom-property** coverage only ([migration plan](./migration-plan.md) — spacing overrides + a dedicated Storybook story, **no** component attribute). - -
- -
-Sizes, states, and styles (Figma matrices) - -- **Sizes:** **S**, **M**, **L**, **XL** (maps to existing SWC **`size`** scale). -- **States (interactive):** **Default**, **Hover**, **Down**, **Disabled** across the published grids. -- **Style families:** **Default** vs **Quiet**, crossed with **open/closed** and **density** rows (**Compact**, **Regular**, **Spacious**) in the canvases. - -**Figma caveat:** Keyboard **focus** state is **not** represented in the file; the spec points authors to Spectrum **coded** components and site docs for focus treatment. - -
- -#### Usage guidelines (from Figma — do not mix) - -- **Do not mix default and quiet accordion items inside one accordion.** Default accordions must not contain quiet items, and quiet accordions must not contain default items — the doc states this avoids **conflicting interaction behaviors**. -- **Quiet hover and dividers:** The quiet accordion item hover state uses **rounded corners**. Using that inside a **default** accordion (with dividers) produces **gaps at corners** that the default divider treatment does not fill; only **keyboard focus** outline is expected to read similarly against dividers. **Prefer one style family per accordion** for both UX and markup/CSS predictability. - -This reinforces **accordion-wide `quiet`** (and consistent items) as the primary authoring model for 2nd-gen; avoid advertising **per-item `quiet`** that could violate this guidance unless Spectrum explicitly documents an exception ([migration plan](./migration-plan.md)). - -### SWC (1st-gen) - -
-Attributes / properties (`sp-accordion`) - -- `allow-multiple` (boolean) -- `density` (`compact` | `spacious`) -- `level` (number, default 3) -- `size` (`s` | `m` | `l` | `xl`) - -
- -
-Attributes / properties (`sp-accordion-item`) - -- `open` (boolean) -- `label` (string) -- `disabled` (boolean) -- `level` (number — overwritten by parent when slotted) -- `size` (from parent) - -
- -
-Slots - -- **`sp-accordion`:** default — `sp-accordion-item` children. -- **`sp-accordion-item`:** default — panel body. - -
- -
-Nested components / assets - -- 1st-gen: `sp-icon-chevron100` from `@spectrum-web-components/icons-ui` and chevron styles from `@spectrum-web-components/icon` -- 2nd-gen: prefer **`swc-icon`** internally for the disclosure indicator ([migration plan](./migration-plan.md)) - -
- ---- - -## Comparison - -### DOM structure changes - -
-Spectrum Web Components (1st-gen `AccordionItem`) - -Conceptual shadow output: - -```html -

- - -

-
- -
-``` - -**`sp-accordion`:** single default ``; parent coordinates items via events and assigned nodes. - -
- -
-Spectrum 2 (TBD — CSS template) - -Paste or link the S2 template markup from **spectrum-css** `spectrum-two` when available. Compare heading/button/slot structure and class names to the above. - -**Design reference (until template is pasted):** [Figma — S2 Web (desktop scale)](#figma--s2-web-desktop-scale) — sizes **S–XL**, **Default/Quiet**, **Compact/Regular/Spacious** density, optional header actions/switch/button, and **do-not-mix** quiet vs default usage. - -
- -### CSS => SWC mapping - -**TBD.** Populate during Step 1 QA: map S2 selectors and `--mod-*` successors to 2nd-gen host parts, internal nodes, and any supported `--swc-accordion-*` surface per [component custom property exposure](../../../02_style-guide/01_css/02_custom-properties.md#component-custom-property-exposure). - ---- - -## Summary of changes - -| Area | 1st-gen today | 2nd-gen direction (high level) | -|---|---|---| -| Styling source | Spectrum 1 generated CSS in package | Spectrum 2 tokens from **spectrum-css** `spectrum-two`; narrow public customization. | -| DOM | `#heading` / `#header` / `#content`, optional chevron wrapper | Align with S2 template; preserve APG shape from [accessibility migration analysis](./accessibility-migration-analysis.md). | -| Quiet vs default | No `quiet` on accordion or item | **Figma / S2:** accordion-level **Quiet** and item-level quiet must **not** be mixed with the opposite style inside one accordion ([Figma section](#figma--s2-web-desktop-scale)). Implement **`quiet`** as a single family per instance ([migration plan](./migration-plan.md)). | -| Header chrome | Title + chevron only | **Figma** shows optional **direct actions**, **switch**, **action button** toggles — likely **slots** or subregions; align with [React Spectrum `AccordionItemHeader`](https://react-spectrum.adobe.com/Accordion) when scoping Phase 5/7. | -| Density / size | `density` + `size` on accordion, chevron scales by size | **Figma** item **Density:** **Compact** / **Regular** / **Spacious**; sizes **S–XL**. Reconcile host vs item propagation with `metadata.json` + [migration plan `density`](./migration-plan.md#public-api). | -| States | `open`, `disabled`, hover/focus in CSS | Match **Default / Hover / Down / Disabled** from Figma; **focus-visible** not in Figma — follow APG + [accessibility analysis](./accessibility-migration-analysis.md). | -| Custom properties | Broad `--spectrum-accordion-*` / `--mod-*` | Replace with S2 equivalents; document breaking token renames in [migration plan](./migration-plan.md). | - ---- - -## Resources - -| Resource | Link | -|---|---| -| Figma — S2 Web (Desktop scale), Accordion | [figma.com/design/Mngz9H7WZLbrCvGQf3GnsY](https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Web--Desktop-scale-?node-id=39469-5419&p=f&m=dev) | -| 1st-gen accordion package | [`1st-gen/packages/accordion/`](../../../../1st-gen/packages/accordion/) | -| Migration plan | [migration-plan.md](./migration-plan.md) | -| Accessibility analysis | [accessibility-migration-analysis.md](./accessibility-migration-analysis.md) | -| Analyze rendering and styling (workflow) | [README](../../02_workstreams/02_2nd-gen-component-migration/02_step-by-step/01_analyze-rendering-and-styling/README.md) | -| Spectrum CSS (external) | [github.com/adobe/spectrum-css](https://github.com/adobe/spectrum-css) — use **`spectrum-two`** branch | From 09cd821f13ce48cc20589509f4fc41d7b6ca2f8b Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:40:56 -0500 Subject: [PATCH 02/56] chore: scaffold core package --- .../components/accordion/Accordion.types.ts | 19 +++++++++++++++++++ .../components/accordion/AccordionBase.ts | 15 +++++++++++++++ .../accordion/AccordionItem.types.ts | 13 +++++++++++++ .../components/accordion/AccordionItemBase.ts | 15 +++++++++++++++ .../core/components/accordion/index.ts | 15 +++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 2nd-gen/packages/core/components/accordion/Accordion.types.ts create mode 100644 2nd-gen/packages/core/components/accordion/AccordionBase.ts create mode 100644 2nd-gen/packages/core/components/accordion/AccordionItem.types.ts create mode 100644 2nd-gen/packages/core/components/accordion/AccordionItemBase.ts create mode 100644 2nd-gen/packages/core/components/accordion/index.ts diff --git a/2nd-gen/packages/core/components/accordion/Accordion.types.ts b/2nd-gen/packages/core/components/accordion/Accordion.types.ts new file mode 100644 index 0000000000..44b0aba7eb --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/Accordion.types.ts @@ -0,0 +1,19 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +export const ACCORDION_VALID_SIZES = ['s', 'm', 'l', 'xl'] as const; +export type AccordionSize = (typeof ACCORDION_VALID_SIZES)[number]; + +export const ACCORDION_DENSITIES = ['compact', 'regular', 'spacious'] as const; +export type AccordionDensity = (typeof ACCORDION_DENSITIES)[number]; + +export const SWC_ACCORDION_ITEM_TOGGLE_EVENT = 'swc-accordion-item-toggle'; diff --git a/2nd-gen/packages/core/components/accordion/AccordionBase.ts b/2nd-gen/packages/core/components/accordion/AccordionBase.ts new file mode 100644 index 0000000000..3b35a08de9 --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/AccordionBase.ts @@ -0,0 +1,15 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; + +export abstract class AccordionBase extends SpectrumElement {} diff --git a/2nd-gen/packages/core/components/accordion/AccordionItem.types.ts b/2nd-gen/packages/core/components/accordion/AccordionItem.types.ts new file mode 100644 index 0000000000..349458211e --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/AccordionItem.types.ts @@ -0,0 +1,13 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +// Item-specific types go here as the accordion item API is defined in subsequent commits. diff --git a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts new file mode 100644 index 0000000000..5d2a344f87 --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts @@ -0,0 +1,15 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; + +export abstract class AccordionItemBase extends SpectrumElement {} diff --git a/2nd-gen/packages/core/components/accordion/index.ts b/2nd-gen/packages/core/components/accordion/index.ts new file mode 100644 index 0000000000..a725fdc5fe --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/index.ts @@ -0,0 +1,15 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +export * from './AccordionBase.js'; +export * from './AccordionItemBase.js'; +export * from './Accordion.types.js'; From bba2e18c90056a0a95d0e79adb1f8acd3cb1d4cf Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:48:07 -0500 Subject: [PATCH 03/56] chore: scaffold swc package --- .../swc/components/accordion/Accordion.ts | 38 +++++++++++++++++++ .../swc/components/accordion/AccordionItem.ts | 36 ++++++++++++++++++ .../swc/components/accordion/accordion.css | 13 +++++++ .../swc/components/accordion/index.ts | 14 +++++++ .../accordion/swc-accordion-item.ts | 23 +++++++++++ .../swc/components/accordion/swc-accordion.ts | 23 +++++++++++ 6 files changed, 147 insertions(+) create mode 100644 2nd-gen/packages/swc/components/accordion/Accordion.ts create mode 100644 2nd-gen/packages/swc/components/accordion/AccordionItem.ts create mode 100644 2nd-gen/packages/swc/components/accordion/accordion.css create mode 100644 2nd-gen/packages/swc/components/accordion/index.ts create mode 100644 2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts create mode 100644 2nd-gen/packages/swc/components/accordion/swc-accordion.ts diff --git a/2nd-gen/packages/swc/components/accordion/Accordion.ts b/2nd-gen/packages/swc/components/accordion/Accordion.ts new file mode 100644 index 0000000000..3f15bb156e --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/Accordion.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { CSSResultArray, html, TemplateResult } from 'lit'; + +import { AccordionBase } from '@spectrum-web-components/core/components/accordion'; + +import styles from './accordion.css'; + +/** + * An accordion component that groups expandable content sections. + * + * @element swc-accordion + * @since 2.0.0 + * + * @example + * + * Content + * + */ +export class Accordion extends AccordionBase { + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + return html``; + } +} diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts new file mode 100644 index 0000000000..b59479c7ae --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { CSSResultArray, html, TemplateResult } from 'lit'; + +import { AccordionItemBase } from '@spectrum-web-components/core/components/accordion'; + +import styles from './accordion.css'; + +/** + * An accordion item component that wraps a single expandable content section. + * + * @element swc-accordion-item + * @since 2.0.0 + * + * @example + * Content + */ +export class AccordionItem extends AccordionItemBase { + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + return html``; + } +} diff --git a/2nd-gen/packages/swc/components/accordion/accordion.css b/2nd-gen/packages/swc/components/accordion/accordion.css new file mode 100644 index 0000000000..40f33594a2 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/accordion.css @@ -0,0 +1,13 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +/* S2 token imports and selector definitions land here in Phase 4. */ diff --git a/2nd-gen/packages/swc/components/accordion/index.ts b/2nd-gen/packages/swc/components/accordion/index.ts new file mode 100644 index 0000000000..f8f7131062 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/index.ts @@ -0,0 +1,14 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +export * from './Accordion.js'; +export * from './AccordionItem.js'; diff --git a/2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts b/2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts new file mode 100644 index 0000000000..cd9853027e --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts @@ -0,0 +1,23 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { defineElement } from '@spectrum-web-components/core/element/index.js'; + +import { AccordionItem } from './AccordionItem.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-accordion-item': AccordionItem; + } +} + +defineElement('swc-accordion-item', AccordionItem); diff --git a/2nd-gen/packages/swc/components/accordion/swc-accordion.ts b/2nd-gen/packages/swc/components/accordion/swc-accordion.ts new file mode 100644 index 0000000000..d51f7f1b1e --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/swc-accordion.ts @@ -0,0 +1,23 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { defineElement } from '@spectrum-web-components/core/element/index.js'; + +import { Accordion } from './Accordion.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-accordion': Accordion; + } +} + +defineElement('swc-accordion', Accordion); From 87bdc5e9ae0d18701ca97108b84f98aa6bcd403c Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:54:09 -0500 Subject: [PATCH 04/56] chore: add Chevron300Icon for xl accordion --- .../icon/elements/Chevron300Icon.ts | 22 +++++++++++++++++++ .../swc/components/icon/elements/index.ts | 1 + 2 files changed, 23 insertions(+) create mode 100644 2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts diff --git a/2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts b/2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts new file mode 100644 index 0000000000..72956bf4b1 --- /dev/null +++ b/2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { html, TemplateResult } from 'lit'; + +export const Chevron300Icon = (): TemplateResult => { + return html` + + + + `; +}; diff --git a/2nd-gen/packages/swc/components/icon/elements/index.ts b/2nd-gen/packages/swc/components/icon/elements/index.ts index b30f66ddd2..8d6cf638b2 100644 --- a/2nd-gen/packages/swc/components/icon/elements/index.ts +++ b/2nd-gen/packages/swc/components/icon/elements/index.ts @@ -20,6 +20,7 @@ export * from './Chevron50Icon.js'; export * from './Chevron75Icon.js'; export * from './Chevron100Icon.js'; export * from './Chevron200Icon.js'; +export * from './Chevron300Icon.js'; export * from './CornerTriangle300Icon.js'; export * from './Cross75Icon.js'; export * from './Cross100Icon.js'; From 570c1381b74ce1e335bd207a75618c090e5f087e Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:58:12 -0500 Subject: [PATCH 05/56] chore: set up storybook story --- .../accordion/stories/accordion.stories.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts diff --git a/2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts b/2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts new file mode 100644 index 0000000000..a6f2c6bb15 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts @@ -0,0 +1,65 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/accordion/swc-accordion.js'; +import '@adobe/spectrum-wc/components/accordion/swc-accordion-item.js'; + +// ──────────────── +// METADATA +// ──────────────── + +/** + * An accordion groups related content sections, each behind a header that can + * be expanded or collapsed. Args, argTypes, and controls are added in a later commit + * once the component API is defined. + */ +const meta: Meta = { + title: 'Accordion', + component: 'swc-accordion', + parameters: { + docs: { + subtitle: 'Groups related content sections behind expandable headers.', + }, + }, + tags: ['migrated'], +}; + +export default meta; + +// ──────────────────── +// AUTODOCS STORY +// ──────────────────── + +export const Playground: Story = { + render: () => html` + + Sample content + + `, + tags: ['autodocs', 'dev'], +}; + +// ────────────────────────── +// OVERVIEW STORY +// ────────────────────────── + +export const Overview: Story = { + render: () => html` + + Sample content + + `, + tags: ['overview'], +}; From 6e71c8db2f2fb162f2b72ce7e2bc79544ee76234 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:58:31 -0500 Subject: [PATCH 06/56] chore: commit preview.ts updates --- 2nd-gen/packages/swc/.storybook/preview.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/2nd-gen/packages/swc/.storybook/preview.ts b/2nd-gen/packages/swc/.storybook/preview.ts index 66d2cc73e7..4d0ec66f80 100644 --- a/2nd-gen/packages/swc/.storybook/preview.ts +++ b/2nd-gen/packages/swc/.storybook/preview.ts @@ -321,11 +321,7 @@ const preview = { 'Components', [ 'Accordion', - [ - 'Accessibility migration analysis', - 'Migration plan', - 'Rendering and styling migration analysis', - ], + ['Accessibility migration analysis', 'Migration plan'], 'Action button', ['Rendering and styling migration analysis'], 'Action group', From bc7bf870daba2a71c8f2cbf7edd73e3f425663c4 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 17:12:40 -0500 Subject: [PATCH 07/56] feat: implement AccordionItem API and render template --- .../components/accordion/AccordionItemBase.ts | 73 +++++++++++++++++- .../swc/components/accordion/AccordionItem.ts | 77 ++++++++++++++++++- 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts index 5d2a344f87..3aa2f07483 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts @@ -10,6 +10,77 @@ * governing permissions and limitations under the License. */ +import { property } from 'lit/decorators.js'; + import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; -export abstract class AccordionItemBase extends SpectrumElement {} +import { + type AccordionSize, + SWC_ACCORDION_ITEM_TOGGLE_EVENT, +} from './Accordion.types.js'; + +/** + * Base class for accordion item components. Manages open/disabled state, + * heading level (set by the parent accordion), and the toggle event. + * + * @slot label - The heading text for this accordion item. + * @slot actions - Optional actions rendered adjacent to the heading, outside + * the toggle button so they remain independently interactive. + * @slot - The panel content revealed when the item is open. + */ +export abstract class AccordionItemBase extends SpectrumElement { + // ────────────────── + // PUBLIC API + // ────────────────── + + /** + * Whether the accordion item panel is expanded. + */ + @property({ type: Boolean, reflect: true }) + public open: boolean = false; + + /** + * Whether the accordion item is disabled. A disabled item keeps its header + * in the tab order but blocks toggling. + */ + @property({ type: Boolean, reflect: true }) + public disabled: boolean = false; + + /** + * The size of the item. Inherited from the parent accordion; controls which + * chevron icon is displayed. Has no effect when the item is used standalone. + */ + @property({ type: String, reflect: true }) + public size?: AccordionSize; + + // ────────────────────── + // INTERNAL STATE + // ────────────────────── + + /** + * @internal + * Heading level (2–6) propagated by the parent accordion. Defaults to 3 + * for standalone items. Used by the concrete render() to emit the correct + * element; made reactive in a later commit. + */ + protected heading: number = 3; + + // ────────────────────── + // IMPLEMENTATION + // ────────────────────── + + /** + * @internal + * Dispatches the toggle event. The concrete class wires this to the + * header button's click handler and adds open-state management. + */ + protected toggle(): void { + this.dispatchEvent( + new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, { + bubbles: true, + composed: true, + cancelable: true, + }) + ); + } +} diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts index b59479c7ae..61cd39872c 100644 --- a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -14,6 +14,13 @@ import { CSSResultArray, html, TemplateResult } from 'lit'; import { AccordionItemBase } from '@spectrum-web-components/core/components/accordion'; +import '../icon/swc-icon.js'; + +import { Chevron75Icon } from '../icon/elements/Chevron75Icon.js'; +import { Chevron100Icon } from '../icon/elements/Chevron100Icon.js'; +import { Chevron200Icon } from '../icon/elements/Chevron200Icon.js'; +import { Chevron300Icon } from '../icon/elements/Chevron300Icon.js'; + import styles from './accordion.css'; /** @@ -23,14 +30,80 @@ import styles from './accordion.css'; * @since 2.0.0 * * @example - * Content + * + * Section heading + * Panel content goes here. + * */ export class AccordionItem extends AccordionItemBase { + // ────────────────────────────── + // RENDERING & STYLING + // ────────────────────────────── + public static override get styles(): CSSResultArray { return [styles]; } + private chevronForSize(): TemplateResult { + switch (this.size) { + case 's': + return Chevron75Icon(); + case 'l': + return Chevron200Icon(); + case 'xl': + return Chevron300Icon(); + case 'm': + default: + return Chevron100Icon(); + } + } + + private handleActionsSlotChange(event: Event): void { + const slot = event.target as HTMLSlotElement; + const container = slot.parentElement as HTMLElement | null; + if (container) { + container.hidden = slot.assignedNodes({ flatten: true }).length === 0; + } + } + + private handleActionsContainerInteraction(event: Event): void { + event.stopPropagation(); + } + protected override render(): TemplateResult { - return html``; + //

is a static stub; the heading level becomes dynamic in a later commit. + return html` +

+ +

+ +
+ +
+ `; } } From f94b6ce650218fcc701ae3c5cd1613e48538e582 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Fri, 15 May 2026 08:10:12 -0500 Subject: [PATCH 08/56] feat: implement Accordion API and propagation --- .../components/accordion/AccordionBase.ts | 112 +++++++++++++++++- .../components/accordion/AccordionItemBase.ts | 5 +- .../swc/components/accordion/Accordion.ts | 13 +- 3 files changed, 120 insertions(+), 10 deletions(-) diff --git a/2nd-gen/packages/core/components/accordion/AccordionBase.ts b/2nd-gen/packages/core/components/accordion/AccordionBase.ts index 3b35a08de9..9d7521f595 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionBase.ts @@ -10,6 +10,116 @@ * governing permissions and limitations under the License. */ +import { html, PropertyValues, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; + import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; -export abstract class AccordionBase extends SpectrumElement {} +import { + type AccordionDensity, + type AccordionSize, +} from './Accordion.types.js'; +import { AccordionItemBase } from './AccordionItemBase.js'; + +/** + * Base class for accordion components. Manages item propagation, heading + * level, density, and the exclusive-open constraint. + * + * @slot - One or more `swc-accordion-item` elements. + */ +export abstract class AccordionBase extends SpectrumElement { + // ────────────────── + // PUBLIC API + // ────────────────── + + /** + * When set, multiple items may be open at the same time. By default only + * one item can be open. + */ + @property({ type: Boolean, reflect: true, attribute: 'allow-multiple' }) + public allowMultiple: boolean = false; + + /** + * Heading level applied to every item header (2–6). Defaults to 3. + * Values outside that range are clamped. + */ + @property({ type: Number, reflect: true }) + public level: number = 3; + + /** + * Size applied to all items. When set, overrides any size set on individual + * items. + */ + @property({ type: String, reflect: true }) + public size?: AccordionSize; + + /** + * Controls vertical spacing between items. + * + * @default regular + */ + @property({ type: String, reflect: true }) + public density: AccordionDensity = 'regular'; + + /** + * Renders the accordion in its quiet (no-border) visual variant. + */ + @property({ type: Boolean, reflect: true }) + public quiet: boolean = false; + + /** + * Disables all items in the accordion. Individual items may also be + * disabled independently. + */ + @property({ type: Boolean, reflect: true }) + public disabled: boolean = false; + + // ────────────────────── + // IMPLEMENTATION + // ────────────────────── + + private updateItems(): void { + const slot = this.shadowRoot?.querySelector('slot'); + if (!slot) { + return; + } + const items = slot + .assignedElements({ flatten: true }) + .filter((el): el is AccordionItemBase => el instanceof AccordionItemBase); + for (const item of items) { + item.heading = this.level; + item.size = this.size; + } + } + + protected override update(changedProperties: PropertyValues): void { + if (changedProperties.has('level')) { + const clamped = Math.min(6, Math.max(2, this.level)); + if (this.level !== clamped) { + this.level = clamped; + } + } + if (changedProperties.has('level') || changedProperties.has('size')) { + this.updateItems(); + } + super.update(changedProperties); + } + + protected override firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + if (window.__swc?.DEBUG && !this.hasAttribute('density')) { + window.__swc.warn( + this, + `<${this.localName}> should have an explicit "density" attribute set. Defaulting to "regular", which corresponds to the 1st-gen default.`, + 'https://opensource.adobe.com/spectrum-web-components/components/accordion/', + { type: 'api', level: 'low' } + ); + } + } + + protected override render(): TemplateResult { + return html` + + `; + } +} diff --git a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts index 3aa2f07483..f13a014039 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts @@ -61,9 +61,10 @@ export abstract class AccordionItemBase extends SpectrumElement { * @internal * Heading level (2–6) propagated by the parent accordion. Defaults to 3 * for standalone items. Used by the concrete render() to emit the correct - * element; made reactive in a later commit. + * element; made reactive in a later commit. Public so AccordionBase + * can write to instances without a type assertion. */ - protected heading: number = 3; + public heading: number = 3; // ────────────────────── // IMPLEMENTATION diff --git a/2nd-gen/packages/swc/components/accordion/Accordion.ts b/2nd-gen/packages/swc/components/accordion/Accordion.ts index 3f15bb156e..f89f575f90 100644 --- a/2nd-gen/packages/swc/components/accordion/Accordion.ts +++ b/2nd-gen/packages/swc/components/accordion/Accordion.ts @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import { CSSResultArray, html, TemplateResult } from 'lit'; +import { CSSResultArray } from 'lit'; import { AccordionBase } from '@spectrum-web-components/core/components/accordion'; @@ -23,16 +23,15 @@ import styles from './accordion.css'; * @since 2.0.0 * * @example - * - * Content + * + * + * Section heading + * Panel content goes here. + * * */ export class Accordion extends AccordionBase { public static override get styles(): CSSResultArray { return [styles]; } - - protected override render(): TemplateResult { - return html``; - } } From 3a4454bfed348f60b23af1efb5f7c9478ff6b2dc Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Fri, 15 May 2026 09:56:59 -0500 Subject: [PATCH 09/56] feat: implement open and toggle logic --- .../components/accordion/AccordionBase.ts | 43 ++++++++++ .../swc/components/accordion/AccordionItem.ts | 45 +++++++++- .../accordion/stories/accordion.stories.ts | 86 ++++++++++++++++--- 3 files changed, 159 insertions(+), 15 deletions(-) diff --git a/2nd-gen/packages/core/components/accordion/AccordionBase.ts b/2nd-gen/packages/core/components/accordion/AccordionBase.ts index 9d7521f595..7692e48d45 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionBase.ts @@ -18,6 +18,7 @@ import { SpectrumElement } from '@spectrum-web-components/core/element/index.js' import { type AccordionDensity, type AccordionSize, + SWC_ACCORDION_ITEM_TOGGLE_EVENT, } from './Accordion.types.js'; import { AccordionItemBase } from './AccordionItemBase.js'; @@ -78,6 +79,32 @@ export abstract class AccordionBase extends SpectrumElement { // IMPLEMENTATION // ────────────────────── + private handleItemToggle = (event: Event): void => { + if (this.disabled) { + event.preventDefault(); + return; + } + if (this.allowMultiple) { + return; + } + const toggling = event.target; + if (!(toggling instanceof AccordionItemBase) || !toggling.open) { + return; + } + const slot = this.shadowRoot?.querySelector('slot'); + if (!slot) { + return; + } + const items = slot + .assignedElements({ flatten: true }) + .filter((el): el is AccordionItemBase => el instanceof AccordionItemBase); + for (const item of items) { + if (item !== toggling) { + item.open = false; + } + } + }; + private updateItems(): void { const slot = this.shadowRoot?.querySelector('slot'); if (!slot) { @@ -92,6 +119,22 @@ export abstract class AccordionBase extends SpectrumElement { } } + public override connectedCallback(): void { + super.connectedCallback(); + this.addEventListener( + SWC_ACCORDION_ITEM_TOGGLE_EVENT, + this.handleItemToggle + ); + } + + public override disconnectedCallback(): void { + super.disconnectedCallback(); + this.removeEventListener( + SWC_ACCORDION_ITEM_TOGGLE_EVENT, + this.handleItemToggle + ); + } + protected override update(changedProperties: PropertyValues): void { if (changedProperties.has('level')) { const clamped = Math.min(6, Math.max(2, this.level)); diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts index 61cd39872c..293a883ec1 100644 --- a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -12,7 +12,10 @@ import { CSSResultArray, html, TemplateResult } from 'lit'; -import { AccordionItemBase } from '@spectrum-web-components/core/components/accordion'; +import { + AccordionItemBase, + SWC_ACCORDION_ITEM_TOGGLE_EVENT, +} from '@spectrum-web-components/core/components/accordion'; import '../icon/swc-icon.js'; @@ -37,13 +40,48 @@ import styles from './accordion.css'; */ export class AccordionItem extends AccordionItemBase { // ────────────────────────────── - // RENDERING & STYLING + // STYLING // ────────────────────────────── public static override get styles(): CSSResultArray { return [styles]; } + // ────────────────────────────── + // DELEGATION + // ────────────────────────────── + + public override focus(options?: FocusOptions): void { + this.shadowRoot?.getElementById('header')?.focus(options); + } + + public override click(): void { + this.shadowRoot?.getElementById('header')?.click(); + } + + // ────────────────────────────── + // TOGGLE BEHAVIOR + // ────────────────────────────── + + protected override toggle(): void { + if (this.disabled) { + return; + } + this.open = !this.open; + const event = new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, { + bubbles: true, + composed: true, + cancelable: true, + }); + if (!this.dispatchEvent(event)) { + this.open = !this.open; + } + } + + // ────────────────────────────── + // RENDERING + // ────────────────────────────── + private chevronForSize(): TemplateResult { switch (this.size) { case 's': @@ -77,8 +115,9 @@ export class AccordionItem extends AccordionItemBase { + `; return html` -

- -

+ ${this.renderHeadingWrapper(button)} From e2ce2a7d12c0777e39e4551b8ef195a544fb8b46 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Fri, 15 May 2026 11:50:56 -0500 Subject: [PATCH 12/56] fix: handle space keyboard behavior (SWC-1487) --- .../packages/swc/components/accordion/AccordionItem.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts index 4d5c6a5d1e..8312fc7e44 100644 --- a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -109,6 +109,13 @@ export class AccordionItem extends AccordionItemBase { event.stopPropagation(); } + private handleHeaderKeydown(event: Event): void { + if ((event as KeyboardEvent).key === ' ') { + event.preventDefault(); + this.toggle(); + } + } + private renderHeadingWrapper(content: TemplateResult): TemplateResult { switch (this.heading) { case 2: @@ -145,6 +152,7 @@ export class AccordionItem extends AccordionItemBase { this.disabled || this.parentDisabled ? 'true' : undefined )} @click=${this.toggle} + @keydown=${this.handleHeaderKeydown} >