Skip to content

fix(styles): stop fade-in from permanently making elements a containing block - #23

Merged
KruGoL merged 1 commit into
mainfrom
fix/fade-in-containing-block
Jul 30, 2026
Merged

fix(styles): stop fade-in from permanently making elements a containing block#23
KruGoL merged 1 commit into
mainfrom
fix/fade-in-containing-block

Conversation

@KruGoL

@KruGoL KruGoL commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

The trap

@keyframes fade-in { from { … transform: translateY(6px); } to { … transform: translateY(0); } }
.animate-fade-in { animation: fade-in 0.3s ease-out both; }

The final keyframe ends at translateY(0), and fill-mode: both retains the last keyframe's value forever. translateY(0) is still a transform — not none — so every .animate-fade-in element stays a containing block for position: fixed descendants permanently, long after the 0.3s animation is over.

Anything fixed rendered inside such a wrapper is then positioned against the wrapper instead of the viewport.

How it surfaced

In the dev portal, a fixed inset-0 dialog rendered inside a page whose root is animate-fade-in. On a tall page, centring inside the wrapper happened to land near the middle of the screen and looked correct. On a short page the dialog centred a few hundred pixels down and its top went above the viewport, unreachable by scrolling — a checklist with no heading and no way to get to it.

It read as a bug in the page that happened to be short. It was this rule.

The fix

End the animation at transform: none. It interpolates identically — none is treated as the identity transform — so the animation looks exactly the same, and no containing block is left behind. The reason is written into the file so the next person does not "tidy" it back to translateY(0).

Consumers get this for free once they pick up the release. The dev portal has already been fixed at its own layer by rendering dialogs through a portal (sphere-dev-portal#37), which is the right defence regardless of this rule — this change removes the trap for everything else.

Verification

109 tests pass, tsup build clean, tsc --noEmit silent. npm run lint cannot run in this repo — eslint is not installed — which is pre-existing and unrelated to this change.

@KruGoL
KruGoL merged commit 1e00eb3 into main Jul 30, 2026
2 checks passed
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.

1 participant