분류 Reactjs

복잡한 반응 경험을 위한 키보드 접근성을 디자인하는 방법

컨텐츠 정보

  • 조회 487 (작성일 )

본문

지난주는 Pingboard에서 UX 디자이너 및 엔지니어로 근무한 마지막 주였습니다. 하지만 여전히 제품에 세계 최고의 조직도 솔루션이 포함되어 있다고 생각합니다.


마우스를 사용하여 조직 구조를 빠르게 끌어서 놓을 수 있으며 직원 데이터는 다른 도구와 동기화 된 상태를 유지합니다. 가장 인상적인 측면은 조직도가 어떻게 살아 있는지입니다. 직원들은 스스로 조직도를 탐색하고 대중에게 공개 할 수도 있습니다.


하지만 사용자가 마우스로 조직도를 탐색 할 수 없으면 어떻게 됩니까? 미국인 열 명 중 세 명은 일종의 장애를 가지고 있습니다. 이러한 장애 중 상당수는 사람들이 키보드만으로 웹을 검색하도록 제한합니다.


사용자가 탭 키를 사용하여 조직도를 탐색하려고 하면 예측할 수 없는 방식으로 노드를 건너 뛰는 데 포커스가 사용되었습니다.


마우스없이 조직도를 편집하는 것은 잊으십시오. 많은 편집 작업이 액세스 할 수 없는 오버플로 메뉴 및 모달 내에 숨겨져 있었습니다. 우리는 조직도를 완전히 키보드에 액세스 할 수 있도록 1 년 동안 보냈습니다.



해결책은 단 하나의 스프린트가 아닙니다. 1 년 동안 많은 릴리스가 배포되었습니다.


이 블로그 게시물에서는 먼저 이러한 상호 작용의 디자인 프로세스를 자세히 설명하겠습니다. 다음 섹션에서는 React에서 이러한 상호 작용을 구현하는 방법에 대해 자세히 설명합니다.


마지막으로 복잡한 경험에 키보드 접근성을 추가하는 일반적인 문제를 처리하는 방법을 배우기를 바랍니다!


포괄적인 경험을 디자인하는 방법 


해결하려는 접근성의 측면을 항상 알아야 합니다. 접근성 솔루션은 특정 장애에 대한 1 : 1 수정이 거의 없습니다.


예를 들어 키보드 접근성을 추가하면 다양한 운동 및 시각 장애가 있는 사람들에게 도움이 될 수 있습니다. 키보드 접근성은 시각 장애인 색맹에 큰 영향을 주지 않습니다. 이것이 일반적으로 액세스 할 수 있는 대신 조직도 키보드를 액세스 할 수 있도록 만드는 이유입니다.


웹 접근성의 가장 좋은 점 중 하나는 국제적으로 승인 된 가이드 라인이 있다는 것입니다. W3C에는 WAI-ARIA Authoring Practices라는 문서가 있습니다. 그게 엄청나다는 건 알고 있지만 그 이유는 다음과 같습니다. 무료 리소스에는 일반적인 UI 요소에 대한 자세한 디자인 패턴이 포함되어 있습니다.

아코디언, 드롭 다운 메뉴, 모달 및 팝 오버가 어떻게 작동해야 하는지 알고 싶으십니까? 이 문서는 코딩 된 예제에 필요한 정확한 키보드 상호 작용을 지정합니다. 이러한 패턴 덕분에 공통 구성 요소를 쉽게 업데이트 할 수 있었습니다.


올바른 정신 모델을 찾는 방법 


Three basketball players with the React logo on their jerseys reading a playbook together 



조직도의 키보드 접근성을 디자인하는 데있어 까다로운 부분은 카드 간 탐색이었습니다. 문서에 조직도에 대한 패턴이 없습니다. 그래서 우리는 무엇을 합니까?!


인터랙션 디자인의 가장 좋은 속성 중 하나는 친숙 함입니다. 우리는 조직도 탐색의 가장 가까운 정신 모델에 대한 문서를 연구하기 시작했습니다. 정신 모델은 다른 경험에서 파생 된 표현입니다.


