Skip to content

feat(popup): fade the panel in, the way Menu already animates - #698

Merged
mrholek merged 2 commits into
v6-devfrom
feat/popup-entry-animation-v6
Aug 4, 2026
Merged

feat(popup): fade the panel in, the way Menu already animates#698
mrholek merged 2 commits into
v6-devfrom
feat/popup-entry-animation-v6

Conversation

@mrholek

@mrholek mrholek commented Aug 4, 2026

Copy link
Copy Markdown
Member

Follow-up to #697. Raising the Firefox floor to 129 bought @starting-style for exactly one component — Menu, which inherited it with the port. Meanwhile .popup, the shared chrome behind six components' panels, had no transition at all: a date picker snapped open next to a menu that glided.

@include transition(
  opacity var(--#{$prefix}popup-transition-duration) var(--#{$prefix}popup-transition-timing),
  display var(--#{$prefix}popup-transition-duration) allow-discrete
);

&.show { display: block; opacity: 1; }

@starting-style { .popup.show { opacity: 0; } }

All three parts are load-bearing: allow-discrete makes display animatable, @starting-style supplies the state to animate from (the element does not exist to be styled before it is shown), and opacity carries the motion. Duration and easing are tokens.

No scale, deliberately

The first attempt scaled from .98, mirroring Menu. mrholek caught it immediately: the pickers popped, the combobox did not. Menu keys transform-origin off data-coreui-placement; a popup carries no such attribute, so any origin is wrong half the time — above the field, or in RTL. And on a panel the size of a calendar even 2% reads as a jump rather than an appearance. The combobox list is small enough to hide it; the pickers are not.

Verification

The visual suite cannot see this: it freezes transitions on purpose, so a screenshot proves nothing about motion. Sampling computed style through the transition instead:

~0ms    display=block  opacity=0.00   ← the @starting-style state
~60ms   display=block  opacity=0.91   ← genuinely interpolating
~300ms  display=block  opacity=1.00   ← settled

prefers-reduced-motion is handled by our transition() mixin, which emits transition: none under it.

One harness change, and why

A fading panel is composited, and a composited layer rasterises text differently enough between frames to move ~3% of pixels — enough to trip a comparator configured with includeAA: true. That setting is not negotiable: it is what catches an adornment icon changing colour, which was itself a bug found by this suite.

So the three popup screenshots get a larger allowance, passed per call rather than globally, with the reason in the code. Everything else keeps the tight one. Verified stable across three consecutive runs.

shoot() also waits two frames before capturing now, which is correct regardless.

Menu was the only thing in the library using @starting-style, inherited
with the port. The .popup primitive - six components' panels - appeared
instantly, so a picker snapped open next to a menu that glided.

It fades now, through the same three-part recipe: allow-discrete makes
display animatable, @starting-style supplies the state to animate from
(the element does not exist to be styled before it is shown), and
opacity carries the motion.

No scale, deliberately. A popup carries no placement attribute, so a
scale would need an origin we cannot know - and on a panel the size of a
calendar even 2% reads as the panel jumping rather than appearing. The
combobox list hid that; the pickers did not.

Verified by sampling computed style through the transition rather than by
reading the CSS: opacity 0 at the first frame, 0.91 at ~60ms, 1 when
settled. The visual suite cannot see this - it freezes transitions on
purpose - but a fading panel is composited, and a composited layer
rasterises text differently enough between frames to trip a comparator
counting antialiased pixels. The three popup screenshots get a larger
allowance for that, and only for that; the icon-colour changes this suite
exists to catch move far more pixels.
@coveralls

coveralls commented Aug 4, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 30948255948

Coverage decreased (-0.002%) to 92.963%

Details

  • Coverage decreased (-0.002%) from the base build.
  • Patch coverage: 2 of 2 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 8667
Covered Lines: 8271
Line Coverage: 95.43%
Relevant Branches: 4819
Covered Branches: 4266
Branch Coverage: 88.52%
Branches in Coverage %: Yes
Coverage Strength: 362.74 hits per line

💛 - Coveralls

The fade I just added made a latent split visible: Combobox added .show to
the popup only when the panel was teleported, and otherwise relied on
`.autocomplete.show &` to set display. That worked while display was the
only thing keyed on the class. Once opacity was keyed on it too, the
untelevised path left the panel laid out and fully transparent —
autocomplete and multi-select opened into nothing.

The panel carries its own open state now, teleported or not, and the
parallel display rules in the combobox and picker partials are gone. They
were what hid this: two selectors driving one state, so a missing class
cost nothing until something else depended on it.

Caught by mrholek in the browser, not by the suite. The visual suite
passes with the panel completely invisible, so the regression test is a
contract one — the popup carries .show after show() and loses it after
hide() — and it fails without this fix.
@mrholek
mrholek merged commit f7a8b7d into v6-dev Aug 4, 2026
9 checks passed
mrholek added a commit that referenced this pull request Aug 7, 2026
An audit of every v6-dev commit against the guide found five user-visible
changes with no entry, and one entry that had gone stale.

Missing:

- **The `*.rtl.css` builds are gone** (#682). The biggest gap: 20 files left
  `dist/css` and nothing said so. Anyone linking `coreui.rtl.min.css` gets a
  404 with no explanation of what to do instead.
- **Button always exposes `aria-pressed`** (#706).
- **Number Input** (#694) was mentioned only in passing, inside the entry about
  the frame it shares with Password Input, never announced as new.
- **Multi Select option checkboxes are the form-check surface now** (#660,
  #661), which retires the per-indicator look variables.
- **Markers are hidden with `list-style-type: ""`** (#664).

Stale: the select-indicator entry still said "the RTL build flips the
right/left keyword at compile time" — there is no RTL build. And the popup
primitive entry claimed "the anchored presentation is unchanged", which stopped
being true when the panel started fading in (#698).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants