Summary
Combining .dialog with .modal-enter un-centers the modal. The two use the same property — transform — for different jobs, and the animation wins.
Cause
.dialog centers itself with position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%).
.modal-enter runs @keyframes modalIn, which ends on transform: scale(1) translateY(0) — no translate(-50%, -50%).
- With
animation-fill-mode: both, the animated transform replaces the base one, so the -50%, -50% centering pullback is dropped and the dialog lands offset by half its own size (down/right of center).
Repro
Render <div class="dialog modal-enter">…</div> over a .dialog-overlay. Expected: centered. Actual: off-center once the entrance animation fills.
Seen in the docs Dialog demo (pushed off-frame); patched there as a stopgap by centering via the preview canvas (flexbox). agentic-trust-ux hit the same thing and worked around it locally with a custom pfIn keyframe that keeps translate(-50%, -50%) in every frame (app.css:161-173).
Proposed fix
Center the dialog on its container, not on itself:
.dialog-overlay { display: flex; align-items: center; justify-content: center; }
.dialog drops top/left/transform self-centering (becomes a normal centered child), freeing transform for .modal-enter.
This is the idiomatic modal pattern and removes the property conflict. Consumers that currently rely on .dialog's self-centering (e.g. agentic's pfIn workaround) can drop their patch after the change.
⚠️ Component-tier change — .dialog is vendored into consumers, so land it as a versioned release and re-sync consumers (see lib/CONSUMING.md).
Summary
Combining
.dialogwith.modal-enterun-centers the modal. The two use the same property —transform— for different jobs, and the animation wins.Cause
.dialogcenters itself withposition: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)..modal-enterruns@keyframes modalIn, which ends ontransform: scale(1) translateY(0)— notranslate(-50%, -50%).animation-fill-mode: both, the animatedtransformreplaces the base one, so the-50%, -50%centering pullback is dropped and the dialog lands offset by half its own size (down/right of center).Repro
Render
<div class="dialog modal-enter">…</div>over a.dialog-overlay. Expected: centered. Actual: off-center once the entrance animation fills.Seen in the docs Dialog demo (pushed off-frame); patched there as a stopgap by centering via the preview canvas (flexbox). agentic-trust-ux hit the same thing and worked around it locally with a custom
pfInkeyframe that keepstranslate(-50%, -50%)in every frame (app.css:161-173).Proposed fix
Center the dialog on its container, not on itself:
.dialog-overlay { display: flex; align-items: center; justify-content: center; }.dialogdropstop/left/transformself-centering (becomes a normal centered child), freeingtransformfor.modal-enter.This is the idiomatic modal pattern and removes the property conflict. Consumers that currently rely on
.dialog's self-centering (e.g. agentic'spfInworkaround) can drop their patch after the change..dialogis vendored into consumers, so land it as a versioned release and re-sync consumers (seelib/CONSUMING.md).