Skip to content

feat(accordion): add direct actions #4020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 29, 2025
11 changes: 11 additions & 0 deletions .changeset/spicy-rings-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@spectrum-css/accordion": minor
---

Accordion now supports direct actions. Direct actions, which may consist of a quiet action button or a switch, or both, may be added to each accordion item's heading. Direct action items are vertically centered within the heading's first line of text for all sizes and densities, and maintain their own individual key focus states.

To allow the same level of customizability found in other elements within this component, the following --mod custom properties have been added:

- "--mod-accordion-item-direct-actions-height",
- "--mod-accordion-item-direct-actions-spacing",
- "--mod-accordion-item-direct-actions-vertical-spacing"
11 changes: 11 additions & 0 deletions components/accordion/dist/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
".spectrum-Accordion-item.is-open > .spectrum-Accordion-itemHeading .spectrum-Accordion-itemIndicator",
".spectrum-Accordion-item:first-child",
".spectrum-Accordion-itemContent",
".spectrum-Accordion-itemDirectActions",
".spectrum-Accordion-itemHeader",
".spectrum-Accordion-itemHeader.spectrum-Accordion-itemHeader:active",
".spectrum-Accordion-itemHeader:focus-visible",
".spectrum-Accordion-itemHeader:has(+ .spectrum-Accordion-itemDirectActions)",
".spectrum-Accordion-itemHeader:hover",
".spectrum-Accordion-itemHeading",
".spectrum-Accordion-itemIndicator",
Expand Down Expand Up @@ -60,6 +62,9 @@
"--mod-accordion-item-content-font-style",
"--mod-accordion-item-content-font-weight",
"--mod-accordion-item-content-line-height",
"--mod-accordion-item-direct-actions-height",
"--mod-accordion-item-direct-actions-spacing",
"--mod-accordion-item-direct-actions-vertical-spacing",
"--mod-accordion-item-focus-indicator-color",
"--mod-accordion-item-focus-indicator-gap",
"--mod-accordion-item-focus-indicator-thickness",
Expand All @@ -74,6 +79,7 @@
"--mod-accordion-item-header-font-style",
"--mod-accordion-item-header-font-weight",
"--mod-accordion-item-header-line-height",
"--mod-accordion-item-header-to-direct-actions-space",
"--mod-accordion-item-header-top-to-text-space",
"--mod-accordion-item-min-block-size",
"--mod-accordion-item-minimum-height",
Expand Down Expand Up @@ -131,6 +137,9 @@
"--spectrum-accordion-item-content-font-style",
"--spectrum-accordion-item-content-font-weight",
"--spectrum-accordion-item-content-line-height",
"--spectrum-accordion-item-direct-actions-height",
"--spectrum-accordion-item-direct-actions-spacing",
"--spectrum-accordion-item-direct-actions-vertical-spacing",
"--spectrum-accordion-item-focus-indicator-color",
"--spectrum-accordion-item-focus-indicator-gap",
"--spectrum-accordion-item-focus-indicator-thickness",
Expand All @@ -145,6 +154,7 @@
"--spectrum-accordion-item-header-font-style",
"--spectrum-accordion-item-header-font-weight",
"--spectrum-accordion-item-header-line-height",
"--spectrum-accordion-item-header-to-direct-actions-space",
"--spectrum-accordion-item-header-top-to-text-space",
"--spectrum-accordion-item-min-block-size",
"--spectrum-accordion-item-minimum-height",
Expand Down Expand Up @@ -227,6 +237,7 @@
"--spectrum-neutral-content-color-hover",
"--spectrum-neutral-content-color-key-focus",
"--spectrum-sans-font-family-stack",
"--spectrum-spacing-100",
"--spectrum-transparent-black-100",
"--spectrum-transparent-black-25",
"--spectrum-transparent-black-300"
Expand Down
31 changes: 30 additions & 1 deletion components/accordion/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
--spectrum-accordion-item-content-area-top-to-content: var(--spectrum-accordion-content-area-top-to-content);
--spectrum-accordion-item-content-area-bottom-to-content: var(--spectrum-accordion-content-area-bottom-to-content);
--spectrum-accordion-divider-thickness: var(--spectrum-divider-thickness-small);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-100);
--spectrum-accordion-item-header-to-direct-actions-space: var(--spectrum-spacing-100); /* same for all sizes */

