Skip to content

@wordpress/ui: Unify the opacity (fade) transition pattern across overlay components #79089

Description

@jameskoster

What

Overlay/animated components in @wordpress/ui currently express the same opacity fade in two different, mutually-inverse ways. We should standardise on a single canonical pattern (potentially centralised in a shared utility) so every component fades in/out identically and the intent is obvious.

This was surfaced in review on #78885 (r3387360744) and flagged again as a follow-up in r3382084439.

Why

The two patterns are functionally equivalent, but the inconsistency makes the code harder to read, harder to keep in sync, and easy to get subtly wrong (e.g. forgetting a :not([data-ending-style]) guard, which previously caused the Popover backdrop to snap in on mount). A single shared approach reduces drift and makes future overlay work copy-pasteable with confidence.

Current state

There are two opposite directions in use:

Pattern A — "hidden by default, reveal when open" (used by the dropdown motion util and Popover):

		.dropdown-motion {
			/* ... */
			opacity: 0;
			/* ... */
			&[data-open]:not([data-starting-style]) {
				opacity: 1;
				transform: translate(0);
			}
		}
		.backdrop {
			/* ... */
			opacity: 0;

			&[data-open]:not([data-starting-style]):not([data-ending-style]) {
				opacity: 1;
			}
		}

Pattern B — "visible by default, hide during transition states" (used by Dialog and Drawer):

		.backdrop {
			/* ... */
			&[data-starting-style],
			&[data-ending-style] {
				opacity: 0;
			}

			&[data-open] {
				opacity: 1;
			}
		}

Note that even within Pattern A the two usages aren't identical: dropdown-motion guards only :not([data-starting-style]), while the Popover backdrop adds :not([data-ending-style]).

Inventory

File Element Pattern Property
utils/css/dropdown-motion.module.css .dropdown-motion A opacity + transform
utils/css/item-popup.module.css .popup A (via composes) opacity + transform
popover/style.module.css .popup (via composes) A opacity + transform
popover/style.module.css .backdrop A (with extra guard) opacity
dialog/style.module.css .popup, .backdrop B opacity + transform
drawer/style.module.css .popup, .backdrop, etc. B opacity + transform
collapsible-card/style.module.css .panel B-style height (not opacity)

Proposal / desired outcome

  • Pick one canonical direction for opacity fades and apply it everywhere.
  • Normalise the state guards so all usages match (consistent handling of [data-starting-style] / [data-ending-style]).
  • Keep collapsible-card in mind as part of the broader "unify transition approaches" effort, even though it animates height rather than opacity.

Open questions

  • Which direction wins? Pattern A (opacity: 0 default + reveal on open) vs. Pattern B (opacity: 1 default + hide in transition states). Worth confirming whether one interacts better with Base UI's mount/data-starting-style timing.

Scope

CSS-only consistency change across @wordpress/ui overlay components; no public component API changes expected.

Metadata

Metadata

Labels

Design SystemIssues related to the system of combining components according to best practices.Needs Technical FeedbackNeeds testing from a developer perspective.[Package] UI/packages/ui[Type] EnhancementA suggestion for improvement.

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions