Skip to content

Commit

Permalink
Добавляет демку в aria-modal (#4807)
Browse files Browse the repository at this point in the history
* Добавляет демку

* Немного улучшает текст, добавляет контрибьютера

* Исправляет ссылку в демке

* Улучшает текст
  • Loading branch information
TatianaFokina authored Nov 10, 2023
1 parent 47a8627 commit d42942b
Show file tree
Hide file tree
Showing 2 changed files with 268 additions and 13 deletions.
245 changes: 245 additions & 0 deletions a11y/aria-modal/demos/custom-modal-dialog/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>Модальное окно с атрибутом — aria-modal — Дока</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap">
<style>
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}

html {
color-scheme: dark;
font-size: 16px;
}

body {
min-height: 100vh;
padding: 50px;
display: flex;
align-items: center;
justify-content: center;
background-color: #18191C;
color: #FFFFFF;
font-family: "Roboto", sans-serif;
}

@media (max-width: 768px) {
body {
padding: 30px;
}
}

.button, .dialog__button {
display: block;
min-width: 210px;
border: 2px solid transparent;
border-radius: 6px;
padding: 9px 15px;
color: #000000;
font-size: 1.12rem;
font-weight: 300;
font-family: inherit;
transition: background-color 0.2s linear;
}

.dialog__button {
margin: 0 auto 10px;
}

.button-aqua {
background-color: #10f3af;
}

.button:hover, .dialog__button:hover {
background-color: #FFFFFF;
cursor: pointer;
transition: background-color 0.2s linear;
}

.dialog__button:hover {
border: 2px solid #000000;
}

.button:focus-visible {
border: 2px solid #FFFFFF;
outline: none;
}

.dialog__button:focus-visible {
border: 2px solid #000000;
outline: none;
}

.button:focus {
border: 2px solid #FFFFFF;
outline: none;
}

.dialog__button:focus {
border: 2px solid #000000;
outline: none;
}

.dialog__link {
border-radius: 3px;
color: inherit;
-webkit-text-decoration-color: #10f3af;
text-decoration-color: #10f3af;
text-decoration-thickness: 2px;
transition: background-color 0.2s linear;
}

.dialog__link:hover, .dialog__link:focus {
background-color: #10f3af;
transition: background-color 0.2s linear;
outline-width: 0;
}

.dialog {
display: none;
width: calc(100% - 40px);
max-width: 400px;
color: #000000;
background-color: #FFFFFF;
position: fixed;
padding: 16px;
transition: transform 0.3s ease-in-out;
}

.dialog__title {
margin-top: 0;
margin-bottom: 17px;
font-weight: 500;
font-size: 1.25rem;
}

.dialog p {
margin: 15px 0;
font-size: 1rem;
line-height: 1.4;
}
</style>
</head>
<body>
<button class="button button-aqua opener" type="button" aria-controls="my-dialog" onclick="openDialog(event)">
Завершить покупку
</button>

<div role="dialog" class="dialog" id="my-dialog" aria-modal="true" aria-labelledby="label" aria-describedby="description">
<h3 class="dialog__title" id="label">Заказ был отправлен!</h3>
<p id="description">
Заказ будет доставлен в течение следующих 3–5 рабочих дней.
</p>
<button class="dialog__button button-aqua" id="dialog-button">Закрыть</button>
<a href="#" class="dialog__link">Детали заказа</a>
</div>

