527 lines
18 KiB
HTML
527 lines
18 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
|
|
<title>A11y First, and Everyone Wins</title>
|
|
|
|
<link rel="stylesheet" href="css/reset.css">
|
|
<link rel="stylesheet" href="css/reveal.css">
|
|
<link rel="stylesheet" href="css/theme/black.css">
|
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
|
|
|
<!-- Theme used for syntax highlighting of code -->
|
|
<link rel="stylesheet" href="lib/css/monokai.css">
|
|
|
|
<style>
|
|
.reveal pre code {
|
|
background: black;
|
|
padding-top: 1em;
|
|
padding-bottom: 1em;
|
|
}
|
|
</style>
|
|
|
|
<!-- Printing and PDF exports -->
|
|
<script>
|
|
var link = document.createElement( 'link' );
|
|
link.rel = 'stylesheet';
|
|
link.type = 'text/css';
|
|
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
|
|
document.getElementsByTagName( 'head' )[0].appendChild( link );
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div class="reveal">
|
|
<div class="slides">
|
|
<section class="text-left">
|
|
<h1 class="m-0 leading-none">
|
|
A11y First
|
|
<div>
|
|
<small>
|
|
<span class="text-gray-600">&</span> Everyone Wins
|
|
</small>
|
|
</div>
|
|
</h1>
|
|
<h2>
|
|
<span class="text-yellow-400 text-4xl">
|
|
More Composable, Intuitive & Testable
|
|
</span>
|
|
</h2>
|
|
<small><a href="https://www.wroten.me/">Ava Gaiety Wroten</a></small>
|
|
</section>
|
|
|
|
<section>
|
|
<p>
|
|
Ava Wroten
|
|
<br>
|
|
<small>She/Her</small>
|
|
</p>
|
|
<img src="./photo.png" alt="" role="presentation" class="w-40 inline-block" />
|
|
<p>
|
|
Software Engineer at <a href="https://allovue.com/"><img src="./allovue.svg" alt="Allovue" class="inline-block pl-4 shadow-none" style="border: none; background: transparent; height: 1.25em;" /></a>
|
|
</p>
|
|
<p class="text-sm">
|
|
On Gitlab @gaiety
|
|
<span class="inline-block mx-4">
|
|
<img src="./avatar.png" alt="" role="presentation" class="inline-block w-20" />
|
|
</span>
|
|
Or Github @sharpshark28
|
|
</p>
|
|
|
|
<aside class="notes">
|
|
...where we build software for K-12 School Districts. You can also find me doing open source work on Gitlab or Github.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>
|
|
We'll Discuss
|
|
</h2>
|
|
|
|
<p>A project feature saved by A11y</p>
|
|
|
|
<ul>
|
|
<li class="fragment">Adding Functionality w/ Composable Components</li>
|
|
<li class="fragment">Equitable & Discoverable UX</li>
|
|
<li class="fragment">Automation Testing</li>
|
|
</ul>
|
|
|
|
<aside class="notes">
|
|
This is a success story. We'll discuss these terms as we go.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>
|
|
Feature:
|
|
</h2>
|
|
<h3>Item Reordering</h3>
|
|
<p class="fragment">Mouse Drag & Drop</p>
|
|
<p class="fragment text-gray-600">Do we support Touch?</p>
|
|
<p class="fragment text-yellow-400">What about A11y?</p>
|
|
|
|
<aside class="notes">
|
|
Prior to Allovue, I was assigned a ticket with a simple feature request and no design. Sounds simple enough. Perhaps, too "simple".
|
|
</aside>
|
|
</section>
|
|
|
|
<section class="font-mono">
|
|
<p><span class="text-yellow-400">A11Y</span></p>
|
|
<p class="fragment"><span class="text-yellow-400">A</span>....11.....<span class="text-yellow-400">Y</span></p>
|
|
<p class="fragment"><span class="text-yellow-400">A</span>ccessibilit<span class="text-yellow-400">y</span></p>
|
|
|
|
<aside class="notes">
|
|
We've seen "A11y" on these slides a lot. Expanded out A11y means Accessibility because it has 18 characters. Acronym.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<p>Equity</p>
|
|
|
|
<aside class="notes">
|
|
I was tasked with delivering a feature that not everyone could use. This felt unfair locking out opportunities to use parts of the app from some users. At Allovue we talk Equity a lot.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<div>
|
|
<cite class="text-sm text-gray-400">
|
|
World Health Organization
|
|
</cite>
|
|
</div>
|
|
<h2>
|
|
<span class="text-yellow-400">15%</span> of the World
|
|
<div>
|
|
<small>
|
|
lives with some form of disability
|
|
</small>
|
|
</div>
|
|
</h2>
|
|
|
|
<aside class="notes">
|
|
A great many talks have been giving trying to sell A11y. I merely wish to set the stage for my motivations here.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Degrees of Disabilities</h2>
|
|
<p>Limited mobility, muscle slowness, tremors, low vision, color blindness, partial hearing loss, etc</p>
|
|
|
|
<footer>
|
|
<p class="text-sm"><a href="https://accessibility.iu.edu/understanding-accessibility/types-of-disabilities.html">Indiana University on Types of Disabilities</a></p>
|
|
</footer>
|
|
|
|
<aside class="notes">
|
|
There are other forms of disability as well as varying degrees of disability.
|
|
People are human, we have differing levels of ability.
|
|
Often disabilities are invisible.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<p>Web assistive technologies commonly help with...</p>
|
|
<ul>
|
|
<li>Vision</li>
|
|
<li>Hearing</li>
|
|
<li>Movement</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section>
|
|
<p>👩🏽🔬 👨🏾🎨 👩🏻🔧 👨🏼✈️</p>
|
|
<p>I build software for humans</p>
|
|
|
|
<aside class="notes">
|
|
I wouldn't be happy with myself if I shipped software that was completely unusable to some users in a shortsighted fashion.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<p>So I did some research...</p>
|
|
</section>
|
|
|
|
<section data-background-iframe="https://www.w3.org/TR/wai-aria-practices-1.1/examples/listbox/listbox-rearrangeable.html" data-background-interactive>
|
|
<p class="bg-black float-right p-12 rounded-full shadow-lg text-xl">W3C Pattern Rearrangable Listbox</p>
|
|
|
|
<aside class="notes">
|
|
World-Wide-Web Consortium. Rearrangable Listbox pattern. This UI felt intuitive because it used existing UI elements and keyboard shortcuts I was familiar with. INTERACT WITH FORM.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<p><a href="https://emberobserver.com/addons/ember-sortable"><img src="./sortable.png" alt="ember-sortable on ember observer" /></a></p>
|
|
|
|
<aside class="notes">
|
|
Ember Observer ranks and describes Ember Addons. #26 of top 100 addons. Score of 10. Top 10% in terms of downloads. Heavily used solid addon.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2><a href="https://github.com/adopted-ember-addons/ember-sortable">ember-sortable</a></h2>
|
|
|
|
<p>✔️ Mouse Drag & Drop</p>
|
|
<p>✔️ Touch Drag & Drop</p>
|
|
<p>✔️ High Ember Observer Ranking</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Component Structure</h2>
|
|
|
|
<div class="flex">
|
|
<div data-markdown class="w-1/2">
|
|
ember-sortable
|
|
|
|
```html.hbs
|
|
<SortableGroup>
|
|
|
|
<SortableItem>
|
|
```
|
|
</div>
|
|
|
|
<div data-markdown class="w-1/2 fragment">
|
|
Custom Wrappers
|
|
|
|
```html.hbs
|
|
<SortableGroupAccessible>
|
|
|
|
<SortableItemAccessible>
|
|
```
|
|
</div>
|
|
</div>
|
|
|
|
<aside class="notes">
|
|
Ember Sortable offers two composable components. I introduced two more so as not to have to Reopen or Fork the addon.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Composed Components</h2>
|
|
|
|
<p class="text-xl text-left text-gray-600">
|
|
index.hbs
|
|
</p>
|
|
|
|
<div data-markdown>
|
|
```html.hbs
|
|
<SortableGroupAccessible>
|
|
<SortableGroup>
|
|
{{#each this.items as | item |}}
|
|
<SortableItem>
|
|
<MyItemComponent />
|
|
</SortableItem>
|
|
{{/each}}
|
|
</SortableGroup>
|
|
</SortableGroupAccessible>
|
|
```
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<p>Heres the final result...</p>
|
|
</section>
|
|
|
|
<section data-background-iframe="http://localhost:4200/52958" data-background-interactive>
|
|
<p class="bg-black float-right p-12 rounded-full shadow-lg text-xl" style="margin-top: 400px;">
|
|
<a href="https://gitlab.com/gaiety/sortable-recipes">https://gitlab.com/gaiety/sortable-recipes</a>
|
|
</p>
|
|
|
|
<aside class="notes">
|
|
Demo app. INTERACT WITH MOUSE. INTERACT WITH KEYBOARD. SHOW FOCUS AND ENTER TO ACTIVATE LINKS.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Implementation Details</h2>
|
|
<p><a href="https://gitlab.com/gaiety/sortable-recipes">https://gitlab.com/gaiety/sortable-recipes</a></p>
|
|
|
|
<aside class="notes">
|
|
Link is in #ember-conf on Discord.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Splattributes</h2>
|
|
|
|
<p class="text-xl text-left text-gray-600">
|
|
index.hbs
|
|
</p>
|
|
|
|
<pre><code class="hljs html.hbs" data-line-numbers="2-3" data-trim>
|
|
<SortableGroupAccessible
|
|
tabindex="0"
|
|
class="border focus:border-teal-400"
|
|
> Foo </SortableGroupAccessible>
|
|
</code></pre>
|
|
|
|
<div class="fragment">
|
|
<p class="text-xl text-left text-gray-600">
|
|
sortable-group-accessible.hbs
|
|
</p>
|
|
|
|
<pre><code class="hljs html.hbs" data-line-numbers="1" data-trim>
|
|
<div ...attributes>
|
|
{{yield}}
|
|
</div>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<div class="fragment">
|
|
<p class="text-xl text-left text-gray-600">
|
|
rendered html
|
|
</p>
|
|
|
|
<pre><code class="hljs html.hbs" data-line-numbers="1" data-trim>
|
|
<div tabindex="0" class="border focus:border-teal-400"
|
|
> Foo </div>
|
|
</code></pre>
|
|
</div>
|
|
|
|
<aside class="notes">
|
|
Useful with glimmer components that dont have a default div wrapper. Useful with composability. Moves implementation details to parent.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Events & Modifiers</h2>
|
|
|
|
<p class="text-xl text-left text-gray-600">
|
|
sortable-group-accessible.hbs
|
|
</p>
|
|
|
|
<pre><code class="hljs html.hbs" data-line-numbers="2,4" data-trim>
|
|
<div
|
|
{{on 'focus' this.handleFocus}}
|
|
{{on 'blur' this.handleBlur}}
|
|
{{key-up this.handleArrowUp key='ArrowUp'}}
|
|
{{key-up this.handleArrowDown key='ArrowDown'}}
|
|
</code></pre>
|
|
|
|
<div class="fragment">
|
|
<p class="text-xl text-left text-gray-600">
|
|
modifiers/key-up.js
|
|
</p>
|
|
|
|
<pre><code class="hljs js" data-line-numbers="6-7" data-trim>
|
|
import { modifier } from 'ember-modifier';
|
|
const listener = (evt) => {
|
|
if (!desiredKey || desiredKey === evt.key) handler(evt);
|
|
}
|
|
export default modifier(function keyUp(element, [handler], { key: desiredKey }) {
|
|
element.addEventListener('keyup', listener);
|
|
return () => { element.removeEventListener('keyup', listener); }
|
|
});
|
|
</code></pre>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Testing Details</h2>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>@ember/test-helpers</h2>
|
|
|
|
<ul>
|
|
<li class="fragment">render</li>
|
|
<li class="fragment">click</li>
|
|
<li class="fragment">triggerEvent</li>
|
|
<li class="fragment">triggerKeyEvent</li>
|
|
</ul>
|
|
|
|
<aside class="notes">
|
|
Automatically imported for `render` in Rendering tests. Commonly people find `click`. Less common and essential to this story is `triggerEvent` and `TriggerKeyEvent`.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Modifier Rendering Tests</h2>
|
|
|
|
<p class="text-xl text-left text-gray-600">
|
|
modifiers/key-up-test.js
|
|
</p>
|
|
|
|
<pre><code class="hljs js" data-line-numbers="2-4,7,9-10" data-trim>
|
|
test('it can listen for specific key up events', async function(assert) {
|
|
this.keyUp = ({ key }) => {
|
|
assert.equal(key, 'Enter');
|
|
assert.step('key up');
|
|
};
|
|
await render(hbs`
|
|
<div {{key-up this.keyUp}} data-test-id='keyup'></div>`);
|
|
let selector = '[data-test-id=keyup]';
|
|
await triggerKeyEvent(selector, 'keyup', 'Enter');
|
|
assert.verifySteps(['key up']);
|
|
});
|
|
</code></pre>
|
|
|
|
<aside class="notes">
|
|
Start at `render` with modifier on a generic div. Async `triggerKeyEvent`. `step` and `verifySteps` for async testing.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Application Testing Reordering</h2>
|
|
|
|
<p class="text-xl text-left text-gray-600">
|
|
index-test.js
|
|
</p>
|
|
|
|
<pre><code class="hljs js" data-trim>
|
|
assert.dom(findAll('[data-test-id=item]')[0]).hasText('Item 1');
|
|
assert.dom(findAll('[data-test-id=item]')[1]).hasText('Item 2');
|
|
|
|
await triggerEvent('[data-test-id=group]', 'focus');
|
|
await click('[data-test-id=sort-down]');
|
|
|
|
assert.dom(findAll('[data-test-id=item]')[0]).hasText('Item 2');
|
|
assert.dom(findAll('[data-test-id=item]')[1]).hasText('Item 1');
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
🎉 A11y allowed us to test reordering! 🎉
|
|
|
|
<aside class="notes">
|
|
Wins on next slide. Be excited here. Mention quick feedback loop for green tests, refactoring, design changes.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Team Wins</h2>
|
|
|
|
<ul>
|
|
<li>
|
|
Quick Feedback Loop
|
|
<ul>
|
|
<li class="fragment"><span class="text-gray-600">...with </span> development</li>
|
|
<li class="fragment"><span class="text-gray-600">...with </span> design</li>
|
|
<li class="fragment"><span class="text-gray-600">...with </span> project managers</li>
|
|
</ul>
|
|
</li>
|
|
<li class="fragment">Sign off for UX</li>
|
|
<li class="fragment">Less stress on QA</li>
|
|
<li class="fragment">Less scope creep</li>
|
|
</ul>
|
|
|
|
<aside class="notes">
|
|
TDD, felt safe knowing that functionality would not break as we fine tuned functionality and design. Paired with design on various states. QA reassured we had automation tests. Less having to go back later to implement A11y later as an afterthought.
|
|
</aside>
|
|
</section>
|
|
|
|
<section data-background-iframe="http://localhost:4201/52958" data-background-interactive>
|
|
<p class="bg-black float-right p-12 rounded-full shadow-lg">v2.x.x ember-sortable</p>
|
|
|
|
<aside class="notes">
|
|
Since this feature shipped there's been a release of ember-sortable v2 that ships with A11y built in. INTERACT WITH IT.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Where do we go from here?</h2>
|
|
|
|
<ul>
|
|
<li class="fragment">A11y Screen Announcements</li>
|
|
<li class="fragment"><a href="https://github.com/adopted-ember-addons/ember-sortable/pull/345">ember-sortable v2.2.x modifiers</a></li>
|
|
<li class="fragment"><a href="https://github.com/ember-a11y/ember-a11y-testing">ember-a11y-testing</a></li>
|
|
<li class="fragment"><a href="https://github.com/ember-template-lint/ember-cli-template-lint">ember-cli-template-lint</a></li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>
|
|
What We Discussed
|
|
</h2>
|
|
<ul>
|
|
<li>Adding Functionality w/ Composable Components</li>
|
|
<li class="fragment">Equitable & Discoverable UX</li>
|
|
<li class="fragment">Automation Testing A11y</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section data-background-color="#5677e4">
|
|
<p><a href="https://allovue.com/about/careers"><img src="./allovue.svg" alt="Allovue" class="inline-block" style="border: none; background: none; height: 2em; box-shadow: none;" /></a></p>
|
|
<p><a href="https://allovue.com/about/careers" style="color: white">We are hiring!</a></p>
|
|
|
|
<aside class="notes">
|
|
K-12 School Districts. Equitable. Shape Up.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Ava's Challenge to You</h2>
|
|
<p class="fragment">Hire someone different than you.</p>
|
|
<p class="fragment">Make A11y a priority at your next design meeting!</p>
|
|
|
|
<aside class="notes">
|
|
Hire someone different than you: Race, Religion, Orientation, Gender, megablocks VS Legos.
|
|
And, finally, make A11y a priority. You may be surprised how many people thank you for it.
|
|
</aside>
|
|
</section>
|
|
|
|
<section>
|
|
<p>Thank you</p>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="js/reveal.js"></script>
|
|
|
|
<script>
|
|
// More info about config & dependencies:
|
|
// - https://github.com/hakimel/reveal.js#configuration
|
|
// - https://github.com/hakimel/reveal.js#dependencies
|
|
Reveal.initialize({
|
|
hash: true,
|
|
progress: false,
|
|
transition: 'fade',
|
|
dependencies: [
|
|
{ src: 'plugin/markdown/marked.js' },
|
|
{ src: 'plugin/markdown/markdown.js' },
|
|
{ src: 'plugin/highlight/highlight.js' },
|
|
{ src: 'plugin/notes/notes.js', async: true }
|
|
]
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|