Mt

← index

Loading contents...

What I've learned about accessibility in SPAs

SPA에서의 접근성에 대해 배운 것들

Over the past year or so, I've learned a lot about accessibility, mostly thanks to working on Pinafore, which is a Single Page App (SPA). In this post, I'd like to share some of the highlights of what I've learned, in the hope that it can help others who are trying to learn more about accessibility.

지난 일 년간, 단일 페이지 앱(Single Page App, 이하 SPA)인 Pinafore를 만들며 감사하게도 접근성에 대해 많은 것을 배울 수 있었습니다. 이 글을 통해 그간 배운 것들 중 중요한 것들을 공유하며, 접근성을 배우고자 하는 이들에게 도움이 되었으면 좋겠습니다.

One big advantage I've had in this area is the help of Marco Zehe, an accessibility expert who works at Mozilla and is blind himself. Marco has patiently coached me on a lot of these topics, and his comments on the Pinafore GitHub repo are a treasure trove of knowledge.

이 분야를 경험하며 시각장애인이며, 모질라에서 접근성 전문가로 일하고 있는 Marco Zehe에게 많은 도움을 받았습니다. Marco는 인내심을 가지고 여러 가지 주제에 대해 저를 지도해주었으며, 그가 Pinafore 깃헙 리포지터리에 남긴 글들은 지식의 보고라고 봐도 무방할 정도입니다.

So without further ado, let's dive in!

고민만 하지 말고, 일단 살펴봅시다!

Misconceptions

오해

One misconception I've observed in the web community is that JavaScript is somehow inherently anti-accessibility. This seems to stem from a time when screen readers did not support JavaScript particularly well, and so indeed the more JavaScript you used, the more likely things were to be inaccessible.

웹 커뮤니티에서 흔히 발견할 수 있는 오해 중 하나는 자바스크립트가 근본적으로 접근성에 해가 된다는 의견입니다. 이는 아마도 스크린 리더가 자바스크립트를 잘 지원하지 않을 때 생기고, 실제로 자바스크립트를 많이 쓰면 쓸수록 그런 경향이 있긴 합니다.

I've found, though, that most of the accessibility fixes I've made have actually involved writing more JavaScript, not less. So today, this rule is definitely more myth than fact. However, there are a few cases where it holds true:

하지만 대부분의 접근성 문제를 해결하는 과정에서 저는 실제로 더 많은 자바스크립트를 작성해야 했습니다. 그러니까 요즘은, 이 얘기는 사실이라기보다는 미신에 가깝습니다. 그럼에도 몇 가지 경우에는 여전히 맞는 말이기도 합니다.

divs and spans versus buttons and inputs

<div> & <span> vs <button> & <input>

Here's the best piece of accessibility advice for newbies: if something is a button, make it a <button>. If something is an input, make it an <input>. Don't try to reinvent everything from scratch using <div>s and <span>s. This may seem obvious to more seasoned web developers, but for those who are new to accessibility, it's worth reviewing why this is the case.

초보자들에게 접근성과 관련된 가장 유용한 내용을 알려드리겠습니다: 버튼 같은 것은 그냥 <button>으로 만드세요. 입력을 받아야 한다면 <input>으로 만들고요. <div><span>을 이용해서 모든 걸 바닥부터 새로 만들려고 하지 마세요. 노련한 웹 개발자들에겐 자명한 문제로 보이겠지만, 접근성을 처음 다뤄보는 분들은 왜 이렇게 해야 하는지 알아두면 좋겠네요.

