70 lines
3.4 KiB
HTML
70 lines
3.4 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Non-ARIA interactive patterns</title>
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
body { font-family: -apple-system, Segoe UI, Roboto, sans-serif; margin: 0; color: #1a1a1a; background: #fff; }
|
||
main { max-width: 760px; margin: 0 auto; padding: 48px 24px; }
|
||
h1 { font-size: 30px; } h2 { font-size: 20px; margin-top: 40px; }
|
||
|
||
/* A custom dropdown with NO aria — just a div with a click handler. */
|
||
.cmenu { position: relative; display: inline-block; }
|
||
.cbtn { cursor: pointer; user-select: none; border: 1px solid #d1d5db; border-radius: 8px; padding: 10px 16px; font-size: 15px; background: #fff; }
|
||
.cbtn.is-open { background: #eef2ff; border-color: #4f46e5; color: #4f46e5; }
|
||
.cpanel { position: absolute; top: calc(100% + 6px); left: 0; min-width: 200px; background: #fff; border: 1px solid #e5e7eb; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 8px; }
|
||
.cpanel { display: none; }
|
||
.cpanel.is-open { display: block; }
|
||
.cpanel a { display: block; padding: 8px 12px; border-radius: 6px; color: #1a1a1a; text-decoration: none; }
|
||
|
||
/* A custom modal with NO aria — div trigger + div overlay. */
|
||
.open-x { cursor: pointer; display: inline-block; border: 1px solid #d1d5db; border-radius: 8px; padding: 10px 16px; font-size: 15px; }
|
||
.ovl { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 50; display: none; align-items: center; justify-content: center; }
|
||
.ovl.is-open { display: flex; }
|
||
.box { background: #fff; border-radius: 14px; padding: 28px; max-width: 440px; width: 90%; }
|
||
.x { float: right; cursor: pointer; font-size: 22px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
<h1>Non-ARIA patterns</h1>
|
||
<p>A custom dropdown and modal built from plain <div>s with click handlers and no ARIA at all — the kind the recognizer can only find via real event listeners + drive-and-diff.</p>
|
||
|
||
<h2>Custom dropdown</h2>
|
||
<div class="cmenu">
|
||
<div class="cbtn" id="cbtn">Resources ▾</div>
|
||
<div class="cpanel" id="cpanel">
|
||
<a href="#docs">Documentation</a>
|
||
<a href="#guides">Guides</a>
|
||
<a href="#api">API reference</a>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Custom modal</h2>
|
||
<div class="open-x" id="openx">Contact us</div>
|
||
<div class="ovl" id="ovl">
|
||
<div class="box">
|
||
<span class="x" id="closex">×</span>
|
||
<h3>Contact us</h3>
|
||
<p>Reach the team at hello@example.com — we usually reply within a day.</p>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<script>
|
||
const cbtn = document.getElementById("cbtn"), cpanel = document.getElementById("cpanel");
|
||
cbtn.addEventListener("click", () => {
|
||
const open = cpanel.classList.toggle("is-open");
|
||
cbtn.classList.toggle("is-open", open);
|
||
});
|
||
const ovl = document.getElementById("ovl");
|
||
document.getElementById("openx").addEventListener("click", () => ovl.classList.add("is-open"));
|
||
document.getElementById("closex").addEventListener("click", () => ovl.classList.remove("is-open"));
|
||
ovl.addEventListener("click", (e) => { if (e.target === ovl) ovl.classList.remove("is-open"); });
|
||
document.addEventListener("keydown", (e) => { if (e.key === "Escape") { ovl.classList.remove("is-open"); cpanel.classList.remove("is-open"); cbtn.classList.remove("is-open"); } });
|
||
</script>
|
||
</body>
|
||
</html>
|