Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions fundamentals/a11y/eslint/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,27 +167,29 @@ export default [
인터랙티브 요소가 아닌 (`<div>`, `<span>` 등)에 클릭 이벤트 핸들러를 추가할 때는 반드시 `role` 속성 등으로 상호작용 요소임을 명시해야 해요.

::: details 인터랙티브 요소 목록
| 요소 | 조건 |
|------|------|
| `<a>` | - |
| `<audio>` | controls 속성이 있는 경우 |
| `<button>` | - |
| `<details>` | - |
| `<embed>` | - |
| `<iframe>` | - |
| `<img>` | usemap 속성이 있는 경우 |
| `<input>` | type 속성이 Hidden state가 아닌 경우 |
| `<keygen>` | - |
| `<label>` | - |
| `<menu>` | type 속성이 toolbar state인 경우 |
| `<object>` | usemap 속성이 있는 경우 |
| `<select>` | - |
| `<textarea>` | - |
| `<video>` | controls 속성이 있는 경우 |

| 요소 | 조건 |
| ------------ | ------------------------------------ |
| `<a>` | - |
| `<audio>` | controls 속성이 있는 경우 |
| `<button>` | - |
| `<details>` | - |
| `<embed>` | - |
| `<iframe>` | - |
| `<img>` | usemap 속성이 있는 경우 |
| `<input>` | type 속성이 Hidden state가 아닌 경우 |
| `<keygen>` | - |
| `<label>` | - |
| `<menu>` | type 속성이 toolbar state인 경우 |
| `<object>` | usemap 속성이 있는 경우 |
| `<select>` | - |
| `<textarea>` | - |
| `<video>` | controls 속성이 있는 경우 |

:::

::: info 왜 `role` 속성이 없는 비상호작용 요소에 클릭 이벤트 핸들러를 추가할 수 없나요?
비상호작용 요소에 클릭 이벤트 핸들러를 추가하면, 스크린 리더 등 보조기기가 해당 요소를 인식하지 못해 스크린 리더, 키보드 사용자 등에게 혼란을 줄 수 있어요.
비상호작용 요소에 클릭 이벤트 핸들러를 추가하면, 스크린 리더 등 보조 기기가 해당 요소를 인식하지 못해 스크린 리더, 키보드 사용자 등에게 혼란을 줄 수 있어요.
:::

**❌ 잘못된 예시**
Expand Down
4 changes: 2 additions & 2 deletions fundamentals/a11y/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
## 이런 분들께 추천해요

- 🎯 무심코 짠 코드가 누군가에겐 큰 장벽이 될 수 있다는 사실을 깨달은 개발자
- 🔍 '접근성을 고려해야 해요'라는 말은 많이 들었지만, 어디서부터 시작 해야 할지 감이 안 오는 개발자
- 👀 '스크린 리더', '키보드 탐색', 'ARIA' 등 용어는 들어봤지만 실무에 적용해본 적 없는 개발자
- 🔍 '접근성을 고려해야 해요'라는 말은 많이 들었지만, 어디서부터 시작해야 할지 감이 안 오는 개발자
- 👀 '스크린 리더', '키보드 탐색', 'ARIA' 등 용어는 들어봤지만 실무에 적용해 본 적 없는 개발자
- 🧩 HTML 구조 문제로 인해 자동화 테스트가 실패하거나 외부 라이브러리 연동이 안 되는 경험이 있는 개발자
4 changes: 2 additions & 2 deletions fundamentals/a11y/playground.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import ScreenReaderExperience from './components/ScreenReaderExperience.vue';

시각장애인이 모바일을 어떻게 사용하는지 직접 체험해 보세요. 아래 "시작하기" 버튼을 누르면 스크린리더 사용자처럼 화면을 보지 않고 소리로만 토스의 송금 서비스를 경험할 수 있어요.

PC에서는 마우스 클릭과 드래그로 터치 동작을 대신할 수 있어요. 어둠 뿐인 화면이 처음에는 낯설 수 있지만, 소리에 귀 기울이며 천천히 따라해 보세요.
PC에서는 마우스 클릭과 드래그로 터치 동작을 대신할 수 있어요. 어둠뿐인 화면이 처음에는 낯설 수 있지만, 소리에 귀 기울이며 천천히 따라 해 보세요.

<ScreenReaderExperience
url="https://service.toss.im/accessibility/screen-reader-experience"
Expand All @@ -37,7 +37,7 @@ PC에서는 마우스 클릭과 드래그로 터치 동작을 대신할 수 있
아래 순서대로 이벤트에 참여하고 상품을 받아보세요! 30일 뒤인 9월 10일에 추첨을 통해 상품을 드립니다.

1. 체험하기 버튼을 눌러 스크린리더 체험을 시작해요.
2. 송금하기 미션을 성공하면, 체험완료 화면을 스크린샷으로 찍어요.
2. 송금하기 미션을 성공하면, 체험 완료 화면을 스크린샷으로 찍어요.
3. 찍은 스크린샷을 이 페이지의 댓글로 올려주세요.

🎁 당첨 안내는 GitHub에 등록된 이메일을 통해 개별로 안내를 보내드려요.
Expand Down
40 changes: 21 additions & 19 deletions fundamentals/a11y/semantic/required-label.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@
인터랙티브 요소(입력 필드, 버튼, 선택 상자 등)에는 반드시 사용자에게 그 목적을 명확히 알려주는 이름이 필요해요. 이름이 없거나 불명확한 요소는 스크린 리더 사용자나 음성 지원 사용자에게 큰 불편을 줄 수 있어요.

::: details 인터랙티브 요소 목록
| 요소 | 조건 |
|------|------|
| `<a>` | - |
| `<audio>` | `controls` 속성이 있는 경우 |
| `<button>` | - |
| `<details>` | - |
| `<embed>` | - |
| `<iframe>` | - |
| `<img>` | `usemap` 속성이 있는 경우 |
| `<input>` | `type` 속성이 "hidden" 이 아닌 경우 |
| `<keygen>` | - |
| `<label>` | - |
| `<menu>` | `type` 속성이 toolbar state인 경우 |
| `<object>` | `usemap` 속성이 있는 경우 |
| `<select>` | - |
| `<textarea>` | - |
| `<video>` | `controls` 속성이 있는 경우 |

| 요소 | 조건 |
| ------------ | ----------------------------------- |
| `<a>` | - |
| `<audio>` | `controls` 속성이 있는 경우 |
| `<button>` | - |
| `<details>` | - |
| `<embed>` | - |
| `<iframe>` | - |
| `<img>` | `usemap` 속성이 있는 경우 |
| `<input>` | `type` 속성이 "hidden" 이 아닌 경우 |
| `<keygen>` | - |
| `<label>` | - |
| `<menu>` | `type` 속성이 toolbar state인 경우 |
| `<object>` | `usemap` 속성이 있는 경우 |
| `<select>` | - |
| `<textarea>` | - |
| `<video>` | `controls` 속성이 있는 경우 |

:::

인터랙티브 요소에 이름을 붙이는 방법은 여러가지가 있는데요. 스크린 리더는 다음 순서대로 이름을 읽습니다:
인터랙티브 요소에 이름을 붙이는 방법은 여러 가지가 있는데요. 스크린 리더는 다음 순서대로 이름을 읽습니다:

1. `aria-labelledby` - 스크린 리더가 가장 먼저 읽는 속성
2. `aria-label` - `aria-labelledby`가 없을 경우 읽는 속성
Expand Down Expand Up @@ -62,7 +64,7 @@ HTML에서 가장 좋은 방법은 `<label>` 요소를 사용해 입력 필드
<label for="user-name">이름</label> <input id="user-name" type="text" />
```

레이블의 장점은 여러가지가 있어요.
레이블의 장점은 여러 가지가 있어요.

먼저, 스크린 리더 사용자에게 입력 필드의 목적을 분명히 알려줘요. 또 레이블을 클릭해도 연결된 입력 필드에 포커스가 가서, 터치 인터페이스에서 특히 유용해요. 필드에 입력 중에도 항상 표시되어 사용자가 필드 목적을 기억할 수 있고요. 모든 사용자에게 가장 명확한 경험을 제공하는 방식이죠.

Expand Down
4 changes: 2 additions & 2 deletions fundamentals/a11y/structure/button-inside-button.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

다음과 같이 `<a>` 태그 안에 `<button>` 을 출력하는 컴포넌트를 넣는 것은 잘못된 방법이에요. HTML에서는 상호작용하는 요소 안에 또 다른 상호작용 요소를 넣는 걸 허용하지 않기 때문이에요[^1]. 이런 구조에서는 접근성에 문제가 생기고, 브라우저에서 예측하지 못한 동작이 생길 수 있어요.

[^1]: https://www.w3.org/TR/2011/WD-html5-20110405/text-level-semantics.html#the-a-element 의 Content model 항목을 참고해주세요.
[^1]: <https://www.w3.org/TR/2011/WD-html5-20110405/text-level-semantics.html#the-a-element> 의 Content model 항목을 참고해 주세요.

```jsx
<a href="/go-to">
Expand Down Expand Up @@ -84,7 +84,7 @@ UI 구성상 다음과 같이 버튼처럼 보이는 카드 안에 또 다른

## 이런 구조가 왜 문제가 될까요?

왜 버튼 안에 버튼을 넣으면 안되는지 자세히 설명해 드릴게요.
왜 버튼 안에 버튼을 넣으면 안 되는지 자세히 설명해 드릴게요.

### 접근성 관점에서의 문제

Expand Down
6 changes: 4 additions & 2 deletions fundamentals/a11y/why.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
# 접근성을 지켜야 하는 이유

접근성을 지키면 장애인, 비장애인, 개발자 모두에게 효용이 있어요. 장애인 사용자는 스크린리더 같은 보조기기를 통해 웹사이트를 원활하게 이용할 수 있고, 일반 사용자는 더 빠르고 편리한 웹 경험을 할 수 있어요. 또한 개발자는 더 견고하고 유지보수하기 쉬운 코드를 작성할 수 있어요.
접근성을 지키면 장애인, 비장애인, 개발자 모두에게 효용이 있어요. 장애인 사용자는 스크린리더 같은 보조 기기를 통해 웹사이트를 원활하게 이용할 수 있고, 일반 사용자는 더 빠르고 편리한 웹 경험을 할 수 있어요. 또한 개발자는 더 견고하고 유지보수하기 쉬운 코드를 작성할 수 있어요.

## 장애인에게 필요한 이유

버튼의 색상, 아이콘 형태, 레이아웃 배치, 차트나 이미지 같은 웹 상의 시각적 정보를 볼 수 없는 사용자는 어떻게 웹사이트를 이용할까요? 바로 스크린리더 같은 보조기기를 사용합니다. 이때, 올바른 역할(`role`), 레이블(`label`), 대체 텍스트(`alt`) 같은 접근성 속성이 제공되지 않으면 원하는 정보를 얻지 못하거나, 버튼·링크 같은 상호작용 요소를 놓쳐 웹사이트 사용에 큰 불편을 겪게 돼요.
버튼의 색상, 아이콘 형태, 레이아웃 배치, 차트나 이미지 같은 웹 상의 시각적 정보를 볼 수 없는 사용자는 어떻게 웹사이트를 이용할까요? 바로 스크린리더 같은 보조 기기를 사용합니다. 이때, 올바른 역할(`role`), 레이블(`label`), 대체 텍스트(`alt`) 같은 접근성 속성이 제공되지 않으면 원하는 정보를 얻지 못하거나, 버튼·링크 같은 상호작용 요소를 놓쳐 웹사이트 사용에 큰 불편을 겪게 돼요.

예를 들어, 단순히 클릭 이벤트만 달아둔 `<div>` 는 스크린리더에서 버튼 요소로 인식되지 않아서 사용자가 해당 기능을 사용할 수 없어요.

## 일반 사용자에게도 유용한 이유

접근성을 지키면 장애가 없는 일반 사용자도 웹을 더 빠르고 편리하게 이용할 수 있어요. 익숙한 웹 동작이 자연스럽게 제공되기 때문이에요.

예를 들어, 링크(`<a>`)를 제대로 사용하면 마우스 오른쪽 버튼으로 '새 창에서 열기', '링크 복사'와 같은 동작을 할 수 있어요. 하지만 단순히 `<div>``<span>`에 클릭 이벤트만 넣으면 이런 기본 기능을 사용할 수 없어요. 또, 폼을 만들 때 올바른 `<form>` 태그와 `<input>`, `<button type="submit">`을 사용하면 사용자가 Enter 키를 눌러 폼을 제출할 수 있어요. 하지만 그렇지 않으면 사용자는 익숙한 동작을 기대할 수 없어요.

또, 키보드를 주로 사용하는 사용자는 Tab 키와 Shift+Tab, Enter, Space, 방향키 등으로 웹사이트를 탐색해요. 버튼이나 링크, 폼 요소에 올바른 역할과 포커스가 지정되어 있지 않으면, 키보드로는 해당 기능을 사용할 수 없어요.

다음 이미지는 브라우저에서 링크를 우클릭했을 때 나타나는 컨텍스트 메뉴예요. 링크 요소를 제대로 사용하면 사용자가 기대하는 이런 기본적인 기능들을 모두 사용할 수 있어요.
Expand Down