Modal windows for small screens using Bootstrap 2 and vertical media queries
For a project in 2013 that was a responsive web app primarily designed to work on mobiles in a way that mimicked a native app the design required a modal window. I used the modal component from (the then current) version 2 of Bootstrap but whilst - out of the box - it worked well on large screens, it didn't work well on small screens.
Note: Modal windows on websites are a very poor UX choice and I would strongly recommend that you find a better alternative way of presenting information rather than use a modal window.
The issue
The standard Bootstrap modal window uses position: fixed
which means it stays in place as the main content scrolls behind it. On a screen with a small vertical viewport this will mean that the bottom of the modal is cut off and there's no way to scroll down to it. (There's also issues with position: fixed
on a number of platforms but I'm glossing over that.)
The solution
To overcome this we start with position: absolute
and don't set a height for the modal body as this allows full scrolling of the modal with the main content. We then use a vertical media query which, when there's a high enough viewport, we use to set position: fixed
and provide a height for the modal that keeps us within the viewport that we know is available to us.
.modal {
position: absolute;
top: 10%;
}
@media (min-height: 40em) {
.modal {
position: fixed;
}
.modal-body {
max-height: 35em; /* This should be less than the min-height in the media query. */
overflow-y: auto;
}
}
Interaction
Whilst the CSS makes the modal usable, the interaction needs some work. To do this we hook into the Bootstrap modal API to control the scroll position when we're using position: absolute
. This has the effect of making the modal appear as the main content and, when dismissed, we return the user to the same position from where the modal was activated.
$(document).ready(function () {
var modal_window = $('#modal-window');
$('a[rel=modal]').on('click', function (e) {
var scroll_position = $(window).scrollTop(), // Where did we start in the window.
return_position = false; // Should we return to the start position?
e.preventDefault();
// Build and show the modal.
modal_window.modal({
'remote': $(this).attr('href') + ' #modal-target' // Get remote content from the link.
}).on('show', function () {
if (modal_window.css('position') === 'absolute') {
// We will need to return to where we were.
return_position = true;
// Jump to the top of the modal.
$(window).scrollTop(modal_window.offset().top);
}
}).on('hidden', function () {
if (return_position) {
// Return to where we were.
$(window).scrollTop(scroll_position);
}
}).modal('show');
});
});
Demonstration
Probably the most effective way of understanding this is to see it in action.