“사람들의 상호 작용 모델을 염두에 두고 설계하십시오. 작동 방식에 대한 표준 멘탈 모델이 있는 경우 해당 모델을 활용하여 설계하십시오. 이것이 가능하지 않은 경우 (예 : 시스템이 새롭고 참신한 경우) 가능한 한 공통적 인 정신 모델에서 도출되는 상호 작용 경험을 생성하십시오 ...”– Lidwell, Holden 및 Butler의 Universal Principles of Design


그렇다면 사람들이 매일 상호 작용하는 가장 유사한 패턴은 무엇일까요? 답은 파일과 폴더를 탐색하기 위한 파인더 창 (공식적으로 트리보기 라고 함)이었습니다. 병렬 파일을 탐색하고 폴더를 더 깊이 파헤치는 정신 모델은 사용자에게 적합했습니다!


우리가 해야 할 유일한 변경은 화살표 키의 축을 뒤집는 것입니다. 폴더 탐색에서 오른쪽 화살표 키를 누르면 더 깊이 이동하지만 조직도에서는 아래쪽 화살표 키를 사용하면 더 자연스럽게 느껴집니다. 우리는 다음과 같은 키보드 컨트롤로 끝났습니다.


  • 왼쪽 및 오른쪽 화살표 – 데이터 구조에서 인접한 노드로 포커스를 이동합니다.
  • 아래쪽 화살표 – 포커스가 닫힌 노드에 있으면 노드가 열리고 포커스가 이동하지 않습니다. 포커스가 열린 노드에 있으면 포커스를 첫 번째 자식 노드로 이동합니다. 초점이 끝 노드에 있으면 아무 작업도 수행하지 않습니다.
  • 위쪽 화살표 – 열려있는 노드에 포커스가 있을 때 노드를 닫습니다. 포커스가 끝 노드이거나 닫힌 노드 인 자식 노드에 있는 경우 포커스를 부모 노드로 이동합니다. 포커스가 끝 노드이거나 닫힌 노드 인 루트 노드에 있으면 아무 작업도 수행하지 않습니다.
  • Home and End – 첫 번째 및 마지막 노드로 초점을 이동합니다.
  • a-z, A-Z – 입력 된 문자로 시작하는 직무 또는 부서 제목이 있는 다음 노드로 포커스가 이동합니다. 포커스 된 노드를 따르는 노드에서 일치하는 이름이 발견되지 않으면 검색이 첫 번째 노드로 래핑됩니다. 검색은 닫힌 노드의 하위 노드를 무시합니다.
  • * (별표) – 포커스가 있는 노드와 동일한 수준에 있는 모든 닫힌 형제 노드를 확장합니다. 초점이 움직이지 않습니다.

호버 상호 작용을 키보드로 변환하는 방법 


조직도의 유쾌한 측면은 탐색 할 때 카드가 얼마나 깔끔하게 보이는지 입니다. 각 카드에는 잠재적으로 8 개의 버튼이 연결되어있을 수 있지만 카드 위에 마우스 커서를 올릴 때까지 해당 버튼을 숨 깁니다.


그렇다면 모든 버튼이 숨겨져 있을 때 키보드 탐색을 어떻게 수용 할 수 있을까요?


Enter 키로 카드를 선택하면 모든 카드 동작 (버튼)을 공개하기로 결정했습니다. 그 시점부터 키보드 상호 작용을 레이아웃 그리드라고 하는 것으로 전환합니다.


레이아웃 그리드에서 찾은 가장 가까운 멘탈 모델은 화살표 키로 스프레드 시트를 탐색하는 것입니다.


레이아웃 그리드 구현의 까다로운 부분은 상호 작용 레이아웃입니다. 레이아웃에서 항목이 어디에 있는지 시각적으로 알 수 있지만 상호 작용 레이아웃을 사용하려면 엄격한 열 및 행 그리드에 작업을 배치해야 합니다.


상호 작용 레이아웃은 어떤 화살표 키를 눌렀는지에 따라 초점이 어디로 이동하는지 개발자에게 알려주는 명확한 기술 문서입니다.


초점을 맞추기 위해 기술 구현에 대해 이야기하겠습니다.


