-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
206 lines (187 loc) · 10.1 KB
/
index.html
File metadata and controls
206 lines (187 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Modal - Resonance Labs</title>
<link rel="stylesheet" href="/resonance-labs/assets/style/global.css" />
<link rel="stylesheet" href="modal.css" />
<link
rel="icon"
href="/resonance-labs/assets/images/favicon.ico"
type="image/x-icon"
/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" />
<script src="/resonance-labs/assets/js/component-layout.js" defer></script>
</head>
<body>
<header>
<a href="/resonance-labs/index.html" class="back-link">
<img src="/resonance-labs/assets/images/logo.svg" alt="Resonance Logo" />
</a>
</header>
<main>
<section class="section">
<h1>Modal</h1>
<p class="component-tagline">A modal dialog overlays the page to present content or request a decision, blocking interaction with the rest of the interface until it is dismissed.</p>
</section>
<section class="section">
<h2>Demo</h2>
<p>Click the "Open Modal" button to launch the dialog. While the modal is open, try pressing Tab to cycle through focusable elements and Escape to close it. Confirm that focus returns to the trigger button after the modal closes.</p>
<div class="mount"></div>
</section>
<section class="section">
<h2>What to Observe</h2>
<ul>
<li>Focus moves into the modal immediately when it opens, landing on the first focusable element.</li>
<li>Tab and Shift+Tab cycle only through elements inside the modal — focus cannot escape to the page behind it.</li>
<li>Pressing Escape closes the modal and returns focus to the element that triggered it.</li>
<li>Screen readers announce the dialog role and its accessible name when it opens.</li>
<li>The content behind the modal is inert — it cannot be clicked, scrolled, or reached by keyboard while the dialog is open.</li>
</ul>
</section>
<section class="section">
<h2>Anatomy</h2>
<p class="anatomy-placeholder">[Anatomy image placeholder — will be added when assets are available]</p>
<ol>
<li><strong>Backdrop / overlay:</strong> The dimmed layer covering the page that signals the modal is active and prevents interaction with background content.</li>
<li><strong>Dialog container:</strong> The panel that holds all modal content, marked up with the dialog role and an accessible name.</li>
<li><strong>Dialog title:</strong> The heading inside the dialog that names the purpose of the modal, used as its accessible label.</li>
<li><strong>Body content:</strong> The main message, form, or information displayed within the dialog.</li>
<li><strong>Action buttons:</strong> Buttons that confirm, cancel, or otherwise resolve the modal's purpose.</li>
<li><strong>Close button:</strong> An explicit control, typically in the header area, that dismisses the modal.</li>
</ol>
</section>
<section class="section">
<h2>Accessibility Behavior</h2>
<ul>
<li>The dialog element must have an accessible name, typically derived from its visible title.</li>
<li>When the modal opens, focus must move inside it automatically — do not leave focus on the trigger or on the document body.</li>
<li>Focus must be trapped within the modal while it is open; Tab and Shift+Tab must not allow focus to reach elements behind the overlay.</li>
<li>Pressing Escape must close the modal and return focus to the element that originally opened it.</li>
<li>Background content must be hidden from assistive technologies while the modal is open.</li>
<li>All interactive controls inside the modal must be reachable and operable by keyboard.</li>
</ul>
</section>
<section class="section">
<h2>Common Mistakes</h2>
<ul>
<li>Not trapping focus inside the modal, allowing keyboard users to Tab into hidden background content.</li>
<li>Failing to return focus to the trigger when the modal closes, leaving keyboard users at the top of the page.</li>
<li>Using a div instead of a proper dialog element or not applying the correct role, so screen readers do not identify it as a dialog.</li>
<li>Omitting an accessible name on the dialog, so screen readers announce it without context.</li>
<li>Relying solely on clicking the backdrop to close the modal, with no keyboard-accessible dismissal path.</li>
</ul>
</section>
<section class="section">
<h2>Why This Matters</h2>
<p>Modals interrupt the normal page flow and demand a user's attention. When focus management is broken, keyboard and screen reader users can become disoriented — either stuck inside a dialog they cannot exit or dropped back at the top of the page after closing one. Proper focus trapping and restoration ensure that modals feel predictable and safe for everyone, preventing the frustration of losing one's place in a complex workflow.</p>
</section>
<section class="section">
<h2>Accessibility Validation</h2>
<p>This component is validated against internal accessibility criteria aligned with WCAG standards, using our internally developed system, <strong>Resonance Specs</strong>.</p>
<p>To learn more, please contact us.</p>
</section>
<section class="section">
<h2>Code</h2>
<div class="code-tabs" role="tablist">
<button class="code-tabs__tab code-tabs__tab--active" role="tab" aria-selected="true" data-tab="html">HTML</button>
<button class="code-tabs__tab" role="tab" aria-selected="false" data-tab="css">CSS</button>
<button class="code-tabs__tab" role="tab" aria-selected="false" data-tab="js">JS</button>
</div>
<div class="code-preview" id="panel-html" role="tabpanel">
<pre class="line-numbers"><code id="code-html" class="language-markup"></code></pre>
<button class="code-preview__copy" data-target="code-html">Copy Code</button>
</div>
<div class="code-preview" id="panel-css" role="tabpanel" hidden>
<pre class="line-numbers"><code id="code-css" class="language-css"></code></pre>
<button class="code-preview__copy" data-target="code-css">Copy Code</button>
</div>
<div class="code-preview" id="panel-js" role="tabpanel" hidden>
<pre class="line-numbers"><code id="code-js" class="language-javascript"></code></pre>
<button class="code-preview__copy" data-target="code-js">Copy Code</button>
</div>
</section>
<section class="section">
<h2>Reference Implementation</h2>
<ul class="btn-group">
<li>
<a class="btn btn-primary" href="/resonance-labs/index.html">Back to Home</a>
</li>
<li>
<a
class="btn btn-secondary"
href="https://github.com/Accenture/resonance-labs"
>GitHub Code</a
>
</li>
</ul>
</section>
</main>
<footer>
<p><strong>Created and maintained by Accenture Song</strong></p>
<p>© 2026 Accenture - Resonance</p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
<script>
// Fetch HTML fragment
fetch("./modal.html")
.then(function (r) { return r.text(); })
.then(function (html) {
document.querySelector(".mount").innerHTML = html;
// Populate HTML code preview (strip leading comment block)
var rawHtml = html.replace(/<!--[\s\S]*?-->\n*/, "").trim();
var codeHtml = document.getElementById("code-html");
codeHtml.textContent = rawHtml;
Prism.highlightElement(codeHtml);
// Load component JS
var s = document.createElement("script");
s.src = "./modal.js";
document.body.appendChild(s);
});
// Fetch CSS
fetch("./modal.css")
.then(function (r) { return r.text(); })
.then(function (css) {
var codeCss = document.getElementById("code-css");
codeCss.textContent = css;
Prism.highlightElement(codeCss);
});
// Fetch JS
fetch("./modal.js")
.then(function (r) { return r.text(); })
.then(function (js) {
var codeJs = document.getElementById("code-js");
codeJs.textContent = js;
Prism.highlightElement(codeJs);
});
// Tab switching
var tabs = document.querySelectorAll(".code-tabs__tab");
var codePanels = document.querySelectorAll("#panel-html, #panel-css, #panel-js");
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
tabs.forEach(function (t) {
t.classList.remove("code-tabs__tab--active");
t.setAttribute("aria-selected", "false");
});
codePanels.forEach(function (p) { p.hidden = true; });
tab.classList.add("code-tabs__tab--active");
tab.setAttribute("aria-selected", "true");
document.getElementById("panel-" + tab.dataset.tab).hidden = false;
});
});
// Copy buttons
document.querySelectorAll(".code-preview__copy").forEach(function (btn) {
btn.addEventListener("click", function () {
var codeEl = document.getElementById(btn.dataset.target);
navigator.clipboard.writeText(codeEl.textContent).then(function () {
btn.textContent = "Copied!";
setTimeout(function () { btn.textContent = "Copy Code"; }, 2000);
});
});
});
</script>
</body>
</html>