<script>
function openDialog({
target: button
}) {
const dialogID = button.getAttribute("aria-controls")
const dialogButton = document.querySelector("#dialog-button")
const dialogElement = document.querySelector(`#${dialogID}`)
const pageElements = Array.from(document.body.children)

setTimeout(() => {
document.addEventListener("click", closeDialogOnOutsideClick)
document.addEventListener("keydown", closeDialogOnPressEsc)
dialogElement.addEventListener("click", closeDialogOnButtonClick)

dialogElement.addEventListener("close", () =>
document.removeEventListener("click", closeDialogOnOutsideClick)
)

dialogElement.style.display = "block"
dialogButton.focus()
dialogElement.addEventListener("keydown", setFocusTrap)
}, 200)

button.addEventListener("refocusAfterClose", function() {
button.focus()
})

pageElements.forEach(child => {
if (child !== dialogElement) {
child.setAttribute("inert", "")
}
})

function setFocusTrap(event) {
const interactiveDialogElements = getInteractiveElements(dialogElement)
const firstInteractiveDialogElement = interactiveDialogElements[0]
const lastInteractiveDialogElement = interactiveDialogElements[interactiveDialogElements.length - 1]

if (event.key === "Tab" && !event.shiftKey) {
if (document.activeElement === lastInteractiveDialogElement) {
firstInteractiveDialogElement.focus()
event.preventDefault()
}
} else if (event.key === "Tab" && event.shiftKey) {
if (document.activeElement === firstInteractiveDialogElement) {
lastInteractiveDialogElement.focus()
event.preventDefault()
}
}
}

function getInteractiveElements(element) {
return element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
}

function closeDialogOnOutsideClick({
target
}) {
const isClickOnDialog = target === dialogElement
const isClickOnDialogChildrenNodes = dialogElement.contains(target)

const isClickOutsideOfDialog = !(
isClickOnDialog || isClickOnDialogChildrenNodes
)

if (isClickOutsideOfDialog) {
closeDialogAndRefocus()
}
}

function closeDialogOnButtonClick({
target
}) {
if (target === dialogButton) {
closeDialogAndRefocus()
}
}

function closeDialogOnPressEsc(event) {
if(event.key === "Escape") {
closeDialogAndRefocus()
}
}

function closeDialogAndRefocus() {
dialogElement.style.display = "none"

pageElements.forEach(child => {
child.removeAttribute("inert")
})

setTimeout(() => {
let refocusEvent = new Event("refocusAfterClose")
button.dispatchEvent(refocusEvent)
}, 50)
}
}
</script>
</body>
</html>
36 changes: 23 additions & 13 deletions a11y/aria-modal/index.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
title: "`aria-modal`"
description: "ARIA-атрибут, который сообщает вспомогательным технологиям о том, что элемент модальный."
description: "ARIA-атрибут, который сообщает вспомогательным технологиям о модальности элемента на странице."
authors:
- tatianafokina
contributors:
- skorobaeus
keywords:
- доступность
- a11y
- ARIA
- модальный диалог
related:
- a11y/role-dialog
- a11y/role-alertdialog
Expand All @@ -22,40 +26,46 @@ tags:

<aside>

🙂 У `aria-modal` сейчас [неплохая поддержка](https://a11ysupport.io/tech/aria/aria-modal_attribute). Его пока не поддерживают только Narrator и TalkBack.
🙂 Сейчас у `aria-modal` [неплохая поддержка](https://a11ysupport.io/tech/aria/aria-modal_attribute). Пока его не поддерживают Narrator и мобильный TalkBack.

</aside>

## Пример

```html
<div
role="alertdialog"
role="dialog"
aria-modal="true"
aria-labelledby="label"
aria-describedby="description"
>
<h2 id="label">Важное уточнение</h2>
<p>Точно хотите посадить дерево?</p>
<button type="button" onclick="growTree(this)">Да</button>
<button type="button" onclick="deleteTree(this)">Нет</button>
<h3 id="label">Заказ был отправлен!</h3>
<p id="description">
Заказ будет доставлен в течение следующих
3–5 рабочих дней.
</p>
<button>Закрыть</button>
<a href="#details">Детали заказа</a>
</div>
```

<iframe title="Модальное окно с атрибутом" src="demos/custom-modal-dialog/" height="350"></iframe>

## Как пишется

Задайте элементу атрибут `aria-modal` с одним из двух значений:

- `false` — это не модальный элемент.
- `false` — это немодальный элемент.
- `true` — это модальный элемент.

Атрибут можно использовать только для некоторых ролей и атрибутов:
`aria-modal` можно использовать только для некоторых HTML-тегов и [ARIA-ролей](/a11y/aria-roles/):

- [`<dialog>`](/html/dialog/) или вместе с ролью [`dialog`](/a11y/role-dialog/);
- с ролью [`alertdialog`](/a11y/role-alertdialog/).
- [`<dialog>`](/html/dialog/) или вместе [с ролью `dialog`](/a11y/role-dialog/);
- [с ролью `alertdialog`](/a11y/role-alertdialog/).

Если показываете модальное окно с `<dialog>` с помощью метода `showModal()`, в этом случае не нужно задавать ему `aria-modal` и менять динамически значения. В методе уже по умолчанию используется `aria-modal="true"`.
Когда показываете модальное окно с `<dialog>` с помощью метода `.showModal()`, не нужно задавать ему `aria-modal` и менять динамически значения. В методе уже по умолчанию есть `aria-modal="true"`.

Если это немодальное окно с `<dialog>`, и для его показа используете другой метод `show()`, к тегу применится `aria-modal="false"`.
Если это немодальное окно с `<dialog>`, и для его показа используете другой метод `.show()`, к тегу применится `aria-modal="false"`.

Обратите внимание, что `aria-modal` не изменяет поведение элементов, а только обозначает модальность для пользователей вспомогательных технологий. Сделать окно по-настоящему модальным помогут JavaScript и CSS.

Expand Down

0 comments on commit d42942b

Please sign in to comment.