React로 접근 가능한 포커스 상태를 코딩하는 방법 


웹에서 키보드 접근성에 대해 이야기 할 때 주로 포커스 상태에 대해 이야기하고 있습니다.


브라우저는 한 번에 하나의 초점 항목 만 가질 수 있습니다. 앞으로 이동하려면 Tab 키를 사용하고 뒤로 이동하려면 Shift + Tab 키를 사용하여 키보드로 브라우저를 탐색하는 데 익숙합니다.


상호 작용 디자인에 대한 이전 섹션에서 살펴본 것처럼 다양한 환경에서 포커스 상태를 이동하려면 더 복잡한 키보드 컨트롤이 필요합니다.


React가 UI를 렌더링 하는 데 사용하는 라이브러리 인 경우 해당 컨텍스트에서 포커스 상태에 대해 생각해야 합니다. React에서 포커스 상태를 처리 할 때 네 가지 일반적인 문제가 있습니다.


  • 사용자 정의 키보드 상호 작용 생성
  • 초점 트래핑 적절한 구성 요소
  • 다른 구성 요소에 초점 전달
  • 아직 장착되지 않은 구성 요소에 초점 전달

빠른 팁 : 아래의 많은 예제에서 모달 (대화 상자)에 대해 작업 할 것입니다. 솔직히, 접근 가능한 모달을 위한 오픈 소스 솔루션이 백만 개가 있습니다. 예제가 가치 있는 이유는 React에서 일반적인 초점 상태 문제를 인식하고 해결할 수 있기 때문입니다.


React에서 사용자 정의 키보드 상호 작용을 만드는 방법 


일반적인 구성 요소의 경우 Reakit을 충분히 권장 할 수 없습니다. 대화 상자, 팝 오버 등이 있는 키보드로 액세스 할 수 있는 스타일이 지정되지 않은 구성 요소 라이브러리입니다. 요즘 내 모든 개인 프로젝트에 사용합니다. 애니메이션을 위한 Framer Motion과 스타일링을 위한 Styled Components와 결합되어 킬러 믹스입니다.


하지만 React에서 사용자 지정 키보드 상호 작용을 만들어야 한다면 어떻게 해야 합니까? 키보드 이벤트에 대한 배경 지식을 갖는 것은 현명하지만 React 후크를 사용하면 매우 쉽습니다!


다음은 React-Use라는 React 후크 라이브러리를 사용하는 간단한 키보드 상호 작용입니다.


https://br4vo.csb.app/


위에서 우리는 esc 키를 사용하여 모달을 닫기 위해 모달 사양을 일치시킵니다. 이것은 접근성에 있어서도 좋을 뿐만 아니라 유용한 키보드 단축키에도 좋습니다!


React에서 컴포넌트 내부에 초점을 잡는 방법 


주의 : 다음 두 데모는 초점 상태를 위해 서로 싸우기 때문에 이 페이지에서 버그가 있을 수 있습니다. 한 번에 하나의 데모 모달 만 열거 나 "샌드 박스 열기"를 클릭하여 최상의 경험을 위해 별도의 창에서 확인해야 합니다.


이제 위의 모달 구성 요소에 대한 나쁜 소식이 있습니다. 모달이 열렸을 때 포커스를 얻은 것은 없습니다. 실제로, 모달 내부의 첫 번째 포커스 가능 요소는 포커스를 얻어야 하고 포커스 상태는 모달을 벗어날 수 없어야 합니다.


우리는 React Focus Lock을 통해 모달에 포커스 트랩을 추가하여 이 문제를 해결할 것입니다. 초점은 마운트 해제 될 때까지 초점 트랩에서 벗어날 수 없으며 첫 번째 초점 가능 항목이 자동으로 초점을 맞춥니 다.


https://owxdt.csb.app/


포커스 트래핑을 해결할 수 있는 다양한 종속성이 있습니다. 또한 사용자 지정 상황에서 구현하는 것도 매우 쉽습니다. 필요한 모든 것에 대해 생각해야 합니다.


  • 트랩하려는 UI로 첫 번째 및 마지막 포커스 가능한 요소 찾기
  • 사용자가 마지막 항목에 초점을 맞춘 상태에서 탭을 누르면 첫 번째 항목에 초점을 맞춥니 다.
  • 사용자가 첫 번째 항목에 초점을 맞춘 상태에서 Shift + Tab을 누르면 마지막 항목에 초점을 맞춥니 다.


