fix(frontend): restore quick actions menu styling and height limit

The quick actions menu (cmd+k) rendered without any background and grew
beyond the viewport:

- Its card visuals came from the global Bulma .card styles, which were
  dropped when Card.vue got its own scoped copy — QuickActions is the
  only place using a bare class="card" div, so it lost background,
  border and shadow. Give it its own card styles.
- Its height limit came from Bulma's .modal-content max-height, lost
  when the Bulma modal import was dropped in the native-dialog refactor.
  The :deep(.modal-content) position override in QuickActions never
  matched (.modal-content is an ancestor of the scoped selector, not a
  descendant). Replace both with a proper `top` modal variant that
  anchors the content 3rem below the top edge and caps its height,
  resolving the FIXME asking for exactly that option.
- The dark scrim never showed: Chromium intermittently stops painting a
  styled ::backdrop (after subtree re-renders, or while display is
  transitioned) even though getComputedStyle reports the color. Move
  the scrim onto the viewport-filling dialog element itself — same as
  the old div-based .modal-mask — and drop the display/allow-discrete
  transitions, which the JS-timed close fade never needed.
This commit is contained in:
kolaente 2026-06-11 22:31:30 +02:00
parent 53d1fa0735
commit 3e940dc9ab
2 changed files with 48 additions and 21 deletions

View File

@ -68,7 +68,7 @@ const props = withDefaults(defineProps<{
enabled?: boolean,
overflow?: boolean,
wide?: boolean,
variant?: 'default' | 'hint-modal' | 'scrolling',
variant?: 'default' | 'hint-modal' | 'scrolling' | 'top',
}>(), {
enabled: true,
overflow: false,
@ -211,7 +211,13 @@ $modal-width: 1024px;
// Reset UA dialog styles
padding: 0;
border: none;
background: transparent;
// The scrim lives on the dialog element, not on ::backdrop: Chromium
// intermittently stops painting a styled ::backdrop (e.g. after the
// dialog's subtree re-renders, or while display is transitioned) even
// though getComputedStyle still reports the color. The dialog fills the
// viewport anyway, and its opacity transition fades the scrim with it
// same as the old div-based .modal-mask.
background: rgba(0, 0, 0, .8);
color: #ffffff;
// Fill viewport
position: fixed;
@ -221,10 +227,12 @@ $modal-width: 1024px;
max-inline-size: 100%;
max-block-size: 100%;
// Transitions
// Transitions. No display/allow-discrete transition needed: the close
// fade runs while the dialog is still [open] (data-closing + timer in
// closeDialog), and transitioning display triggers the Chromium paint
// bug above.
opacity: 0;
transition: opacity 150ms ease,
display 150ms ease allow-discrete;
transition: opacity 150ms ease;
&[open]:not([data-closing]) {
opacity: 1;
@ -236,16 +244,11 @@ $modal-width: 1024px;
&::backdrop {
background-color: rgba(0, 0, 0, 0);
transition: background-color 150ms ease,
display 150ms ease allow-discrete;
}
&[open]:not([data-closing])::backdrop {
background-color: rgba(0, 0, 0, .8);
@starting-style {
background-color: rgba(0, 0, 0, 0);
}
// in quick-add mode the Electron window itself is the overlay no scrim
&:has(.is-quick-add-mode) {
background: transparent;
}
}
@ -261,7 +264,8 @@ $modal-width: 1024px;
}
.default .modal-content,
.hint-modal .modal-content {
.hint-modal .modal-content,
.top .modal-content {
text-align: center;
position: absolute;
// fine to use top/left since we're only using this to position it centered
@ -289,11 +293,31 @@ $modal-width: 1024px;
}
}
// anchored below the top edge instead of centered, used for QuickActions
.top .modal-content {
inset-block-start: 3rem;
transform: translate(-50%, 0);
max-block-size: calc(100dvh - 6rem);
overflow: auto;
[dir="rtl"] & {
transform: translate(50%, 0);
}
// the fullscreen mobile layout flows and scrolls in .modal-container
@media screen and (max-width: $tablet) {
transform: none;
max-block-size: none;
overflow: visible;
}
}
// Default width for centered modals. Scoped with :not(.is-wide) so the
// `wide` prop can still expand the modal (the .is-wide rule below would
// otherwise be outranked by .default .modal-content's specificity).
.default .modal-content:not(.is-wide),
.hint-modal .modal-content:not(.is-wide) {
.hint-modal .modal-content:not(.is-wide),
.top .modal-content:not(.is-wide) {
inline-size: calc(100% - 2rem);
max-inline-size: 640px;
@ -403,6 +427,7 @@ $modal-width: 1024px;
block-size: auto;
max-inline-size: none;
max-block-size: none;
background: transparent;
&::backdrop {
display: none;

View File

@ -2,6 +2,7 @@
<Modal
:enabled="active"
:overflow="isNewTaskCommand"
variant="top"
@close="closeQuickActions"
>
<div
@ -704,15 +705,16 @@ function reset() {
<style lang="scss" scoped>
.quick-actions {
// global Bulma .card styles are gone (ported into Card.vue, scoped),
// so this bare .card div needs its own card visuals
background-color: var(--white);
border-radius: $radius;
border: 1px solid var(--card-border-color);
box-shadow: var(--shadow-sm);
color: var(--text);
overflow: hidden;
justify-content: flex-start !important;
// FIXME: changed position should be an option of the modal
:deep(.modal-content) {
inset-block-start: 3rem;
transform: translate(-50%, 0);
}
&.is-quick-add-mode {
padding: 0;
margin: 0;