First off, for anyone who doubts that this is a thing, there was a large open-source dependency of Pinafore (and of Mastodon) that had several thousand GitHub stars, tens of thousands of weekly downloads on npm, and was composed almost entirely of <div>s and <span>s. In other words: when something should have been a <button>, it was instead a <span> with a click listener. (I've since fixed most of these accessibility issues, but this was the state I found it in.)

이게 이렇게 말할 거리나 되나 싶은 분들에게 설명드리자면, Pinafore(그리고 Mastodon도 마찬가지로)이 의존하고 있는 수많은 오픈소스들 – 대부분 GitHub에서 수천 개 이상의 스타를 받았고, npm의 주간 다운로드가 수십만 회가 넘는 프로젝트들임에도 – 거의 전체가 <div><span>으로 이루어져 있었습니다. 즉, <button>이어야 했던 것들이 <span>click 리스너가 붙은 체로 있었다는 얘깁니다. (그 이후로 이런 접근성 이슈를 대부분 고쳤지만, 제가 발견한 당시에는 그랬었습니다.)

This is a real problem! People really do try to build entire interfaces out of <div>s and <span>s. Rather than chastise, though, let me analyze the problem and offer a solution.

이거 진짜 문제 아닐까요?! 사람들은 전체 인터페이스를 <div><span> 덩어리로 만들려 하고 있어요. 하지만 비난만 하지 않고, 원인을 찾아 해결책을 제안해보고자 합니다.

I believe the reason people are tempted to use <div>s and <span>s is that they have minimal user agent styles, i.e. there is less you have to override in CSS. However, resetting the style on a <button> is actually pretty easy:

<div><span>이 브라우저 기본 스타일(user agent style)이 가장 적어, CSS에서 재정의할 값이 별로 없는 점 때문에 널리 사용된다고 생각합니다. 하지만 <button>의 스타일을 초기화하는 것도 그리 어렵지 않습니다.

button {
  margin: 0;
  padding: 0;
  border: none;
  background: none;
}


button {
  margin: 0;
  padding: 0;
  border: none;
  background: none;
}

대부분의 경우에 이것만으로도 <button>의 스타일을 기본적으로 <div><span>과 같도록 초기화할 수 있었습니다. 몇몇 특별한 경우에 대해서는 this CSS Tricks article에서 확인해보세요.

어쨌든, <span>이나 <div>가 아닌 진짜 <button>을 사용해야 하는 근본적인 이유는 다음의 접근성 개선을 거저 얻을 수 있기 때문입니다.

99% of the time, I've found that this was all I needed to reset a <button> to have essentially the same style as a <div> or a <span>. For more advanced use cases, you can explore this CSS Tricks article.

In any case, the whole reason you want to use a real <button> over a <span> or a <div> is that you essentially get accessibility for free:

  • 마우스 대신 키보드의 Tab을 사용하는 사람에게, <button>에 포커스를 올바른 순서로 제공할 수 있습니다.
    • For keyboard users who Tab around instead of using a mouse, a <button> automatically gets the right focus in the right order.
    • When focused, you can press the Space bar on a <button> to press it.
    • Screen readers announce the <button> as a button.
    • Etc.
  • 포커스를 가졌을 때, <button> 위에서 Space를 눌러 선택할 수 있습니다.
  • 스크린 리더가 <button>을 버튼으로 알려줄 수 있습니다.
  • 등등.
  • 이 모든 걸 직접 JavaScript로 구현하는 것도 가능은 하지만, 어딘가에서 실수하거나 추가로 유지 보수해야 하는 코드가 늘어날 겁니다. 그러니까 기본으로 제공되는 의미 있는 HTML 요소를 쓰는 것이 좋겠습니다.

    SPA는 포커스와 스크롤 위치를 반드시 수동으로 다뤄야 한다

    You could build all this yourself in JavaScript, but you 'll probably mess something up, and you'll also have a bunch of extra code to maintain. So it's best just to use the native semantic HTML elements.

    “자바스크립트는 접근성에 반한다”는 격언이 지닌 진실된 핵심이 적용되는 경우가 있습니다: 바로 SPA 내비게이션이죠. SPA 상에서, 자바스크립트를 통해 페이지를 전환하는 것은 일반적입니다. 페이지 전체를 새로 불러오는 대신에 DOM을 조작하고 History API를 이용하는 식이죠. 이런 방식은 접근성 측면에서 몇 가지 문제를 가져옵니다.

    SPAs must manually handle focus and scroll position

    There is another case where the "JavaScript is anti-accessibility" mantra has a kernel of truth: SPA navigation. Within SPAs, it's common for JavaScript to handle navigation between pages, i.e. by modifying the DOM and History API rather than triggering a full page load. This causes several challenges for accessibility:

  • 포커스를 직접 관리해야 합니다.
    • You need to manage focus yourself.
    • You need to manage scroll position yourself.
  • 스크롤 위치를 직접 관리해야 합니다.
  • 제 타임라인에서 타임스탬프를 클릭해서 해당 포스트의 전체 타래를 보고자 하는 경우를 예로 들어봅시다.

    제가 쓴 포스트의 타임스탬프를 커서로 가리키고 있는 Pinafore 타임라인의 스크린샷

    For instance, let's say I'm in my timeline, and I want to click this timestamp to see the full thread of a post:

    링크를 클릭하고, 뒤로 가기 버튼을 누르면 제가 마지막으로 클릭했던 버튼에 포커스가 있어야 합니다(보라색 외곽선을 보세요).

    Screenshot of Pinafore timeline showing a post from me with an arrow pointing to the timestamp

    위와 같은 이미지이지만, 타임스탬프 주위로 포커스 외곽선이 있는 스크린샷

    When I click the link and then press the back button, focus should return to the element I last clicked (note the purple outline):

    전통적인 서버 측에서 렌더링되는 페이지에서는, 대부분의 브라우저 엔진[^1]이 이 기능을 공짜로 제공해줍니다. 아무것도 코딩할 필요가 없죠. 하지만 SPA에서는 일반적인 내비게이션 행동을 재정의한 상황이므로, 포커스를 스스로 다뤄야 합니다.

    Screenshot showing the same image as above, but with a focus outline around the timestamp

    이는 스크롤할 때도 적용되는데요, 특히 가상의 리스트(역주: 무한히 스크롤되는)에서 그렇습니다. 위의 스크린샷에서 보면, 제가 클릭했을 때와 정확하게 같은 위치로 스크롤되어 있음을 알 수 있습니다. 다시 말하지만, 이건 서버 측 렌더링을 할 경우에만 해당되는 얘기고, SPA에서는 여러분에게 그 책임이 있습니다.

    For classic server-rendered pages, most browser engines [^1] give you this functionality for free. You don't have to code anything. But in an SPA, since you're overriding the normal navigation behavior, you have to handle the focus yourself.

    통합 테스트가 쉬워집니다

    This also applies to scrolling, especially in a virtual list. In the above screenshot, note that I'm scrolled down to exactly the point in the page from before I clicked. Again, this is seamless when you're dealing with server-rendered pages, but for SPAs the responsibility is yours.

    제 앱의 접근성을 향상시키면서 놀라운 것을 하나 배웠는데, 이를 통해 테스트가 더 쉬워진다는 것이었습니다. 토글 버튼의 경우를 예로 들어볼게요.

    Easier integration testing

    토글 버튼은 눌렸거나, 눌리지 않은 두 가지 상태를 가질 수 있습니다. 아래 스크린샷에서 “boost”와 “favorite” 버튼(재귀 화살표와 별 모양)은 토글 버튼인데요, 포스트를 부스트 하거나 즐겨찾기 할 수 있는데, 둘 다 부스트 하지 않고/즐겨찾기 하지 않은 상태로 시작하기 때문입니다.

    One thing I was surprised to learn is that, by making my app more accessible, I also made it easier to test. Consider the case of toggle buttons.

    눌려 있는 별 버튼과 눌리지 않은 부스트 버튼을 가진 Pinafore에 게시된 Mastodon 포스트

    A toggle button is a button that can have two states: pressed or not pressed. For instance, in the screenshot below, the "boost" and "favorite" buttons (i.e. the circular arrow and the star) are toggle buttons, because it's possible to boost or favorite a post, and they start off in unboosted/unfavorited states.

    눌리고/눌리지 않은 상태를 시각적으로 표현하는 방법은 다양합니다. 저는 눌렸을 때 더 어두운 색을 사용하도록 하기도 했습니다. 하지만 스크린 리더 사용자들을 고려한다면, 일반적으로 다음과 같은 패턴을 선택할 겁니다.

    Screenshot of a Mastodon post in Pinafore with the favorite \(star\) button pressed and the boost button unpressed

    <button type="button" aria-pressed="false">
    
        

    Visually, there are plenty of styles you can use to signal the pressed/unpressed state - for instance, I've opted to make the colors darker when pressed. But for the benefit of screen reader users, you'll typically want to use a pattern like the following:

    Unpressed
    <button type="button" aria-pressed="false">
      Unpressed
    </button>
    <button type="button" aria-pressed="true">
      Pressed
    </button>
    
    
    
    </button> <button type="button" aria-pressed="true"> Pressed </button>

    놀랍게도, 이렇게 함으로써 통합 테스트를 작성하기가 더 쉬워졌습니다. (TestCafeCypress에서요) 앱을 다시 디자인할 때 달라질 수도 있는 클래스나 스타일에 의존하기보다는, 동일함이 보장되는 의미(semantic)를 속성으로 가지는 쪽에 의존하는 편이 낫지 않을까요?

    이런 패턴을 계속해서 발견했습니다. 제가 접근성을 개선하면 할수록, 테스트하기는 더 쉬워졌습니다. 예를 들면,

    • 피드 패턴을 사용하면서 aria-posinsetaria-setsize를 이용해 가상 리스트가 올바른 아이템의 수와 순서를 유지하는지 확인했습니다.

    Incidentally, this makes it easier to write integration tests (e.g. using TestCafe or Cypress). Why rely on classes and styles, which might change if you redesign your app, when you can instead rely on the semantic attributes, which are guaranteed to stay the same?

  • 문자열이 없는 버튼을 테스트할 때, 디자인이 달라지면 바뀔 수 있는 배경 이미지나 다른 것을 사용하는 대신 aria-label를 사용할 수 있었습니다.
  • I observed this pattern again and again: the more I improved accessibility, the easier things were to test. For instance:

  • 숨겨진 요소에 대해서 aria-hidden으로 확인했습니다.
    • When using the feed pattern, I could use aria-posinset and aria-setsize to confirm that the virtual list had the correct number of items and in the correct order.
    • For buttons without text, I could test the aria-label rather than the background image or something that might change if the design changed.
    • For hidden elements, I could use aria-hidden to identify them.
    • Etc.
  • 등등.
  • 그러므로, 접근성 향상을 테스팅의 전략으로 삼아보세요! 스크린 리더가 해석하기에 쉽다면, 아마 자동화된 테스트가 해석하기에도 쉬울 것입니다. 스크린 리더 사용자가 색상을 볼 수 없는 것처럼, 헤드리스 브라우저(headless browser) 테스트도 마찬가지일테니까요.

    포커스 관리의 미묘함

    Ian Forrest의 발표playing around with KaiOS를 보고, 제 앱에서 작은 변경사항 만으로도 키보드 접근성에 대한 향상을 가져올 수 있음을 알았습니다.

    발표에서 지적한 것과 같이, 마우스로 접근할 수 있는 모든 요소에 대해 키보드로 접근하게 만들 필요는 없습니다. 꼭 필요한 링크가 아니라면 tabindex 순서에서 제외시켜, 키보드 사용자가 Tab을 지나치게 많이 누르지 않도록 할 수도 있습니다.

    So make accessibility a part of your testing strategy! If something is easy for screen readers to interpret, then it'll probably be easier for your automated tests to interpret, too. After all, screen reader users might not be able to see colors, but neither can your headless browser tests!

    Pinafore의 경우에는 각 포스트에 사용자 프로필 페이지로 이동하는 두 개의 링크가 있습니다. 프로필 사진과 사용자 이름이죠.

    Subtleties with focus management

    Mastodon 포스트에 있는 사용자 아바타와 사용자 이름에 빨간 테두리가 쳐져 있는 스크린샷

    After watching this talk by Ian Forrest and playing around with KaiOS, I realized I could make some small changes to improve keyboard accessibility in my app.

    두 링크는 같은 페이지로 연결되어 있으므로 꼭 둘 다 있을 필요는 없습니다. 그래서 프로필 사진에 tabindex="-1"을 적용해서 키보드 사용자가 Tab을 한 번 덜 누를 수 있도록 했습니다. KaiOS 장치와 같이 작은 다이얼 패드를 가진 경우에 특히 좋을 겁니다!

    As pointed out in the talk, it's not necessarily the case that every mouse-accessible element also needs to be keyboard-accessible. If there are redundant links on the page, then you can skip them in the tabindex order, so a keyboard user won't have to press Tab so much.

    In the case of Pinafore, consider a post. There are two links that lead to the user's profile page - the profile picture and the user name:

    위 영상을 통해 프로필 사진과 타임스탬프는 탭 순서에서 제외되어 있음을 확인할 수 있습니다. 프로필 사진을 누르는 것은 사용자 이름을 누르는 것과 같고, 타임스탬프는 전체 포스트를 누르는 것과 같아 불필요하기 때문입니다. (운동 장애가 있는 사람에게는 문제가 될 수 있으므로 “전체 포스트 클릭하기”도 비활성화할 수 있습니다. 이 경우에는 타임스탬프가 탭 순서에 다시 포함됩니다.)

    Screenshot showing a Mastodon post with red rectangles around the user avatar and the user name

    tabindex="-1"을 가진 요소를 클릭한 다음 뒤로 돌아오면 포커스가 가능하다는 것이 이상하긴 하지만, 탭으로 이동 가능한 다른 요소들이 올바른 순서로 있는 한, 그 요소에서 탭으로 나온 뒤에는 제대로 동작해서 다행입니다.

    These two links lead to exactly the same page; they are strictly redundant. So I chose to add tabindex="-1" to the profile picture, giving keyboard users one less link to have to Tab through. Especially on a KaiOS device with a tiny d-pad, this is a nice feature!

    최종 보스: 자동 완성에 대한 접근성

    피드 패턴이나 이미지 캐러셀 등 (이전 포스트에서 설명했던) 몇몇 접근성 위젯을 바닥부터 만들어가며, 올바르게 구현하기 가장 어려운 요소 중 하나가 자동완성임을 알게 되었습니다.

    In the above video, note that the profile picture and timestamp are skipped in the tab order because they are redundant - clicking the profile picture does the same thing as clicking the user name, and clicking the timestamp does the same thing as clicking on the entire post. (Users can also disable the "click the entire post" feature, as it may be problematic for those with motor impairments. In that case, the timestamp is re-added to the tab order.)

    Interestingly, an element with tabindex="-1" can still become focused if you click it and then press the back button. But luckily, tabbing out of that element does the right thing as long as the other tabbable elements are in the proper order.

    처음에는 모든 상태 변경에 대해 – “현재 선택된 아이템은 세 가지 중 두 번째입니다”와 같이 — 말하는 aria-live="assertive" 속성을 가진 요소를 만드는 것에 상당히 의존하는 이 디자인을 따라 이런 위젯을 구현했었습니다. 한 땀 한 땀 수작업으로 해결하는 방법이라 결국 다양한 버그가 나올 수밖에 없었습니다.

    The final boss: accessible autocomplete

    몇 가지 패턴을 시험해본 후, 최종적으로 aria-activedescendant를 사용하는 표준 디자인을 선택했습니다. HTML은 대략 이렇게 생겼습니다.

    After implementing several accessible widgets from scratch, including the feed pattern and an image carousel (which I described in a previous post), I found that the single most complicated widget to implement correctly was autocompletion.

    <textarea
    
        

    id="the-textarea"

    Originally, I had implemented this widget by following this design, which relies largely on creating an element with aria-live="assertive" which explicitly speaks every change in the widget state (e.g. "the current selected item is number 2 of 3"). This is kind of a heavy-handed solution, though, and it led to several bugs.

    aria-describedby="the-description"

    After toying around with a few patterns, I eventually settled on a more standard design using aria-activedescendant. Roughly, the HTML looks like this:

    aria-owns="the-list"
    <textarea
      id="the-textarea"
      aria-describedby="the-description"
      aria-owns="the-list"
      aria-expanded="false"
      aria-autocomplete="both"
      aria-activedescendant="option-1">
    </textarea>
    <ul id="the-list" role="listbox">
      <li
        id="option-1"
        role="option"
        aria-label="First option (1 of 2)">
      </li>
      <li
        id="option-2"
        role="option"
        aria-label="Second option (2 of 2)">
      </li>
    </ul>
    <label for="the-textarea" class="sr-only">
      What's on your mind?
    </label>
    <span id="the-description" class="sr-only">
      When autocomplete results are available, press up or down
      arrows and enter to select.
    </span>
    
    
    
    aria-expanded="false" aria-autocomplete="both" aria-activedescendant="option-1"> </textarea> <ul id="the-list" role="listbox"> <li id="option-1" role="option" aria-label="First option (1 of 2)"> </li> <li id="option-2" role="option" aria-label="Second option (2 of 2)"> </li> </ul> <label for="the-textarea" class="sr-only"> What's on your mind? </label> <span id="the-description" class="sr-only"> When autocomplete results are available, press up or down arrows and enter to select. </span>

    이를 설명하기 위해 따로 글을 써야 할 수준이지만, 대략적으로 이렇게 동작한다고 볼 수 있습니다.

    • 설명과 라벨은 스크린 리더에서만 보이는 스타일을 사용해서 화면에서는 보이지 않게 합니다. 이를 통해 위아래 키를 눌러 엔터로 선택할 수 있음을 알려줍니다.
    • aria-expanded는 자동완성 결과 여부를 알려줍니다.
    • aria-activedescendant는 목록에서 어떤 옵션을 골랐는지 알려줍니다.
    • 옵션에 있는 aria-label을 통해 스크린 리더가 정보를 말하지 않을 경우, “두 개중 첫 번째”와 같이 명확하게 스크린 리더가 어떤 문자열을 포함해서 읽어야 하는지 제어할 수 있습니다.

    Explaining this pattern probably deserves a blog post in and of itself, but in broad strokes, what's happening is:

    • The description and label are offscreen, using styles which make it only visible to screen readers. The description explains that you can press up or down on the results and press enter to select.
    • aria-expanded indicates whether there are autocomplete results or not.
    • aria-activedescendant indicates which option in the list is selected.
    • aria-labels on the options allow me to control how it's spoken to a screen reader, and to explicitly include text like "1 of 2" in case the screen reader doesn't speak this information.

    광범위한 테스팅 끝에, 이게 덜하지도 더하지도 않는, 제가 할 수 있는 최선의 방법임을 알았습니다. 최신 버전의 파이어폭스의 NVDA 하에서 완벽하게 동작합니다. 비록 [사파리의 보이스오버(VoiceOver)와 크롬의 NVDA에서는 문제]((https://github.com/nolanlawson/pinafore/pull/1513#issue-320087960)가 있지만요. 이 방법이 aria-live="assertive" 핵에 의존하지 않는 표준에 기반판 방법이므로, 브라우저와 스크린 리더가 이 구현을 제대로 할 날을 기다립니다.

    수정: 크롬+NVDA와 사파리+보이스오버에서도 이 위젯이 동작하도록 했습니다. 변경사항은 이 댓글에서 확인해보세요.

    수동 및 자동화된 접근성 테스트

    웹 앱의 접근성을 개선하기 위한 좋은 팁을 알려주는 다양한 자동화 도구들이 있습니다. 저도 사용하는 Lighthouse(내부적으로 Axe를 사용하죠)라던가 크롬 접근성 도구, 그리고 파이어폭스 접근성 도구들이 있습니다. (도구들마다 조금씩 다른 결과를 제시하니, 저는 다양한 의견을 얻기 위해 여러 도구를 사용하는 편입니다!)

    파이어폭스 접근성 도구를 옆 패널에 열어둔 Pinafore 포스트의 스크린샷

    그런데, 특히 스크린 리더 접근성에 대해서는 스크린 리더로는 실제 브라우저에서의 테스트를 대체할 수 없다는 것을 알았습니다. 스크린 리더 사용자가 가질만한 경험을 정확하게 제공할 뿐 아니라 어떤 디자인 패턴이 음성 내비게이션과 잘 동작하며 어떤 것은 그렇지 않을 지에 대한 공감을 이끌어내는데 도움을 줍니다. 스크린 리더는 버그가 있어 종종 다른 행동을 하기도 하는데, 이런 것들은 접근성 감사 도구에서는 확인할 수 없는 부분입니다.

    After extensive testing, this was more-or-less the best solution I could come up with. It works perfectly in NVDA on the latest version of Firefox, although sadly it has some minor issues in VoiceOver on Safari and NVDA on Chrome. However, since this is the standards-based solution (and doesn't rely on aria-live="assertive" hacks), my hope is that browsers and screen readers will catch up with this implementation.

    갓 시작한 분이라면, Rob Dodson의 “A11ycasts” 시리즈VoiceOver for macOSNVDA for Windows에 대한 튜토리얼 시청을 추천합니다. (NVDA는 파이어폭스에, 보이스오버는 사파리에 더 최적화돼있음을 유의하세요. 다른 조합으로도 사용할 수는 있지만, 이 조합이 실제로 가장 많이 사용되고 있습니다.)

    Update: I managed to get this widget working in Chrome+NVDA and Safari+VoiceOver. The fixes needed are described in this comment.

    개인적으로 개발자의 입장에서 사용하기 가장 편리했던 도구는 말하는 내용을 도움 문자열(assistive text)로 보여주는 보이스오버였습니다.

    Manual and automated accessibility testing

    macOS의 사파리에서 무엇을 말했는지 화면의 하단에 문자열로 보여주는 보이스오버의 스크린샷

    There are a lot of automated tools that can give you good tips on improving accessibility in your web app. Some of the ones I've used include Lighthouse (which uses Axe under the hood), the Chrome accessibility tools, and the Firefox accessibility tools. (These tools can give you slightly different results, so I like to use multiple so that I can get second opinions!)

    NVDA도 이렇게 동작하도록 할 수 있지만, 설정에 들어가 “Speech Viewer” 옵션을 활성화해야 합니다. 만약 NVDA 개발자가 이 글을 보고 있다면 켜있는 것을 기본값으로 해주었으면 좋겠어요!

    Screenshot of a post in Pinafore with the Firefox accessibility tools open in a side panel

    Screenshot of Speech Viewer in NVDA on Firefox showing lines of text representing what's spoken

    However, I've found that, especially for screen reader accessibility, there is no substitute for testing in an actual browser with an actual screen reader. It gives you the exact experience that a screen reader user would have, and it helps build empathy for what kinds of design patterns work well for voice navigation and which ones don't. Also, sometimes screen readers have bugs or slightly differing behavior, and these are things that accessibility auditing tools can't tell you.

    스크린 리더를 테스트하는 것과 비슷하게, 여러분의 앱에서 Tab을 눌러보며 키보드로 얼마나 편하게 사용할 수 있는지 알아보는 것도 좋습니다. 포커스가 의도치 않게 달라졌나요? 원하는 곳에 가기 위해서 Tab을 불필요하게 많이 눌러야 하나요? 사용하기에 편리하도록 추가하고 싶은 키보드 단축키가 있나요?

    If you're just getting started, I would recommend watching Rob Dodson's "A11ycasts" series, especially the tutorials on VoiceOver for macOS and NVDA for Windows. (Note that NVDA is usually paired with Firefox and VoiceOver is optimized for Safari. So although you can use either one with other browsers, those are the pairings that tend to be most representative of real-world usage.)

    접근성에 대한 많은 것들에 반드시 해야 할(hard-and-fast) 규칙이란 없습니다. 디자인이나 사용성에 대한 얘기와 마찬가지로, 사용자가 어떤 경험을 하고 있고 어디를 최적화해야 하는지 실제로 직접 경험해야 합니다.

    Personally I find VoiceOver to be the easiest to use from a developer's point of view, mostly because it has a visual display of the assistive text while it's being spoken.

    결론

    Screenshot of VoiceOver in Safari on macOS showing text at the bottom of the screen representing what's spoken

    접근성은 도전적이지만, 궁극적으로 노력을 들일 만한 일입니다. 다양한 방법으로 접근성을 향상시키기 위해 노력하다 보면 KaiOS의 방향키 내비게이션과 같은 예상치 못했던 개선과 더 나은 통합 테스트로 나아갈 수 있습니다.

    NVDA can also be configured to do this, but you have to know to go into the settings and enable the "Speech Viewer" option. I would definitely recommend turning this on if you're using NVDA for development!

    그리고 가장 큰 보람은, 제가 했던 작업으로 행복한 사용자들에게서 옵니다. Marco에게 이런 얘기를 들었을 때 더할 나위 없이 기뻤습니다.

    Screenshot of Speech Viewer in NVDA on Firefox showing lines of text representing what's spoken

    Similar to testing screen readers, it's also a good idea to try Tabing around your app to see how comfortable it is with a keyboard. Does the focus change unexpectedly? Do you have to do a lot of unnecessary Tabing to get where you want? Are there any handy keyboard shortcuts you'd like to add?

    “Mastodon을 사용하기 위한 가장 접근성 높은 방법은 바로 Pinafore입니다. 저는 데스크톱뿐만 아니라 아이폰과 아이패드의 iOS에서도 같이 사용하고 있습니다. 개발 초기부터 접근성을 중요하게 생각하고, 계속해서 새로운 기능을 접근성 있게 만들어주신 것에 감사드립니다.”

    For a lot of things in accessibility, there are no hard-and-fast rules. Like design or usability in general, sometimes you just have to experience what your users are experiencing and see where you can optimize.

    Conclusion

    Accessibility can be challenging, but ultimately it's worth the effort. Working on accessibility has improved the overall usability of my app in a number of ways, leading to unforeseen benefits such as KaiOS arrow key navigation and better integration tests.

    Marco, 당신의 도움이 진심으로 감사드립니다! 여러분에게도 이 글이 접근성에 대해 한 걸음 나아갈 수 있는 도움이 되기를 빕니다.

    The greatest satisfaction, though, comes from users who are happy with the work I've done. I was beyond pleased when Marco had this to say:

    이 글에 피드백을 주신 Sorin Davidoi, Thomas Wilburn, 그리고 Marco Zehe에게 감사합니다.

    [^1]: 이 글을 쓰는 와중에, 서버 측 렌더링을 하는 경우에 뒤로 가기 버튼을 누르면 이전에 클릭했던 포커스가 복원된다는 것을 알고 놀랬습니다. 크롬을 제외한 파이어폭스, 사파리, 엣지(EdgeHTML)에서 동작합니다. a webcompat.com bug에서 브라우저 차이점에 대한 설명을 발견하고, 크롬에 버그를 제보했습니다.

    "Pinafore is for now by far the most accessible way to use Mastodon. I use it on desktop as well as iOS, both iPhone & iPad, too. So thank you again for getting accessibility in right from the start and making sure the new features you add are also accessible."

    Translators

    Latest update at 2019-11-25T02:51:05Z

    In alphabetical order