다른 구성 요소에 포커스를 전달하는 방법 


Basketball player labeled 'A' passing basketball that says 'Focus' to another player 


우리가 만난 또 다른 일반적인 문제는 동일한 바로 가기 키를 사용하는 다른 구성 요소 안에 키보드 바로 가기가 있는 구성 요소를 넣는 것입니다.


예를 들어, 모달 내부에 드롭 다운이 있다면 어떻게 될까요? 드롭 다운이 확장되면 esc 키가 드롭 다운을 닫아야 하지만 모달은 닫아야 합니다.


후크 상태로 모달의 바로 가기를 일시 중지 할 수 있습니다.


https://9lcfl.csb.app/


새로운 오버플로 메뉴 구성 요소에 포함 된 두 가지 중요한 props : onClose 및 onOpen. 메뉴를 열거 나 닫을 때마다 해당 소품을 실행합니다. 이렇게 하면 모달 구성 요소가 필요할 때 자체 포커스 트랩을 일시 중지하고 다시 시작할 수 있으며 오버플로 메뉴 포커스 트랩이 인계 됩니다.


아직 장착되지 않은 구성 요소에 초점을 전달하는 방법 


대규모 조직도를 보유한 대기업이 있습니다. 즉, 조직도 경험이 성능을 발휘하려면 조직도의 많은 비동기 로드를 수행해야 합니다. 사용자가 관리자의 카드를 풀면 조직도에 더 많은 데이터를 로드합니다.


그렇다면 아직 존재하지 않는 카드에 초점을 어떻게 전달할까요? 핸드 오프를 지연 시키기 위해 setTimeout을 추가하고 싶지 않습니다. 초점이 카드의 위치를 ​​알고 있고 준비가 되었을 때 만날 수 있다면 좋을 것입니다.


투표에 대해 이야기 해 봅시다. 이것은 외부 시스템이 지속적으로 상태를 묻는 경우입니다. 웹 개발에서 우리는 일반적으로 웹 앱에 대한 폴링을 사용하여 일종의 데이터로 완료되었는지 서버에 묻습니다.


포커스가 필요한 조직도 카드가 아직 렌더링 되었는지 물어보기 위해 폴링의 매우 간단한 예제를 사용할 수도 있습니다.


let polling = null;
let pollCount = 0;
polling = setInterval(() => {
 if (pollCount > 20) {
  clearInterval(polling)
  return;
 }

 const nodeInDOM = getNodeInDOM(node);

 if (nodeInDOM) {
  nodeInDOM.focus();
  clearInterval(polling);
  return;
 }

 pollCount =+ 1;
}, 100);

이제 사용자 고유의 노드 식별자와 getNodeInDOM을 제공해야 하지만 이 모든 것이 초점을 맞추기 위한 폴링입니다. 카드가 아직 표시되었는지 여부를 최대 2 초 동안 10 분의 1 초마다 확인합니다. 해킹이라고 부를 수 있습니까? 확실한. 그러나 그것은 망할 믿을만한 해킹으로 입증되었습니다.


Player labeled 'component B' throwing the basketball to where player labeled 'component A' will catch the ball and dunk it on a basket. 


세상을 더 나은 곳으로 만들기 * 


나는 Pingboard에서 많은 훌륭한 릴리스의 일부가 되었지만,이 것이 가장 영향력이 있다고 느낍니다.


모든 사람이 동일한 도구를 사용할 수 있다는 것은 기분이 좋습니다. 이전 경험에서 작업하면서 접근성을 염두에 두고 새로운 경험을 제공 할 수 있도록 훈련했습니다.


귀하의 회사가 경험에 접근 할 수 없는 상태를 유지하는 데 따른 윤리적 및 법적 결과를 모두 고려하기를 바랍니다.


https://www.freecodecamp.org/news/designing-keyboard-accessibility-for-complex-react-experiences/