Centre modals in the viewport instead of pinning them near the top (#7715)

## What

Every dialog in the processor is the shared `.sui-modal` shell, and its
backdrop was top-aligning the panel:

```css
align-items: flex-start;
padding: 5rem 1.5rem 1.5rem;   /* 80px above, 24px below */
```

On a 900px-tall viewport that started every dialog at `y=80` with ~350px
of dead space beneath it. Phones already had an `align-items: center`
override; desktop never got one.

## Change

`frontend/editor/src/core/ui/Modal.css` only:

- Symmetric block inset, `align-items: center`.
- The inset is published as `--modal-inset-block`, and `.sui-modal`'s
`max-height` derives from it. That coupling is the point: if the two
drift apart, a tall modal overflows a centre-aligned backdrop and loses
its header off the top of the screen, unreachable.
- The phone breakpoint now only moves the variable. Measured at 375x812
it resolves to exactly the previous values (`16px 12px`, `max-height:
780px`), so mobile behaviour is unchanged.

One shared file, so this covers flow modals, source / user / pipeline /
API-key modals, billing and procurement.

## Before / After


<img width="2104" height="2284" alt="image"
src="https://github.com/user-attachments/assets/bcb50145-f75e-449e-92c6-a0b085cc091c"
/>


## Testing

- `task frontend:check` passes (lint + typecheck + 2356 tests).
- Phone breakpoint measured directly in the browser, values match the
previous behaviour.
This commit is contained in:
EthanHealy01
2026-08-28 12:27:05 +00:00
committed by GitHub
parent 4ab2505a6c
commit 1c055f3d18
+14 -10
View File
@@ -1,11 +1,16 @@
/* The inset is symmetric so the panel sits in the optical centre of the viewport rather than
riding the top edge. It is published as a var because .sui-modal's max-height has to be the
viewport minus both halves of it — if the two drift apart a tall modal overflows the backdrop
and, because the panel is centre-aligned, loses its header off the top of the screen. */
.sui-modal__backdrop { .sui-modal__backdrop {
--modal-inset-block: 2.5rem;
position: fixed; position: fixed;
inset: 0; inset: 0;
background: rgba(0, 0, 0, 0.55); background: rgba(0, 0, 0, 0.55);
display: flex; display: flex;
align-items: flex-start; align-items: center;
justify-content: center; justify-content: center;
padding: 5rem 1.5rem 1.5rem; padding: var(--modal-inset-block) 1.5rem;
z-index: 100; z-index: 100;
animation: fadeIn 0.18s ease both; animation: fadeIn 0.18s ease both;
overscroll-behavior: contain; overscroll-behavior: contain;
@@ -24,20 +29,19 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
max-height: calc(100vh - 6.5rem); max-height: calc(100vh - var(--modal-inset-block) * 2);
max-height: calc(100dvh - 6.5rem); /* mobile browser chrome shrinks 100vh */ /* mobile browser chrome shrinks 100vh */
max-height: calc(100dvh - var(--modal-inset-block) * 2);
overflow: hidden; overflow: hidden;
animation: scaleIn 0.2s cubic-bezier(0.4, 0, 0.2, 1) both; animation: scaleIn 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
} }
/* Phones: drop the tall top inset so the modal gets the vertical space */ /* Phones: tighten the inset so the modal gets the vertical space. Only the variable moves —
the max-height above follows it, so the pair cannot fall out of step. */
@media (max-width: 30rem) { @media (max-width: 30rem) {
.sui-modal__backdrop { .sui-modal__backdrop {
padding: 1rem 0.75rem; --modal-inset-block: 1rem;
align-items: center; padding-inline: 0.75rem;
}
.sui-modal {
max-height: calc(100dvh - 2rem);
} }
} }