From 84898212954947359d2e6aa26c1f707f53681163 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 10 Mar 2026 22:48:23 +0100 Subject: [PATCH 1/7] Animate chevron rotation on CollapsibleCard toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace dual chevron icon swap (chevronUp/chevronDown) with a single chevronDown icon that rotates 180° via CSS when the panel is open. The rotation uses the `data-panel-open` attribute set by Base UI's Collapsible.Trigger. The CSS transition is gated behind `prefers-reduced-motion: no-preference` so the icon still flips instantly for users who prefer reduced motion. Made-with: Cursor --- packages/ui/src/collapsible-card/style.module.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ui/src/collapsible-card/style.module.css b/packages/ui/src/collapsible-card/style.module.css index 55b0744ebb6532..c1ea40c8050d11 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 { From 50a6ea44df72518ab4c64af3ba6b2f5712872b9a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 10 Mar 2026 22:49:33 +0100 Subject: [PATCH 2/7] Add fade + translate animation to CollapsibleCard content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Animate the collapsible content panel with a subtle opacity fade and vertical translation (-4px) on open/close. Uses Base UI's `data-starting-style` and `data-ending-style` attributes on the Collapsible.Panel element. Height changes remain instant — only the content appearance is animated. The transition is gated behind `prefers-reduced-motion: no-preference`. Made-with: Cursor --- packages/ui/src/collapsible-card/content.tsx | 8 +++++++- packages/ui/src/collapsible-card/style.module.css | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/collapsible-card/content.tsx b/packages/ui/src/collapsible-card/content.tsx index 0f57970b68b2e9..d88c23d5191b41 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,10 +10,14 @@ import type { ContentProps } from './types'; * visible when expanded. */ export const Content = forwardRef< HTMLDivElement, ContentProps >( - function CollapsibleCardContent( { render, ...restProps }, ref ) { + function CollapsibleCardContent( + { className, render, ...restProps }, + ref + ) { return ( } { ...restProps } /> diff --git a/packages/ui/src/collapsible-card/style.module.css b/packages/ui/src/collapsible-card/style.module.css index c1ea40c8050d11..83b222149bc060 100644 --- a/packages/ui/src/collapsible-card/style.module.css +++ b/packages/ui/src/collapsible-card/style.module.css @@ -37,6 +37,18 @@ .header[data-disabled] .header-trigger { color: var(--wpds-color-fg-interactive-neutral-disabled); } + + .content { + @media not (prefers-reduced-motion) { + transition: opacity 0.15s ease, transform 0.15s ease; + + &[data-starting-style], + &[data-ending-style] { + opacity: 0; + transform: translateY(-4px); + } + } + } } @layer wp-ui-compositions { From 3758b073883766d8bdc4f2d13206690a32ac9f11 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 10 Mar 2026 22:59:39 +0100 Subject: [PATCH 3/7] Refine content animation: open-only with delay, no exit animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the exit (close) animation — with instant height changes, a fade-out has no visible effect. Keep only the enter (open) animation by defining the transition on `[data-open]` so it's absent when the panel closes and Base UI hides it immediately. Add a 50ms delay to the open transition so the container snaps open first, then the content fades in. Also move `data-starting-style` state outside the media query to match the Dialog component pattern — only the `transition` property is gated behind `prefers-reduced-motion`. Made-with: Cursor --- packages/ui/src/collapsible-card/style.module.css | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/collapsible-card/style.module.css b/packages/ui/src/collapsible-card/style.module.css index 83b222149bc060..401647690151d1 100644 --- a/packages/ui/src/collapsible-card/style.module.css +++ b/packages/ui/src/collapsible-card/style.module.css @@ -39,13 +39,14 @@ } .content { - @media not (prefers-reduced-motion) { - transition: opacity 0.15s ease, transform 0.15s ease; + &[data-starting-style] { + opacity: 0; + transform: translateY(-4px); + } - &[data-starting-style], - &[data-ending-style] { - opacity: 0; - transform: translateY(-4px); + @media not (prefers-reduced-motion) { + &[data-open] { + transition: opacity 0.15s ease 0.05s, transform 0.15s ease 0.05s; } } } From 848a1ea13725cc0e6cd7ba2d0dcd14dd2103364c Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 13 Mar 2026 18:29:47 +0100 Subject: [PATCH 4/7] Animate whole card height --- packages/ui/src/card/style.module.css | 3 ++- packages/ui/src/collapsible-card/content.tsx | 12 +++++++--- .../ui/src/collapsible-card/style.module.css | 22 ++++++++++++++----- 3 files changed, 27 insertions(+), 10 deletions(-) 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 d88c23d5191b41..cff163eeef6534 100644 --- a/packages/ui/src/collapsible-card/content.tsx +++ b/packages/ui/src/collapsible-card/content.tsx @@ -11,16 +11,22 @@ import type { ContentProps } from './types'; */ export const Content = forwardRef< HTMLDivElement, ContentProps >( function CollapsibleCardContent( - { className, render, ...restProps }, + { className, render, children, ...restProps }, ref ) { return ( } { ...restProps } - /> + > + + { children } + + ); } ); diff --git a/packages/ui/src/collapsible-card/style.module.css b/packages/ui/src/collapsible-card/style.module.css index 401647690151d1..4a322d11f2f933 100644 --- a/packages/ui/src/collapsible-card/style.module.css +++ b/packages/ui/src/collapsible-card/style.module.css @@ -39,17 +39,27 @@ } .content { - &[data-starting-style] { - opacity: 0; - transform: translateY(-4px); + 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) { - &[data-open] { - transition: opacity 0.15s ease 0.05s, transform 0.15s ease 0.05s; - } + transition: all 150ms ease-out; } } + + .content-inner { + padding-block-start: 0; + } } @layer wp-ui-compositions { From 726c50bf7ef53e1cc526f2fca8547ef31961fcb1 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 13 Mar 2026 18:31:34 +0100 Subject: [PATCH 5/7] CHANGELOG --- packages/ui/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) 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) From 80164c73c89e67757a99ad03500fcd5c480bf112 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Fri, 13 Mar 2026 18:44:44 +0100 Subject: [PATCH 6/7] use correct CSS layer --- packages/ui/src/collapsible-card/style.module.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/collapsible-card/style.module.css b/packages/ui/src/collapsible-card/style.module.css index 4a322d11f2f933..168490e44176bc 100644 --- a/packages/ui/src/collapsible-card/style.module.css +++ b/packages/ui/src/collapsible-card/style.module.css @@ -57,12 +57,13 @@ } } +} + +@layer wp-ui-compositions { .content-inner { padding-block-start: 0; } -} -@layer wp-ui-compositions { .header { display: flex; flex-direction: row; From 0de2140953544110099ff7df61be405712a7d7a8 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 16 Mar 2026 17:05:14 +0100 Subject: [PATCH 7/7] Add stacked storybook example --- .../collapsible-card/stories/index.story.tsx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) 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.