diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md
index 7af6d21a72e450..02add77bbc758e 100644
--- a/packages/ui/CHANGELOG.md
+++ b/packages/ui/CHANGELOG.md
@@ -20,6 +20,7 @@
- `Notice`: Use `Text` component for `Title` and `Description` typography ([#75870](https://github.com/WordPress/gutenberg/pull/75870)).
- `Card`, `CollapsibleCard`: update padding to match legacy `Card` component ([#76368](https://github.com/WordPress/gutenberg/pull/76368)).
- `CollapsibleCard`: move trigger to the header ([#76265](https://github.com/WordPress/gutenberg/pull/76265)).
+- `CollapsibleCard`: add animations ([#76378](https://github.com/WordPress/gutenberg/pull/76378)).
## 0.8.0 (2026-03-04)
diff --git a/packages/ui/src/card/style.module.css b/packages/ui/src/card/style.module.css
index ac9f45ab9add08..61dce405820b4d 100644
--- a/packages/ui/src/card/style.module.css
+++ b/packages/ui/src/card/style.module.css
@@ -4,6 +4,7 @@
.root {
--wp-ui-card-padding: var(--wpds-dimension-padding-2xl);
--wp-ui-card-header-content-gap: var(--wpds-dimension-gap-xl);
+ --wp-ui-card-header-content-margin: calc(var(--wp-ui-card-header-content-gap) - var(--wp-ui-card-padding));
display: flex;
flex-direction: column;
@@ -28,7 +29,7 @@
/* Custom vertical gap between header and content */
.header + .content {
padding-block-start: 0;
- margin-block-start: calc(var(--wp-ui-card-header-content-gap) - var(--wp-ui-card-padding));
+ margin-block-start: var(--wp-ui-card-header-content-margin);
}
.fullbleed {
diff --git a/packages/ui/src/collapsible-card/content.tsx b/packages/ui/src/collapsible-card/content.tsx
index 0f57970b68b2e9..cff163eeef6534 100644
--- a/packages/ui/src/collapsible-card/content.tsx
+++ b/packages/ui/src/collapsible-card/content.tsx
@@ -1,6 +1,8 @@
import { forwardRef } from '@wordpress/element';
+import clsx from 'clsx';
import * as Card from '../card';
import * as Collapsible from '../collapsible';
+import styles from './style.module.css';
import type { ContentProps } from './types';
/**
@@ -8,13 +10,23 @@ import type { ContentProps } from './types';
* visible when expanded.
*/
export const Content = forwardRef< HTMLDivElement, ContentProps >(
- function CollapsibleCardContent( { render, ...restProps }, ref ) {
+ function CollapsibleCardContent(
+ { className, render, children, ...restProps },
+ ref
+ ) {
return (
}
+ className={ clsx( styles.content, className ) }
{ ...restProps }
- />
+ >
+
+ { children }
+
+
);
}
);
diff --git a/packages/ui/src/collapsible-card/stories/index.story.tsx b/packages/ui/src/collapsible-card/stories/index.story.tsx
index 97b0b19b3f2507..a505bd3bf4a5ea 100644
--- a/packages/ui/src/collapsible-card/stories/index.story.tsx
+++ b/packages/ui/src/collapsible-card/stories/index.story.tsx
@@ -105,6 +105,57 @@ export const Disabled: Story = {
},
};
+/**
+ * Multiple collapsible cards stacked vertically, simulating a typical
+ * settings-panel or FAQ-style layout.
+ */
+export const Stacked: Story = {
+ parameters: { controls: { disable: true } },
+ render: () => (
+
+ { [
+ 'General',
+ 'Advanced',
+ 'Accessibility',
+ 'Performance',
+ 'Privacy',
+ 'Notifications',
+ ].map( ( title ) => (
+
+
+ { title }
+
+
+
+ Configure all { title.toLowerCase() } settings for
+ your site. Changes here affect how your site behaves
+ across all pages and posts.
+
+
+ Review each option carefully before saving. Some
+ changes may require a page reload to take effect.
+ Hover over individual options for more details about
+ what they control.
+
+
+ If you're unsure about a setting, you can
+ always reset to defaults using the button at the
+ bottom of this section. Your previous configuration
+ will be saved as a backup.
+
+
+
+ ) ) }
+
+ ),
+};
+
/**
* Visual comparison: a `CollapsibleCard` (open) next to a regular `Card`
* to verify identical spacing and layout.
diff --git a/packages/ui/src/collapsible-card/style.module.css b/packages/ui/src/collapsible-card/style.module.css
index 55b0744ebb6532..168490e44176bc 100644
--- a/packages/ui/src/collapsible-card/style.module.css
+++ b/packages/ui/src/collapsible-card/style.module.css
@@ -23,6 +23,10 @@
/* For an outline that looks like `IconButton`'s */
border-radius: var(--wpds-border-radius-sm);
+
+ @media not (prefers-reduced-motion) {
+ transition: rotate 0.15s ease-out;
+ }
}
.header[data-panel-open] .header-trigger {
@@ -33,9 +37,33 @@
.header[data-disabled] .header-trigger {
color: var(--wpds-color-fg-interactive-neutral-disabled);
}
+
+ .content {
+ height: var(--collapsible-panel-height);
+ overflow: hidden;
+ margin-block-start: var(--wp-ui-card-header-content-margin);
+
+ &[hidden]:not([hidden="until-found"]) {
+ display: none;
+ }
+
+ &[data-starting-style],
+ &[data-ending-style] {
+ height: 0;
+ }
+
+ @media not (prefers-reduced-motion) {
+ transition: all 150ms ease-out;
+ }
+ }
+
}
@layer wp-ui-compositions {
+ .content-inner {
+ padding-block-start: 0;
+ }
+
.header {
display: flex;
flex-direction: row;