Skip to content

Commit 6c7023e

Browse files
committed
fix: add intro section
1 parent 873f14e commit 6c7023e

File tree

10 files changed

+708
-76
lines changed

10 files changed

+708
-76
lines changed

.vitepress/config.mts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ export default defineConfig({
99
sidebar: [
1010
{
1111
items: [
12+
{ text: "Introduction", link: "/intro" },
1213
{ text: "WAI-ARIA", link: "/wai-aria" },
1314
{ text: "Semantic HTML", link: "/semantic-html" },
1415
{ text: "Accessible Form", link: "/accessible-forms" },
16+
{ text: "Visibility Methods", link: "/visibility-methods" },
17+
// { text: "Accessible Multimedia", link: "/multimedia" },
1518
{ text: "Focus Control", link: "/focus-control" },
1619
{
1720
text: "Mouse and Pointer Events",

.vitepress/theme/custom.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@
99
--vp-c-text: #ffffff;
1010
--vp-c-bg-alt: #121212;
1111
}
12+
13+
iframe {
14+
width: 100%;
15+
}

components/visibility-widget.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { registerAll } from "@tapsioss/web-components";
2+
registerAll();
3+
4+
class VisibilityWidget extends HTMLElement {
5+
private isToggled: boolean;
6+
constructor() {
7+
super();
8+
this.attachShadow({ mode: "open" });
9+
this.isToggled = false;
10+
setTimeout(() => this.render());
11+
}
12+
13+
get name() {
14+
return `\`${this.getAttribute("option-name")}\`` || "Feature";
15+
}
16+
17+
get classesToToggle() {
18+
return this.getAttribute("classes-to-toggle") || "";
19+
}
20+
21+
get attributeToToggle() {
22+
return this.getAttribute("attribute-to-toggle");
23+
}
24+
25+
toggle() {
26+
this.isToggled = !this.isToggled;
27+
this.render();
28+
}
29+
30+
render() {
31+
const container = document.createElement("div");
32+
const toggledClass =
33+
this.isToggled && this.classesToToggle ? this.classesToToggle : "";
34+
35+
const attributes =
36+
this.attributeToToggle && this.isToggled
37+
? `${this.attributeToToggle}="true"`
38+
: "";
39+
40+
container.innerHTML = `
41+
<style>
42+
figure {
43+
margin: 1.5rem 0;
44+
}
45+
button {
46+
border: 2px solid black;
47+
background: black;
48+
color: white;
49+
border-radius: 0.375rem;
50+
padding: 0.5rem;
51+
box-shadow: inset 0 1px 2px rgba(0,0,0,0.6);
52+
margin-right: 0.5rem;
53+
min-width: 220px;
54+
cursor: pointer;
55+
}
56+
a {
57+
text-decoration: underline;
58+
}
59+
.playground {
60+
background: white;
61+
display: flex;
62+
align-items: center;
63+
gap: 1rem;
64+
padding: 1rem;
65+
}
66+
.sr-only {
67+
position: absolute;
68+
width: 1px;
69+
height: 1px;
70+
padding: 0;
71+
margin: -1px;
72+
overflow: hidden;
73+
clip: rect(0, 0, 0, 0);
74+
white-space: nowrap;
75+
border-width: 0;
76+
}
77+
.invisible {
78+
visibility: hidden;
79+
}
80+
.hidden {
81+
display: none;
82+
}
83+
.opacity-0 {
84+
opacity: 0;
85+
}
86+
</style>
87+
<figure>
88+
<tapsi-button variant="brand" part="toggle" style="margin-bottom: 1rem;">
89+
Turn ${this.name} ${this.isToggled ? "off" : "on"}
90+
</tapsi-button>
91+
<br>
92+
<div class="playground">
93+
<div class="${toggledClass}" ${attributes}><tapsi-badge color="info" value="Target Element"></tapsi-badge></div>
94+
<div><tapsi-badge color="info" class="ml-2" value="Content after target element"></tapsi-badge></div>
95+
</div>
96+
</figure>
97+
`;
98+
99+
if (this.shadowRoot) {
100+
this.shadowRoot.innerHTML = "";
101+
this.shadowRoot.appendChild(container);
102+
103+
const button = this.shadowRoot.querySelector("tapsi-button");
104+
button?.addEventListener("click", () => this.toggle());
105+
}
106+
}
107+
}
108+
109+
customElements.define("visibility-widget", VisibilityWidget);

dev-checklists.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
outline: "deep"
33
---
44

5+
<script setup>
6+
import { registerAll } from '@tapsioss/web-components';
7+
8+
registerAll();
9+
10+
const openModal = () => {
11+
document.getElementById('sample-modal').show();
12+
};
13+
const closeModal = () => {
14+
document.getElementById('sample-modal').hide();
15+
};
16+
</script>
17+
518
# Development Checklists
619

720
There are a number of tools we can use to assist in the creation of accessible web applications.
@@ -15,6 +28,12 @@ By far the easiest and also one of the most important checks is to test if your
1528
3. Using `Enter` to activate elements.
1629
4. Where required, using your keyboard arrow keys to interact with some elements, such as menus and dropdowns.
1730

31+
::: info
32+
33+
Note that not everything has to be interactive for screen readers (e.g. headings)!
34+
35+
:::
36+
1837
## Increase text size to 200%!
1938

2039
Is the content still readable? Does increasing the text size cause content to overlap?
@@ -38,3 +57,161 @@ How do we test to ensure there are minimal to no proximity issues with our desig
3857
See the Pen <a href="https://codepen.io/svinkle/pen/LrqNPV/">Better Proximity</a> by Scott Vinkle
3958
(<a href="https://codepen.io/svinkle">@svinkle</a>) on <a href="https://codepen.io">CodePen</a>.
4059
</iframe>
60+
61+
62+
## Modals
63+
64+
Modals are a big one. Anything that opens as a layer on top of other content has accessibility requirements, including:
65+
66+
- Sending focus into the new content when it opens.
67+
- Restoring focus to the element the user was on previously when closing the layer.
68+
- Preventing keyboard and screen reader interaction with elements in the background.
69+
- Using a `dialog` role, focusable and labeled buttons and CTAs.
70+
71+
Non-modal dialogs don’t have all of the same background requirements. But the goal is still to move focus into relevant content when a non-modal dialog opens and closes.
72+
73+
<tapsi-button @click="openModal" variant="brand" id="open-modal-btn">Open a sample modal</tapsi-button>
74+
<tapsi-modal style="z-index: 999" id="sample-modal" heading="a sample modal" description="As you can see. the focus is now on the first focusable element inside the modal.">
75+
<tapsi-button-group slot="action-bar">
76+
<tapsi-button @click="closeModal">Got it</tapsi-button>
77+
</tapsi-button-group>
78+
</tapsi-modal>
79+
80+
81+
## Don't be a `div` button creator!
82+
83+
It’s real easy to add a click event to a `div`. Too easy, in fact. And it happens all the time! the problem is, `div`s are not interactive elements so you have to backfill quite a few things to make them accessible. All the while, you could have just used a `<button>` element and been done with it.
84+
85+
::: details What if we really need to be a `div` button creator?!
86+
87+
First, do the easiest part!
88+
89+
```tsx
90+
const MyComponent = () => {
91+
const fn = () => {
92+
console.log("clicked on the div!")
93+
}
94+
95+
return (
96+
<div
97+
onClick={fn} // [!code focus]
98+
>
99+
click!
100+
</div>
101+
)
102+
}
103+
```
104+
105+
Then we set a `role="button"` attribute. based on [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/button_role):
106+
107+
> The button role is for clickable elements that trigger a response when activated by the user. Adding role="button" tells the screen reader the element is a button, but provides no button functionality.
108+
109+
110+
```tsx
111+
const MyComponent = () => {
112+
const fn = () => {
113+
console.log("clicked on the div!")
114+
}
115+
116+
return (
117+
<div
118+
role="button" // [!code focus]
119+
onClick={fn}
120+
>
121+
click!
122+
</div>
123+
)
124+
}
125+
```
126+
127+
Now we need should pass key down event handler for keyboard interaction:
128+
129+
```tsx
130+
const MyComponent = () => {
131+
const fn = () => {
132+
console.log("clicked on the div!")
133+
}
134+
135+
const handleKeyDown = (e: KeyboardEvent) => { // [!code focus]
136+
if (event.code === 'Space' || event.code === 'Enter') { // [!code focus]
137+
fn(); // [!code focus]
138+
} // [!code focus]
139+
} // [!code focus]
140+
141+
return (
142+
<div
143+
role="button"
144+
onClick={fn}
145+
onKeyDown={handleKeyDown} // [!code focus]
146+
>
147+
click!
148+
</div>
149+
)
150+
}
151+
```
152+
153+
We are not done yet! The element can trigger the `fn` function using `Enter` and `Space` keys, but the problem is the element is not focusable and we should bass a `tabIndex` attribute to the `div` element.
154+
155+
```tsx
156+
const MyComponent = () => {
157+
const fn = () => {
158+
console.log("clicked on the div!")
159+
}
160+
161+
const handleKeyDown = (e: KeyboardEvent) => {
162+
if (event.code === 'Space' || event.code === 'Enter') {
163+
fn();
164+
}
165+
}
166+
167+
return (
168+
<div
169+
role="button"
170+
onClick={fn}
171+
onKeyDown={handleKeyDown}
172+
tabIndex="0" // [!code focus]
173+
>
174+
click!
175+
</div>
176+
)
177+
}
178+
```
179+
180+
**QUESTION**: Wasn't it eassier to use the following approach instead?
181+
182+
```tsx
183+
const MyComponent = () => {
184+
const fn = () => {
185+
console.log("clicked on the div!")
186+
}
187+
188+
return <button onClick={fn}>click!</div>
189+
}
190+
```
191+
192+
That’s it. No explicit button `role`, no `tabIndex`, no key handler (because buttons will fire from clicks with the keyboard, unlike `div`s)
193+
194+
:::
195+
196+
## Use `prefer-reduce-motion` for your animations!
197+
198+
::: danger Warning
199+
200+
An embedded example in this section has a scaling movement that may be problematic for some readers. Readers with [vestibular motion disorders](https://www.a11yproject.com/posts/understanding-vestibular-disorders/) may wish to enable the reduce motion feature on their device before viewing the animation.
201+
202+
:::
203+
204+
The `prefers-reduced-motion` CSS media feature is used to detect if a user has enabled a setting on their device to minimize the amount of non-essential motion. The setting is used to convey to the browser on the device that the user prefers an interface that removes, reduces, or replaces motion-based animations.
205+
206+
Such animations can trigger discomfort for those with vestibular motion disorders. Animations such as scaling or panning large objects can be vestibular motion triggers.
207+
208+
<iframe height="358" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/amir78729/embed/myJmQZR?default-tab=css%2Cresult&theme-id=dark" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
209+
See the Pen <a href="https://codepen.io/amir78729/pen/myJmQZR">
210+
Untitled</a> by Amirhossein Alibakhshi (<a href="https://codepen.io/amir78729">@amir78729</a>)
211+
on <a href="https://codepen.io">CodePen</a>.
212+
</iframe>
213+
214+
215+
## Accessible Media
216+
217+
Make note of any media in need of [captions](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Audio_and_video_delivery/Adding_captions_and_subtitles_to_HTML5_video), [transcripts](https://www.w3.org/WAI/media/av/transcripts/), and other alternative content.

focus-control.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ The idea is simple enough: provide a link at the top of the page that, when acti
6262
### Landmark Elements
6363

6464
Check [Semantic HTML: Landmark Elements](/semantic-html#landmark-elements)
65-
65+
<!--
6666
## Programmatically managing focus
6767
6868
Our React applications continuously modify the HTML DOM during runtime, sometimes leading to keyboard focus being lost or set to an unexpected element. In order to repair this, we need to programmatically nudge the keyboard focus in the right direction. For example, by resetting keyboard focus to a button that opened a modal window after that modal window is closed.
@@ -129,4 +129,4 @@ A great focus management example is the [react-aria-modal](https://github.com/d
129129
130130
::: warning
131131
While this is a very important accessibility feature, it is also a technique that should be used judiciously. Use it to repair the keyboard focus flow when it is disturbed, not to try and anticipate how users want to use applications.
132-
:::
132+
::: -->

0 commit comments

Comments
 (0)