/* Text header */
--spectrum-accordion-item-header-font: var(--spectrum-sans-font-family-stack);
Expand All @@ -40,6 +42,7 @@
--spectrum-accordion-item-header-line-height: var(--spectrum-line-height-100);

--spectrum-accordion-item-header-cursor: pointer;
--spectrum-accordion-item-direct-actions-spacing: var(--spectrum-spacing-100);
--spectrum-accordion-animation-duration: var(--spectrum-animation-duration-100);

/* Text body */
Expand Down Expand Up @@ -72,6 +75,12 @@
var(--mod-accordion-item-header-top-to-text-space, var(--spectrum-accordion-item-header-top-to-text-space)) + var(--mod-accordion-item-header-bottom-to-text-space, var(--spectrum-accordion-item-header-bottom-to-text-space)) + (var(--mod-accordion-item-header-font-size, var(--spectrum-accordion-item-header-font-size)) * var(--mod-accordion-item-header-line-height, var(--spectrum-accordion-item-header-line-height)))
);

/* Calculated vertical spacing for action button and switch to center them within the accordion item */
--spectrum-accordion-item-direct-actions-vertical-spacing: calc(
(var(--mod-accordion-item-min-block-size, var(--spectrum-accordion-item-min-block-size)) -
var(--mod-accordion-item-direct-actions-height, var(--spectrum-accordion-item-direct-actions-height))) / 2
);

Comment on lines +78 to +83
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Did I miss a better way to do vertical spacing for this situation? I was finding that this got pretty complex, pretty fast:

  • An align-items: center on the .spectrum-Accordion-itemDirectActions isn't ideal when the text wraps because the designs show that we want direct actions to align with the first line of text, instead of all of them.
  • Accordion items have a min-height of component-height-75/component-height-100/component-height-200/component-height-300, same with the direct actions, but we don't usually see that min-height in accordion (except for compact) because of the calculations for spacing and font-size * line-height making it larger

/* Right-to-left adjustments for transforms */
&:dir(rtl) {
--spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0);
Expand Down Expand Up @@ -100,6 +109,7 @@
--spectrum-accordion-item-header-top-to-text-space: var(--spectrum-accordion-top-to-text-small);
--spectrum-accordion-item-header-bottom-to-text-space: var(--spectrum-accordion-bottom-to-text-small);
--spectrum-accordion-top-to-disclosure-indicator: var(--spectrum-field-top-to-disclosure-icon-small);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-75); /* component height for switch and action button */
}

.spectrum-Accordion--sizeL {
Expand All @@ -116,6 +126,7 @@
--spectrum-accordion-item-header-top-to-text-space: var(--spectrum-accordion-top-to-text-large);
--spectrum-accordion-item-header-bottom-to-text-space: var(--spectrum-accordion-bottom-to-text-large);
--spectrum-accordion-top-to-disclosure-indicator: var(--spectrum-field-top-to-disclosure-icon-large);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-200);
}

.spectrum-Accordion--sizeXL {
Expand All @@ -132,6 +143,7 @@
--spectrum-accordion-item-header-top-to-text-space: var(--spectrum-accordion-top-to-text-extra-large);
--spectrum-accordion-item-header-bottom-to-text-space: var(--spectrum-accordion-bottom-to-text-extra-large);
--spectrum-accordion-top-to-disclosure-indicator: var(--spectrum-field-top-to-disclosure-icon-extra-large);
--spectrum-accordion-item-direct-actions-height: var(--spectrum-component-height-300);
}

.spectrum-Accordion--compact {
Expand Down Expand Up @@ -237,6 +249,7 @@
margin: 0;
position: relative;
box-sizing: border-box;
display: flex;
}

