-
Notifications
You must be signed in to change notification settings - Fork 96
Description
Flux version
2.10.2
Livewire version
3.7.6
Tailwind version
4.1.18
Browser and Operating System
Chrome on macOS
What is the problem?
When a modal's content is taller than the viewport, it can look visually complete. It's a bad UX pattern and users don't know to scroll depending on how the content hits at the cutoff. (easiest to visualize in the video below)
The native <dialog> element constrains content to the viewport with internal scrolling. But the modal's padding, rounded corners, and shadow all render within that box, so the bottom looks like the natural end.
A related issue: when content grows dynamically (accordion, async load), the centering causes the modal to jump/reposition.
The common UI patter to solve this is the <dialog> becomes a transparent full-viewport scroll container, and the visible modal is an inner panel. The whole modal scrolls as a unit, making overflow obvious and eliminating the jumping issue.
Code snippets to replicate the problem
Presents with any long content modal or any dynamic content modal (top varient fix)
<flux:modal name="terms" class="md:w-xl">
<div class="space-y-4">
<flux:heading size="lg">Terms of Service</flux:heading>
<flux:text>Please review and accept the following terms.</flux:text>
<flux:subheading size="lg">1. Introduction</flux:subheading>
<flux:text>Welcome to our platform. These Terms of Service govern your use of our website and services. By accessing or using our services, you agree to be bound by these terms.</flux:text>
<flux:subheading size="lg">2. Account Registration</flux:subheading>
<flux:text>To access certain features, you must register for an account. You agree to provide accurate, current, and complete information during registration.</flux:text>
<!-- Repeat sections 3-10 so content extends well below the viewport -->
<!-- The modal will look complete after section 5 or so. Nothing indicates sections 6-10 exist. -->
</div>
</flux:modal>Screenshots/ screen recordings of the problem
Here's a 2 min video explaining both issues and showing the fixes:
https://img.dropinblog.com/FDGMtS65
Screen recording has 4 short parts:
Problem: tall modal looks complete, content hidden below
Fix: same modal scrolls as a unit, overflowing obvious
Problem: expanding content causes modal to jump causing disorientation
Fix: modal stays pinned, grows downward when using top varient
How do you expect it to work?
The modal should scroll as a visible unit so it's immediately obvious when content extends below the fold.
The fix is to make the <dialog> a transparent full-viewport scroll container and move the visual styling to an inner panel div.
Here is a working implementation for non-flyout modals (flyouts are unaffected)
@php
// Extract 'top' variant to boolean (same pattern as 'flyout')
if ($variant === 'top') {
$top = true;
$variant = null;
}
// ... existing flyout/closable/dismissible logic unchanged ...
if ($flyout) {
// ... unchanged ...
} else {
// Dialog is now a transparent scroll container, so no classes needed on it
$classes = Flux::classes();
// Visual styling moves to the inner panel div
$panelClasses = Flux::classes()
->add('relative flux-modal-panel')
->add(($top ?? false) ? 'self-start' : '')
->add(match ($variant) {
default => 'p-6 [:where(&)]:max-w-xl [:where(&)]:min-w-xs shadow-lg rounded-xl',
'bare' => '',
})
->add(match ($variant) {
default => 'bg-white dark:bg-zinc-800 ring ring-black/5 dark:ring-zinc-700 shadow-lg rounded-xl',
'bare' => 'bg-transparent',
});
}
@endphp
{{-- ... existing <ui-modal> and <dialog> opening unchanged ... --}}
<dialog
wire:ignore.self
@if ($flyout)
{{ $styleAttributes->class($classes) }}
@else
class="flux-dialog-scroll"
@endif
@if ($name) data-modal="{{ $name }}" @endif
@if ($flyout) data-flux-flyout @endif
x-data
{{-- ... existing x-on:modal-show/close handlers unchanged ... --}}
>
@if ($flyout)
{{-- Flyout: unchanged --}}
{{ $slot }}
<?php if ($closable): ?>
{{-- ... existing close button unchanged ... --}}
<?php endif; ?>
@else
{{-- Default/Bare: scroll container with centering wrapper + panel --}}
<div
class="flex min-h-full items-center justify-center p-4 sm:p-6"
@if ($dismissible !== false)
x-data="{ _mds: false }"
x-on:mousedown.self="_mds = true"
x-on:click.self="if (_mds) $el.closest('dialog').close(); _mds = false"
@endif
>
<div {{ $styleAttributes->class($panelClasses) }}>
{{ $slot }}
<?php if ($closable): ?>
{{-- ... existing close button unchanged ... --}}
<?php endif; ?>
</div>
</div>
@endif
</dialog>Then the needed css should be something like..
/* Dialog as transparent scroll container (non-flyout) */
[data-flux-modal] > dialog.flux-dialog-scroll {
position: fixed;
inset: 0;
margin: 0;
padding: 0;
max-height: none;
max-width: none;
width: 100%;
height: 100%;
overflow-y: auto;
background: transparent;
border: none;
box-shadow: none;
outline: none;
transform: none !important;
}
/* Panel scale + fade (replaces dialog-level animation) */
[data-flux-modal] > dialog.flux-dialog-scroll .flux-modal-panel {
opacity: 0;
transform: scale(0.95);
transition: all 0.075s allow-discrete;
}
[data-flux-modal] > dialog.flux-dialog-scroll[open] .flux-modal-panel {
opacity: 1;
transform: scale(1);
transition: all 0.15s allow-discrete;
}
@starting-style {
[data-flux-modal] > dialog.flux-dialog-scroll[open] .flux-modal-panel {
opacity: 0;
transform: scale(0.95);
}
}Please confirm (incomplete submissions will not be addressed)
- I have provided easy and step-by-step instructions to reproduce the bug.
- I have provided code samples as text and NOT images.
- I understand my bug report will be closed if I haven't met the criteria above.