Оформление модальных окон
Оригинал статьи: Considerations for Styling a Modal
Оглавление:- Расположение в DOM
- Центрирование
- Фиксированное позиционирование
- Ширина
- Высота
- Переполнение
- Кнопки
- Наложение модального окна
- Специальный класс для закрытия окна, а не для открытия
- Открытие/закрытие окна
Модальное окно. Небольшой блок, появляющийся на экране, чтобы сообщить вам что-то важное. Какие с ним могут возникнуть сложности? Хм, нельзя сказать, что слишком суровые, но могут возникнуть. В этой статье я изложу некоторое количество соображений и хитростей о том, как сделать все правильно. Приступим.
Расположение в DOM
Я обычно размещаю модальное окно перед закрывающим тегом </body>
.
<div class="modal" id="modal"></div>
</body>
</html>
Это делается ради удобства применения стилей. Модальное окно проще позиционировать, когда у вас уже есть готовое тело страницы, а не неизвестный набор родительских элементов, у каждого из которых может быть свой контекст позиционирования.
Насколько это подходит для работы со скринридерами? Я не эксперт в области доступности, но я слышал, что модальные окна вызывают сложности.
Вот, что говорит Роб Додсон:
Каждый, кто когда-либо пытался сделать модальное окно доступным, знает, что несмотря на внешнюю безобидность, модальные окна имеют решающее значение для доступности сайта, это как битва с боссом в игре. Они разжуют вас и выплюнут. Например, в правильно сделанном модальном окне должны быть реализованы следующие возможности:
- Фокус клавиатуры должен перемещаться в модальное окно, а после закрытия возвращен на предыдущий активный элемент.
- Фокус клавиатуры должен захватываться модальным окном, чтобы пользователь случайно из него не вышел.
- Скринридеры также должны захватываться модальным окном для предотвращения внезапного исчезновения.
Роб предлагает демо невероятно доступного модального окна. Есть еще недавний пример Ноа Бло и Николаса Хоффмана. Также подойдет ARIA Live Regions.
Если вы самостоятельно решаете вопрос с фокусом, расположение модального окна внизу документа будет совершенно приемлемым.
Центрирование
Для начала ознакомьтесь с полным руководством по центрированию в CSS (русский перевод).
Я использую один из моих любимых трюков. С его помощью можно центрировать блок вертикально и горизонтально, не зная его ширины или высоты.
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
Этот метод отлично подходит для модальных окон, которые, как правило располагаются по центру и могут быть разной ширины. Изменение высоты еще более вероятно, так как высота всегда зависит от количества содержимого в модальном окне.
Если вы знаете точно высоту и ширину модального окна, то вы можете использовать и другие методы центрирования. Я поднимаю эту тему потому, что в моем методе есть шанс легкого размытия текста из-за трансформаций, поэтому если вы столкнетесь с подобной проблемой вам стоит обратить внимание на другие методы из руководства по центрированию (например, на отрицательные отступы).
Фиксированное позиционирование
Обратите внимание. что мы используем position: fixed;
. Смысл этого в том, что, если пользователь прокрутит страницу и вызовет модальное окно, оно будет отцентрировано и видимо для пользователя, независимо от степени прокрутки.
В общем, я считаю, что фиксированное позиционирование в наши дни безопасно, даже на мобильных устройствах. Но если вы знаете, что вам придется сталкиваться со старыми мобильными устройствами, фиксированное позиционирование может стать проблемой и тогда вам придется использовать что-то вроде position: absolute;
с принудительным скроллингом вверх страницы. Или что-то еще, тестируйте, в общем.
Ширина
На больших экранах типичное модальное окно выглядит не только центрированным, но также и ограниченным по ширине (как на предыдущем скриншоте).
.modal {
/* код, который уже был выложен выше*/
width: 600px;
}
Это опасный подход. Мы получим то, что нам надо на больших экранах, но существует множество устройств, экран которых не достигает и 600 пикселей в ширину.
Это легко исправить с помощью свойства max-width
:
.modal {
/* код, который уже был выложен выше */
width: 600px;
max-width: 100%;
}
Высота
Задание высоты еще более проблематично. Мы все знаем, что контент меняется. Плюс техника центрирования при помощи трансформации с легкостью срежет верхнюю часть модального окна, не предоставив никакой полосы прокрутки.
Нас опять спасет задание максимума, в данном случае max-height
:
.modal {
/* код, который уже был выложен выше */
height: 400px;
max-height: 100%;
}
Переполнение
Итак, раз мы всерьез озадачились высотой модального окна, нам надо подумать и о его возможном переполнении. Есть соблазн использовать свойство overflow
непосредственно на .modal
, но здесь возникает пара проблем:
- у нас может быть желание обойтись без скроллинга в некоторых местах;
- переполнение обрезает
box-shadow
, что может нам не подойти.
Я думаю использовать для этого внутренний контейнер:
<div class="modal" id="modal">
<!-- то, что не прокручивается -->
<div class="modal-guts">
<!-- то, что прокручивается -->
</div>
</div>
Для того, чтобы внутренний блок .modal-guts
прокручивался, ему нужна высота. Это решается несколькими способами. Один из них это позиционирование этого блока так, чтобы он покрывал модальное окно целиком с последующим добавлением свойства overflow
:
.modal-guts {
/* код, который уже был выложен выше */
/* расположение над модальным окном */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* отступы при необходимости */
padding: 20px 50px 20px 20px;
/* прокрутка */
overflow: auto;
}
Кнопки
Цель модального окна это форсировать требуемое действие, прежде чем будет сделано что-либо еще. Если вам не надо форсировать действие, вам стоит использовать другой элемент пользовательского интерфейса вместо модального окна. И, конечно, нужен какой-нибудь способ выйти из модального окна. Традиционно используются кнопки опций (типа “удалить/отменить”), еще более распространена кнопка закрытия. Ей мы и займемся.
Кнопка закрытия должна быть всегда видимой, чтобы у пользователя не возникало проблем с пониманием того, как закрыть модальное окно. Именно по этому мы и сделали не прокручиваемый участок.
Наложение модального окна
Модальное окно часто сопровождается полноэкранным слоем-наложением. Это полезно по ряду причин:
- затемняется (или иным способом заглушается) остальная часть экрана, вынуждая пользователя сначала разобраться с модальным окном;
- наложение можно использовать для предотвращения нажатий и иных взаимодействий с содержимым вне модального окна;
- наложение можно использовать в качестве очень большой кнопки закрытия модального окна (или кнопки “отмены” и иных безопасных действий).
Типичные разметка и стили для этого:
<div class="modal" id="modal">
<!-- содержимое модального окна -->
</div>
<div class="modal-overlay" id="modal-overlay">
</div>
.modal {
/* код, который уже был выложен выше */
z-index: 1010;
}
.modal-overlay {
/* рекомендация:
не зацикливайтесь на числе "1000", вместо этого используйте свою документированную систему для z-index и не забывайте следовать ей. Это число должно быть достаточно высоким в вашей системе.
*/
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Специальный класс для закрытия окна, а не для открытия
Мне казалось заманчивой идеей скрыть класс .modal
по умолчанию с помощью display: none;
. А для открытия добавить класс .modal.open
со стилями { display: block; }
.
Вы заметили display: block;
, не так ли? Я вижу здесь проблему. Правило display: none;
очень полезно, так как скрывает модальное окно как чисто визуально, так и от вспомогательных технологий. Его проще применить для замены существующего свойства display
, чем действовать наоборот, угадывая значение display
, заменяющее none
. Ведь ваше модальное окно может использовать display: flex
илиdisplay: grid;
, или что-либо еще. Различные варианты модальных окон могут использовать то, что им нужно, не беспокоясь, что их сбросят на display: block;
.
.modal {
/* например, так... */
display: flex;
}
.modal.closed {
display: none;
}
Открытие/закрытие окна
Вот наиболее простой возможный способ открытия и закрытия полученного модального окна:
var modal = document.querySelector("#modal");
var modalOverlay = document.querySelector("#modal-overlay");
var closeButton = document.querySelector("#close-button");
var openButton = document.querySelector("#open-button");
closeButton.addEventListener("click", function() {
modal.classList.toggle("closed");
modalOverlay.classList.toggle("closed");
});
openButton.addEventListener("click", function() {
modal.classList.toggle("closed");
modalOverlay.classList.toggle("closed");
});
И это пример без добавления доступности (помните, что мы о ней говорили). Вот демо с перемещением, захватом и возвратом фокуса в исходное место.
See the Pen Considerations for Styling a Modal by Chris Coyier (@chriscoyier) on CodePen.