.spectrum-Accordion-itemIndicator {
Expand Down Expand Up @@ -268,15 +281,17 @@

/* Focusable button that expands/collapses the accordion item. */
.spectrum-Accordion-itemHeader {
overflow-wrap: anywhere;
word-break: normal;
box-sizing: border-box;
position: relative;

display: flex;
align-items: flex-start;
justify-content: flex-start;

/* start spacing controlled by edge to disclosure icon spacing */
padding-inline: 0 var(--mod-accordion-edge-to-content-area, var(--spectrum-accordion-edge-to-content-area));
padding-block: 0; /* reset user-agent styles */
line-height: var(--mod-accordion-item-header-line-height, var(--spectrum-accordion-item-header-line-height));

text-overflow: ellipsis;
Expand Down Expand Up @@ -313,6 +328,20 @@
background-color: var(--spectrum-accordion-background-color-down);
color: var(--spectrum-accordion-item-header-color-down);
}

&:has(+ .spectrum-Accordion-itemDirectActions) {
/* set spacing between header and direct actions, whether or not noInlinePadding variant is used */
padding-inline-end: var(--mod-accordion-item-header-to-direct-actions-space, var(--spectrum-accordion-item-header-to-direct-actions-space));
}
}

.spectrum-Accordion-itemDirectActions {
margin-inline-end: var(--mod-accordion-edge-to-content-area, var(--spectrum-accordion-edge-to-content-area));
display: inline-flex;
gap: var(--mod-accordion-item-direct-actions-spacing, var(--spectrum-accordion-item-direct-actions-spacing));

/* margin needs to be set on top and bottom to keep compact XL items vertically centered and prevent them from growing vertically */
margin-block: var(--mod-accordion-item-direct-actions-vertical-spacing, var(--spectrum-accordion-item-direct-actions-vertical-spacing));
}

.spectrum-Accordion-item.is-open {
Expand Down
53 changes: 49 additions & 4 deletions components/accordion/stories/accordion.stories.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js";
import { Sizes } from "@spectrum-css/preview/decorators";
import { disableDefaultModes } from "@spectrum-css/preview/modes";
import { isQuiet, size } from "@spectrum-css/preview/types";
import metadata from "../dist/metadata.json";
import packageJson from "../package.json";
import { AccordionGroup, testsContent as accordionContent } from "./accordion.test.js";
import { AccordionGroup, testsContent as accordionContent, directActionsContent, longerContent } from "./accordion.test.js";
import { Template } from "./template.js";

/**
Expand Down Expand Up @@ -60,7 +61,36 @@ export default {
},
control: { type: "boolean" },
},
isQuiet
isQuiet,
hasActionButtons: {
name: "Has action buttons",
description: "Adds an action button to each accordion item header, in the direct actions section.",
type: { name: "boolean" },
table: {
type: { summary: "boolean" },
category: "Direct actions",
},
control: { type: "boolean" },
},
actionButtonIconName: {
name: "Action button icon",
...(IconStories?.argTypes?.iconName ?? {}),
if: { arg: "hasActionButtons", truthy: true },
table: {
type: { summary: "string" },
category: "Direct actions",
},
},
hasSwitches: {
name: "Has switches",
description: "Adds a switch to each accordion item header, in the direct actions section.",
type: { name: "boolean" },
table: {
type: { summary: "boolean" },
category: "Direct actions",
},
control: { type: "boolean" },
},
},
args: {
rootClass: "spectrum-Accordion",
Expand All @@ -70,6 +100,9 @@ export default {
disableAll: false,
isQuiet: false,
hasNoInlinePadding: false,
hasActionButtons: false,
actionButtonIconName: "Circle",
hasSwitches: false,
},
parameters: {
actions: {
Expand Down Expand Up @@ -121,9 +154,9 @@ export const CustomWidth = AccordionGroup.bind({});
CustomWidth.tags = ["!dev"];
CustomWidth.storyName = "Custom width";
CustomWidth.args = {
items: accordionContent,
items: longerContent,
customStyles: {
"--mod-accordion-item-width": "500px",
"--mod-accordion-item-width": "auto",
},
};
CustomWidth.parameters = {
Expand Down Expand Up @@ -158,6 +191,18 @@ Spacious.parameters = {
};
Spacious.storyName = "Density: Spacious";

/**
* Direct actions within accordion items are supported. A quiet
* [action button](/?path=/docs/actionbutton--default), a
* [switch](/?path=/docs/switch--default), or both can be added to
* each accordion item header.
*/
export const DirectActions = Template.bind({});
DirectActions.tags = ["!dev"];
DirectActions.args = {
items: directActionsContent
};

/**
* Individual accordion items can be disabled by applying the `.is-disabled` class to the
* `.spectrum-Accordion-item` element. This example also demonstrates the use of the disabled
Expand Down
Loading
Loading