널리 알리는 기술 소식 다양한 접근성과 사용성, UI 개발에 대한 소식을 널리 알리고 참여하세요!
Spread your knowledge!

포럼 우리 모두의 소중한 의견이 모이는 곳입니다.

검색하기
  • tip
    [Sense Reader and WAI-ARIA] aria-pressed 접근성 지원 관련
    Webacc NV 2020-11-25 18:40:35

    센스리더에서도 조금씩 WAI-ARIA 지원 속성이 늘어나고 있으며 최근에 대표적으로 추가된 속성이 role radio, role checkbox, aria-checked 등입니다.

    오늘은 aria-pressed 지원에 대해 잠시 살펴 보려고 합니다.

    aria-pressed 속성은 토글 버튼을 구현할 때 사용하며 눌려졌을 때와 누르지 않았을 때의 상태를 표시합니다.

    최근 센스리더에서는 aria-pressed 속성을 일부는 지원을 하여 aria-pressed true 일 때 선택이라는 상태 정보값을 음성 출력합니다.

    예를 들어 버튼 요소의 레이블이 음소거이고 aria-pressed true 이면 선택, 음소거 버튼 이 됩니다.

    하지만 false 일 때는 값을 전달하지 못합니다.

    센스리더의 WAI-ARIA 지원 범위를 궁금해 하시는 분들이 있어 팁으로 올려봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [NVDA & HTML] tabindex -1 속성과 NVDA 포커스 처리 관련
    Webacc NV 2020-11-24 09:53:11

    웹페이지를 마크업할 때 모달 다이얼로그를 표시하거나 본문 바로가기 링크를 자바스크립트로 구현하는 경우 해당 div 에 포커스를 보내는 경우가 있습니다.

    div에는 기본적으로 탭키 등을 눌러서 포커스 할 수 없으므로 이 때는 tabindex -1 속성을 사용하게 됩니다.

    그리고 해당 div 안에는 링크나 버튼과 같은 요소와 텍스트가 함께 포함될 것입니다.

    그런데 NVDA에서 크롬이나 파이어폭스 같은 브라우저를 사용했을 때 tabindex -1 속성이 걸려 있는 div 하위 요소 탐색 시 다음과 같은 이슈가 있습니다.

    포커스가 가능한 링크나 버튼과 같은 요소 아랫줄에 있는 텍스트에 커서를 두고 탭키를 누르면 이전 요소에 포커스 되는 이슈: 예를 들어 1 링크, a 텍스트, 2 버튼이 있다고 가정해 봅시다.

    이때 화살표키로 a 텍스트에 위치해 놓고 탭키를 누르면 당연히 2 버튼에 포커스가 되어야 할 것입니다.

    하지만 다시 1 링크에 포커스 되는 현상입니다.

    해당 이슈는 NVDA 브라우즈모드 설정에서 자동으로 포커스 가능한 요소에 시스템 포커스 보내기를 체크한 경우 나타나는 이슈로 현재 수정 요청을 한 상태입니다.

    NVDA로 접근성 테스트 시에 참고가 되시길 바라며 자세한 관련 티켓은 NVDA 깃허브를 참고 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 음성검색화면 접근성 적용 예제
    Webacc NV 2020-11-23 10:12:56

    오늘은 지난 번 음성검색 접근성 구현 관련 설명을 보충하는 시간입니다.

    1. 먼저 음성 검색 화면으로 진입 시에 새로운 activity로 전환되는 경우에는 setTitle 값을 빈 값으로 두어 톡백이 액티비티 제목을 읽지 못하게 합니다.

    setTitle("");

    2. 음성검색 화면이 실행될 때 톡백이 그 어누 요소에도 포커스 되지 못하게 하기 위하여 다음 예시와 같이 모든 화면의 요소를 접근성 서비스에서 숨깁니다.

    ViewCompat.setImportantForAccessibility(getWindow().getDecorView(), viewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);

    3. 약 1초 뒤에 모든 숨긴 포커스는 원래대로 되돌립니다.

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            ViewCompat.setImportantForAccessibility(getWindow().getDecorView(), ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
        }
    }, 1000);
    

    4. 음성듣기가 완료되어 다른 activity로 갱신되지 않고 말을 이해하지 못했다는 등의 텍스트가 화면상으로 출력되는 경우에는 다음 예시와 같이 이를 톡백이 바로 읽을 수 있도록 처리합니다.

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mPlayer.start();
                    textView.setText(R.string.voiceNotUnderstand);
                    example1.setText(R.string.voiceListen);
                    textView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            example1.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
    }, 1000);
                }
            }, 5000);

    위의 예시에서 접근성 이벤트가 두 개 들어간 것을 알 수 있습니다. 

    하나는 windowStateChanged 이벤트로 음성 듣기가 끝났을 때 텍스트 내용을 바로 읽을 수 있도록 한 것입니다.

    그리고 1초 정도 딜레이를 준 다음 viewFocused 이벤트를 통하여 듣기 버튼으로 초점을 보내주었습니다.

    위와 비슷한 방법으로 구현하면 스크린 리더 사용자가 톡백을 실행한 상태에서도 음성 검색을 무난하게 수행할 수 있을 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [모바일 스크린리더 & WAI-ARIA] 우리가 표준을 따라야 하는 이유와 WAI-ARIA의 한계
    Webacc NV 2020-11-20 11:44:11

    커스텀(Custom)은 주문 제작, 사용자 지정 등의 뜻을 갖고 있습니다. 웹 개발이나 앱 개발에 있어서도 마찬가지로 개발자의 입맛에 맞게 표준에는 정의되지 않은 커스텀 요소를 만들 수 있습니다.

    웹에서는 커스텀 요소의 접근성을 지키기 위해 WAI-ARIA 명세를 사용합니다. WAI-ARIA는 보조기술(이하 AT)에서 받아들일 수 있는 요소 정보, 상태 정보, 선택 정보 등, 요소에 필요한 정보를 제공하기 위해 사용합니다.

    WAI-ARIA를 사용하지 않고 커스텀 요소를 만들면 생기는 일

    만약 WAI-ARIA를 사용하지 않는다면, 스크린리더와 같은 AT에서는 아무리 디자인을 다르게 한들, div나 span으로 만들었다면 아무 요소도 아닌 것으로 인식할 것입니다. AT를 직접적으로 사용하는 스크린리더 사용자에게는 아무런 정보도 알리지 못할 것입니다.

    WAI-ARIA는 커스텀 요소를 만드는 만능의 도구가 아니다 (1) : WAI-ARIA는 정보를 제공할 뿐, 기능을 완전히 구현해 주지는 않습니다.

    WAI-ARIA를 마법의 지팡이인 듯 사용하지 마십시오. WAI-ARIA는 오로지  사람과 AT 소프트웨어에게 정보를 전달하는 것에 초점을 맞춘 명세입니다. AT 소프트웨어 중 특히 스크린리더에 지대한 영향을 미치므로, 스크린리더를 기준으로 말씀을 드리겠습니다.

    스크린리더는 요소의 정보를 전달받으면 가상 커서가 꺼지거나, dialog(대화상자) 역할처럼 현재 영역 외의 모든 영역을 숨겨준다거나 하는 기능을 수행할 수 있습니다. 하지만 요소에는 정보 전달만의 목적을 가진 요소만 있는 것은 아닙니다. OS나 웹에는 라디오 버튼이나, 탭 컨트롤, 메뉴바, 컨텍스트 메뉴, 트리뷰와 같은 요소는 상호작용 가능한 요소(Interactable Element)가 있습니다. WAI-ARIA는 이들에 대한 정보를 제공하나 상호작용할 수 있는 조작방법을 구현해주지는 않습니다.

    HTML이나 응용프로그램에서의 커스텀은 하나부터 열까지 전부 스스로 구현해야 한다는 것을 명심해야 합니다.

    WAI-ARIA는 커스텀 요소를 만드는 만능의 도구가 아니다(2) : 모바일 기기와의 상호작용을 일반적으로 구현하기 어렵다.

    1980년도부터 2000년대 중반까지, 웹이나 응용프로그램을 사용하는 기기는 데스크탑이나 랩탑(노트북)에 한정되어 있었습니다. 하지만, 2007년, 애플이 처음으로 아이폰을 선보여 스마트폰 시대를 예고했으며, 2009년부터 상품화와 대중화가 이루어진 스마트폰이 나오기 시작했습니다.

    모바일 환경에서는 모바일 OS에서 제공하는 네이티브 이벤트, 웹에서 제공하는 이벤트가 있습니다. PC 웹 환경에서는 웹에서의 이벤트만 신경쓰면 됬었기에 별 문제가 없었으며, WAI-ARIA를 사용할 때 별 문제가 없습니다.

    하지만, 모바일 기기의 AT는 해당 기기에 최적화된 조작법을 포함하는 경우가 많습니다. 예를들어 아이폰에서 슬라이더의 값을 조절할 때에는 위 또는 아래로 한 손가락을 쓸어 값을 조절할 수 있으며, 안드로이드 기기에서는 컨텍스트 메뉴를 열어 슬라이더의 값을 숫자로 직접 입력하여 조절하거나, 슬라이더에 초점을 두고, 음량 조절 버튼으로 슬라이더 값을 조절할 수 있습니다.

    하지만 role="slider"로 구현한 슬라이더에서는 이를 일반적으로 구현하여 접근성을 보장할 방법이 없습니다. 이는 모바일 OS에서 지원하는 이벤트이기 때문입니다. 그런데 role을 사용하면, 요소 유형 정보를 AT에 전달하기 때문에, 위 조작방법을 사용할 수 없는 커스텀 요소임에도 불구하고, 볼륨 버튼을 눌러 조절할 수 있다거나, iOS에서는 위 또는 아래로 쓸어서 조절할 수 있다는 안내 힌트가 자동으로 따라 붙게 됩니다.

    그러면 어떻게 해야 하는가?

    네이티브 앱에는 HTML에 없는 요소가 많습니다. 이러한 요소를 구현할 때에는 당연히 WAI-ARIA를 사용해야 맞는 것입니다. 방법이 이것 밖에 없으니까요. 하지만, 위에서 slider는 HTML5 네이티브 input 태그에서 충분히 구현할 수 있습니다. <input type="range" />는 role="slider"와 동일합니다.

    이렇게 대체 가능한 네이티브 HTML 태그가 있다면, 되도록 네이티브 태그를 사용해야 합니다. 네이티브 HTML 태그는 어떠한 OS든 이벤트가 대응되어 있기 때문입니다.

    특히 모바일 환경은 대부분 webkit 기반의 safari(iPhone)과 웹킷-크로미움 기반의 웹 브라우저(Android)를 사용합니다.  따라서 PC 브라우저 환경보다 CSS를 사용하여 통합된 디자인을 만드는 것이 보다 수월합니다. 커스텀 요소를 사용하는 이유가 디자인적인 부분이라면 네이티브 태그의 디자인을 수정해 보는 것이 더 바람직해 보입니다.

    만약, 어쩔 수 없이 모바일 페이지에서 커스텀 요소를 써야 한다면, AT 사용자를 위해 네이티브 요소를 함꼐 제공하고, 값을 공유하는 것도 생각해볼 만할 가치가 있을 것 같습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [센스리더 & HTML] 가상커서가 자동으로 꺼지는 것 업데이트 관련
    Webacc NV 2020-11-20 08:58:42

    스크린 리더는 탭키를 눌러서 라디오버튼이나 탭컨트롤과 같은 특정 요소를 만나면 가상커서를 자동으로 끕니다.

    왜냐하면 라디오버튼이나 탭컨트롤 등은 화살표를 눌러서 옵션을 변경하도록 규정하고 있기 때문입니다.

    탭컨트롤 등을 구현할 때 키보드 이벤트를 구현해야 하는 이유가 여기에 있습니다.

    센스리더에서도 탭키를 눌렀을 때 여러 줄 입력창이나 라디오버튼, 탭컨트롤을 만나면 가상커서를 자동으로 해제합니다.

    다만 별도 소리가 없어서 사용자가 그것을 자세히 인지를 하지 못할 뿐입니다.

    그런데 7.4 버전부터는 롤 슬라이더, 아리아로 구현한 메뉴 요소를 만나도 가상커서를 자동으로 끕니다.

    따라서 슬라이더나 메뉴 내비게이션을 반드시 아리아를 사용하셔야 한다면 키보드 접근성 고려가 필요합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 한 손가락 옆으로 쓸기 탐색으로 특정 영역의 요소들이 탐색되지 않는 경우
    Webacc NV 2020-11-19 14:44:17

    아이폰에서 특정 앱이 실행되면 한 화면에 당연한 이야기이지만 여러 요소들이 존재할 것입니다.

    그런데 해당 요소들은 화면에는 보이지 않지만 UIView, UITableView, UIScrollView 와 같은 부모 요소들이 존재하게 됩니다.

    이것을 컨테이너라고 부릅니다.

    따라서 한 화면에는 콘텐츠 유형에 따라 여러 컨테이너들이 존재하게 됩니다.

    그런데 문제는 한 손가락 오른쪽 쓸기로 순차 탐색 시에 특정 그룹, 즉 특정 컨테이너는 아예 포커스가 되지 않는 경우가 있다는 것입니다.

    따라서 스크린 리더 사용자는 해당 영역이 아예 없는 것처럼 생각할 수 있습니다.

    이것의 원인은 무엇일까요?

    접근성 초점이 가는 버튼과 같은 view 하위에 또 다른 하위 요소들을 구현했기 때문입니다.

    특정 컨테이너 그룹의 접근성 포커스를 수정하거나 그룹의 여러 요소들을 하나의 포커스로 만드는 등의 작업을 하지 않는 이상 컨테이너를 구성하는 테이블, 스크롤뷰 등에는 보이스오버 포커스가 되지 않는 것이 원칙입니다.

    즉, 컨테이너 그룹 자체 뷰가 접근성 포커스를 가지게 되면 기본적으로 하위 요소에 포커스가 안 됩니다.

    따라서 접근성 초점이 가는 요소 하위에 또 다른 무언가의 콘텐츠들을 두는 것은 보이스오버 입장에서는 컨테이너 부모에 포커스가 되는 상황이 되므로 스크린 리더 사용자에게 큰 문제를 주게 됩니다.

    따라서 view를 구성할 때 접근성 초점이 가는 요소 하위에 또 다른 하위 view를 만들지 않도록 주의가 필요합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 음성 검색 시에 톡백이 말하지 않게 하기
    Webacc NV 2020-11-18 09:52:19

    음성 검색을 누르면 마이크가 켜지면서 톡백이 화면의 내용을을 읽는 것 때문에 스크린 리더 사용자가 어려움을 겪는 경우가 많습니다.

    사실 톡백 입장에서는 화면이 변경되면 변경된 내용을 읽는 것이 당연할 것입니다.

    그래서 음성 검색 화면에서는 톡백이 아무 말도 못하도록 별도 접근성 구현이 필요합니다.

    접근성 구현을 해 주어야 하는 것은 다음과 같습니다.

    1. 화면이 전환될 때 액티비티 레이블을 톡백이 읽지 못하게 하기: 이 부분은 지난 팁에서 한번 다룬 적이 있는데 setTitle을 null값 즉 ""로 둡니다.

    2. 화면 전환 시 그 어떤 요소에도 포커스 되지 못하게 하기: 톡백은 화면 전환 시에 자동으로 화면의 제목 다음에 있는 첫 요소에 포커스를 위치시킵니다.

    화면 제목 이전에 위로 이동과 같은 버튼이 있다면 그 버튼에 포커스가 됩니다.

    우리는 이러한 요소에 톡백이 포커스 하지 못하도록, 그래서 아무 말도 하지 않도록 해야 하는 것입니다.

    3. 반드시 음성 듣기 시작과 종료에 대한 효과음을 재생해야 합니다.

    4. 음성 듣기 종료 시 다른 화면으로 전환되지 않고 현재 화면에 머무르는 경우에는 어떤 메시지가 발생했는지를 자동으로 읽게 해야 하며 듣기 버튼으로 초점을 이동시켜 주어야 합니다.

    다음 팁에서는 이를 적용하기 위한 예제 코드를 공유하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 콘텐츠 검색 화면 구현 시 검색어 삭제 관련
    Webacc NV 2020-11-13 17:01:11

    일반적으로 콘텐츠 검색 화면을 구현하는 경우에는 텍스트 입력창에 글자가 입력되어 있으면 검색어를 삭제하는 버튼을 표시하는 경우가 많습니다.

    그리고 텍스트가 삭제되면 해당 버튼은 화면에서 사라지는 형태입니다.

    이때도 접근성 구현을 해 주지 않으면 스크린 리더 사용자에게 이슈가 있는데 텍스트 삭제 후에 톡백의 포커스가 상단으로 튀어버리거나 톡백의 초점이 어디에 포커스 되었는지에 대한 음성 피드백을 받을 수 없다는 점입니다.

    따라서 이를 해결하기 위해 우리는 사용자가 검색어 삭제 버튼을 누르면 다음 예시와 같이 초점을 검색어 입력 편집창으로 보내주면 됩니다.

    public void deleteText(View view) {
        Button clearText = (Button)findViewById(R.id.clearText);
        EditText editText = (EditText)findViewById(R.id.editText);
    editText.getText().clear();
    editText.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 포커스 순서 변경 예제
    Webacc NV 2020-11-12 09:56:41

    어제는 포커스 순서를 변경하는 방법에 대해 살폈습니다. 

    오늘은 관련된 예제 코드를 공유하려고 합니다.

    먼저 제목 1, 내용 1, 제목 2 내용 2 네 개의 view가 있다고 가정해 봅시다.

    그리고 한 손가락 오른쪽 쓸기를 통해 이동할 때 제목 1, 제목 2, 내용 1, 내용 2 순으로 포커스가 된다고 가정해 봅시다.

    그러면 우리는 제목 1, 내용1과 같이 포커스 되도록 수정을 해야 할 것입니다.

    UIView에 다음과 같이 4개의 view를 포커스 순서대로 AccessibilityElements 배열에 담습니다.

        override var accessibilityElements: [Any]? {

            set{}

            get{

                return [header1!,

                        detail1!,

                        header2!,

                        detail2!,
    }

    }

    }

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 포커스 순서 재조정하기
    Webacc NV 2020-11-11 10:12:20

    스크린 리더 사용자가 한 손가락 오른쪽 혹은 왼쪽 쓸기를 하면 대부분 화면상의 레이아웃 순서대로 포커스가 이동합니다.

    그러나 특정 상황에서는 화면의 포커스 순서가 논리적이지 않아 수정이 필요한 경우가 발생하게 됩니다.

    이때는 포커스 문제가 발생하는 컨테이너의 하위 뷰들의 순서를 재정의해줌으로써 해당 문제를 해결할 수 있습니다.

    이를 위해서는 컨테이너의 각각의 view들을 accessibilityElements 안에 array 형태로 담습니다.

    여기서는 순서가 중요하며 array 안에 담겨 있지 않은 뷰들은 모두 무시됩니다.

    다음 팁에서는 구체적인 예시를 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction을 활용한 톡백 사용자용 힌트 별도 지정
    Webacc NV 2020-11-10 09:48:06

    이제 replaceAccessibilityAction의 실제 적용사례를 나눌 차례입니다.

    지난 주에는 replaceAccessibilityAction의 문법에 대해 살폈었습니다.

    우리는 화면에 보여지는 편집창 레이블을 톡백에서는 다르게 읽어주도록 해볼 것입니다.

    먼저 휴대폰 번호를 입력하는 편집창이 있고 실제 레이블은 전화번호, - 제외 라고 가정해 봅시다.

    톡백에서는 -를 읽지 않으므로 톡백 사용자를 위해 전화번호, 숫자만 입력 이라고 힌트 메시지를 바꾸어볼 것입니다.

    톡백 사용자용 힌트 메시지를 별도 마크업하는 방법이 있으면 좋겠지만 현재는 접근성 API에서 이를 제공하고 있지 않으므로 우리는 접근성 액션 중에서 포커스라는 액션을 활용할 것입니다.

    즉 톡백이 전화번호 입력창에 포커스가 가면 즉 접근성 액션 중에서 접근성 포커스가 실행이 되면 이 때는 힌트 메시지를 변경하는 것입니다.

    그렇다면 다음과 같이 replaceAccessibilityAction을 활용할 수 있을 것입니다.

    ViewCompat.replaceAccessibilityAction(editText, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_ACCESSIBILITY_FOCUS, "", new AccessibilityViewCommand() {
        @Override
        public boolean perform(@NonNull View view, @Nullable CommandArguments arguments) {
            editText.setHint(R.string.talkBackHint);
            return false;
        }
    });
    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 보이스오버가 자체적으로 만들어내는 대체 텍스트 구분하기
    Webacc NV 2020-11-09 15:58:47

    접근성을 테스트 할 때 가장 흔히 접하게 되는 이슈는 대체 텍스트일 것입니다.

    이미지에 대한 대체 텍스트가 없다면 스크린 리더 사용자는 해당 요소가 어떤 요소인지조차 알 수 없기 때문입니다.

    그런데 최근들어 iOS 보이스오버에서는 이미지 인식 기술을 이용하여 대체 텍스트가 없어도 일부 요소에 자동으로 대체 텍스트를 추가하여 읽어주는 경우가 종종 있습니다.

    대표적인 요소들이 뒤로, 닫기, 검색, 홈 등입니다.

    그래서 해당 기능을 잘 모를 경우 접근성을 테스트 하는 입장에서는 이러한 버튼들에 대체 텍스트가 들어간 것으로 생각할 수 있으나 보이스오버가 예상하는 정보를 주는 것일 수 있다는 것을 알아 두셔야 합니다.

    아래는 뒤로, 닫기, 검색과 같은 버튼들이 실제 대체 텍스트로 추가된 것인지 보이스오버가 이미지를 인식하여 읽어주는 것인지를 구분하는 방법입니다.

    1. 보이스오버가 이미지 인식 기술을 통해 유추한 대체 텍스트는 요소 유형 뒤에 읽어줍니다.

    예: 버튼, 뒤로.

    2. 구분하기 애매하다 판단되는 경우에는 해당 요소에 포커스 하고 세 손가락 한 번 탭을 해봅니다.

    세 손가락 한 번 탭은 현재 위치한 포커스가 화면의 어느 부분이 위치해 있는지를 알려주는 기능인데 이미지 인식 기술이 가능한 경우에는 보이스오버가 인식한 이미지에 대한 텍스트를 함께 읽어주고 그렇지 않은 경우에는 '화면 중앙'과 같은 위치 정보만 읽어주게 됩니다.

    따라서 위치 정보만 읽어준다면 개발팀에서 대체 텍스트를 추가한 것이라고 볼 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction 사용법
    Webacc NV 2020-11-05 09:53:08

    오늘은 어제에 이어서 replaceAccessibilityAction 사용법에 대해 다룰 차례입니다.

    replaceAccessibilityAction에는 4가지의 값이 들어갑니다.

    1. AccessibilityAction: 어떤 접근성 액션을 대체할 것인지를 정해 주어야 합니다. 

    이것은 말 그대로 접근성 액션을 대체하는 것이기 때문에 해당 요소에 클릭이면 클릭, 롱클릭이면 롱클릭 등의 접근성 액션이 이미 들어가 있는 상태여야 하겠습니다.

    2. 레이블: 접근성 액션에 따라 톡백에서 디폴트로 가지고 있는 레이블 값을 변경할 때 사용합니다.

    예를 들어 클릭 액션에 대한 디폴트 레이블이 활성화 하려면 이중탭하세요 인데 이것을 재생하려면 이중탭하세요 로 변경한다면 레이블을 넣는 란에 "재생" 과 같이 값을 줍니다.

    참고로 지난번에 아티클에서도 다룬 적이 있는데 doubletap to 부분은 변경할 수 없습니다.

    레이블과 관련이 없는 접근성 액션을 대체하거나 레이블이 아닌 액션만 변경할 경우에는 레이블은 "" 로 넣으면 됩니다.

    3. AccessibilityViewCommand: 개발자가 디폴트로 구현한 액션이 아닌 접근성 서비스에서 액션을 따로 지정하는 경우에 사용합니다.

    쉽게 예를 들어 개발자가 완료 버튼 클릭 액션을 하면, 즉 톡백을 사용하지 않는 사용자가 탭을 하면 햄버거 주문이 바로 완료된다고 가정합시다.

    그렇다면 톡백 사용자도 이중탭을 하면 햄버거 주문이 완료될 것입니다.

    그런데 톡백을 실행하고 클릭 액션 즉 이중탭을 하면 햄버거 주문을 완료하겠는지 팝업을 띄우고 싶다고 가정합시다.

    이때 AccessibilityViewCommand를 사용할 수 있습니다.

    따라서 AccessibilityViewCommand 안에는 사용자가 해당 액션을 했을 때의 구현에 대한 자바 혹은 코틀린 코드를 넣게 됩니다.

    만약 AccessibilityViewCommand 는 변경하지 않고 레이블, 즉 힌트만 변경하고 싶다면 역시 AccessibilityViewCommand 는 null로 두면 됩니다.

    4. return true 혹은 false: true를 하게 되면 AccessibilityViewCommand 안의 코드를 실행한 다음에 원래 디폴트로 가진 액션을 함께 실행할 때 사용을 합니다.

    그러나 false를 하게 되면 AccessibilityViewCommand 안의 코드만 실행하고 종료됩니다.

    다음 시간에는 사용 예제를 통해 힌트 메시지를 변경하는 것에 대해 말씀드리겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction이란
    Webacc NV 2020-11-04 10:44:37

    지난 팁에서 제가 replaceAccessibilityAction에 대해 다룰 것이라고 말씀을 드렸습니다.

    안드로이드는 각 요소의 유형에 따라 클릭, 롱클릭, 이전으로 스크롤, 다음으로 스크롤 등과 같은 접근성 액션이 붙게 됩니다.

    이러한 액션의 종류는 클릭, 롱클릭 외에도, 이전으로 스크롤, 다음으로 스크롤, 포커스, 포커스 초기화 등 대략 30개가 넘습니다.

    접근성 액션은 요소 유형에 따라 시스템에서 자동으로 추가되기도 하지만 상황에 따라 수동으로 액션을 추가하거나 삭제할 수도 있습니다.

    그런데 시스템에서 자동으로 추가되어 있는 접근성 액션의 레이블이나 동작을 다른 레이블이나 동작으 변경하고 싶을 때가 있습니다.

    예를 들어 클릭을 가지고 생각해봅시다.

    만약 버튼 하나를 만들었다면 action_click이라는 접근성 액션이 시스템에서 자동으로 부여됩니다.

    action_click 접근성 액션이 삽입되었기 때문에 버튼에 포커스 하면 활성화 하려면 이중탭하세요 라는 힌트가 출력되는 것입니다.

    그리고 클릭 액션의 동작 즉 이중탭은 기본적으로 접근성 액션에 대한 수정 작업을 하지 않는 한 시스템 클릭이 동작을 합니다.

    여기서 말하는 시스템 클릭이란 개발자가 일반적으로 클릭을 구현한 그 동작이 실행된다는 것입니다.

    그런데 클릭의 레이블을 활성화 하려면 이중탭하세요 가 아니라 재생 작업을 하려면 이중탭하세요 등으로 레이블을 변경하고싶거나 톡백의 클릭 액션과 톡백을 끄고 클릭을 했을 때의 액션을 다르게 지정해야 할 때가 있습니다.

    이럴 때 사용할 수 있는 것이 바로 replaceAccessibilityAction 입니다.

    정리하자면 replaceAccessibilityAction은 현재 포함되어 있고 디폴트로 동작하는 AccessibilityAction을 다르게 수정해야 할 때 사용하는 메소드입니다.

    그럼 다음 팁에서는 replaceAccessibilityAction의 코드 사용방법과 편집창에서 어떻게 적용할 수 있는지를 다루도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 편집창의 기존 레이블 오버라이드하기
    Webacc NV 2020-11-03 09:22:22

    안드로이드에서는 무언가를 입력할 수 있는 EditText 편집 영역을 톡백에서 수정창이라고 읽어주며 해당 수정창에는 android:hint 속성을 통해 무엇을 입력해야 하는지를 표시하게 됩니다.

    또한 EditText와 입력 레이블을 표시하는 view가 분리된 경우에는 HTML와 마찬가지로 스크린 리더, 즉 톡백이 편집창에만 포커스 해도 레이블을 읽을 수 있도록 labelFor 속성을 사용합니다.

    그런데 편집창에 따라서는 화면에 보여지는 레이블과 톡백이 읽어주는 레이블을 다르게 표시하고 싶을 때가 있을 수 있습니다.

    대표적인 예를 두 가지 들어보겠습니다.

    1. 전화번호 입력 편집창의 화면에 보여지는 텍스트가 전화번호(-) 제외 라고 표시되고 있을 때: 이때톡백은 -라는 글자를 읽어주지 않습니다.

    2. 검색어를 입력하는 편집창의 화면에 보여지는 텍스트가 메뉴검색 인경우: 자동완성을 지원함을 톡백 사용자에게 알리고 싶을 때.

    지난 팁에서 EditText에는 contentDescription을 사용하면 안 된다고 말씀을 드렸습니다.

    그리고 이미 androd:hint 속성이 제공되고 있을 때는 contentDescription 속성을 넣어도 톡백이 읽어주는 레이블이 contentDescription 레이블로 변경되지 않습니다.

    게다가 AccessibilityNodeInfo 객체 내의 setHintText를 변경해도 이미 android:hint 속성이 들어가 있기 때문에 레이블이 변경되지 않습니다.

    그렇다면 이 문제를 어떻게 해결해야 할까요?

    여러 방법이 있을 수 있겠지만 방법 중 하나가 바로 replaceAccessibilityAction을 사용하는 것입니다. 

    다음 팁에서는 replaceAccessibilityAction에 대해서 살펴보고 android:hint 속성 변경을 어떻게적용할 수 있는지 살피겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 웹뷰의 페이지 로딩 화면을 구현할 경우
    Webacc NV 2020-11-02 19:43:27

    안드로이드 웹뷰에 대한 팁은 이번이 마지막이 될 것 같습니다.

    지난 안드로이드 웹뷰 팁에서는 페이지 로딩 시에 페이지 제목을 자동으로 읽어주게 함으로써 스크린 리더 사용자가 페이지가 갱신되었음을 알리게 하는 방법에 대해 공유했습니다.

    오늘은 페이지 로딩중에 대한 이야기를 해보겠습니다.

    화면에 페이지가 로딩중이라는 것을 표시하는 경우에는 스크린 리더 사용자 역시 이를 알 수 있어야 합니다.

    일반적으로 페이지 로딩 구현은 ProgressBar를 많이 사용하기 때문에 ProgressBar가 화면에 표시되는 경우 다음 예시와 같이 대체 텍스트 추가 및 ProgressBar에 초점이 자동으로 이동되도록 구현해 주면 됩니다.

    이렇게 하면 페이지 로딩 시에 초점이 ProgressBar로 이동되며 로딩이 되고 있는 진행률을 실시간으로 읽어주게 됩니다.

    @Override
        public void onProgressChanged(WebView view, int newProgress) {
            progressBar.setProgress(newProgress);
            progressBar.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
            progressBar.setContentDescription(newProgress+"%");
        }
    });
    댓글을 작성하려면 해주세요.
  • tip
    [NVDA & WAI-ARIA] aria-current 속성 처리 업데이트 소식
    Webacc NV 2020-10-30 11:35:03

    몇주 전에 NVDA에서 크롬 브라우저 사용 시에 DOM 새로고침 없이 스크립트로 aria-current 속성이 변경되는 경우에 이를 NVDA 가상커서가 캐치하지 못한다는 이슈에 대해 공유하였습니다.

    그런데 NVDA 개발자버전에서 해당 이슈가 일부 수정되어 aria-current 변경된 값을 사용자가 인지할 수 있게 되었습니다.

    NVDA 개발자분의 말에 의하면 aria-current 속성이 변경될 때 크롬 브라우저에서는 IA2_EVENT_TEXT_ATTRIBUTE_CHANGED 이벤트를 발생시킨다고 합니다.

    Firefox 브라우저에서는 크롬과는 달리 IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED 이벤트를 발생시킨다고 합니다.

    따라서 NVDA에서 크롬 브라우저에서 발생시키는 IA2_EVENT_TEXT_ATTRIBUTE_CHANGED 이벤트를 캐치하도록 수정하여 어느정도 해결이 된 것입니다.

    그런데 제가 어느정도라는 말을 사용하는 이유는 aria-current 속성이 변경되었을 때 음성으로 그것을 바로바로 피드백 해 주지 못하기 때문입니다.

    다른 곳에 포커스를 했다가 다시 변경된 요소에 포커스를 해야만 변경된 aria-current 속성을 들을 수 있습니다.

    이것을 해결하려면 크롬 브라우저 자체에서 aria-current 속성이 변경될 때 firefox 브라우저와 같이 IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED 이벤트를 발생시켜야 할 것으로 보입니다.

    그래도 어느정도 변경된 aria-current 속성을 지원하게 된 것은 잘된 일이며 다음 업데이트 되는 NVDA 정식 버전에서 반영되길 기대해 봅니다.

    또한 크롬 브라우저에서의 이벤트 속성 변경 역시 기대해 봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 앱뷰에 웹뷰 삽입 시에 페이지 로딩 완료되면 웹페이지 제목 읽어주게 하기
    Webacc NV 2020-10-29 09:18:27

    어제는 한 화면에 웹뷰와 앱뷰 요소가 함께 있을 때 앱뷰 요소와 웹뷰 요소를 톡백으로 구분하는 방법에 대해 함께 살폈습니다.

    오늘은 앱뷰 안에 웹뷰 구현 시 접근성 적용에 대해 함께 생각해 보려고 합니다.

    크롬 브라우저에서는 웹뷰 내에서 특정 링크를 눌러 페이지가 로딩이 되면 로딩 완료시에 스크린 리더 초점이 웹뷰로 이동하는 것을 알 수 있습니다.

    이렇게 되면 스크린 리더 사용자는 두 가지를 알 수 있습니다.

    1. 페이지 로딩이 완료되었다는 것.

    2. 변경된 페이지 타이틀.

    하지만 앱뷰 안에 웹뷰를 구현하는 경우에는 기본적으로 페이지가 새로고침이 되어도 스크린 리더에서는 아무런 변화가 없으므로 페이지 로딩이 완료된 것을 알 수 없습니다.

    따라서 sendAccessibilityEvent 메소드를 활용하여 페이지 로딩이 완료되었을 때 스크린 리더 포커스를 웹뷰로 보내주는 접근성 구현이 필요하며 예시는 다음과 같습니다.

    public void onPageFinished(WebView view, String url) {
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                browser.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
            }
        }, 1000);
    }
    
    여기서 약간의 시간 딜레이를 준 이유는 딜레이를 주지 않으면 어떤 경우에는 웹페이지 쪽으로 초점이 이동되지 모하는 경우가 있기 때문이며 이에 대해서는 각 웹뷰별로 테스트가 필요할 수 있습니다.
    }
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 앱뷰 내에 웹뷰를 포함시키는 경우
    Webacc NV 2020-10-28 09:18:25

    콘텐츠에 따라 앱뷰 내에 웹뷰를 삽입하여 화면을 구성하는 경우가 많습니다.

    접근성을 테스트 하고 접근성 문제에 대한 해결방안을 제시하려면 현재 탐색하고 있는 요소들이 앱뷰인지 웹뷰인지를 판단하는 것이 중요합니다.

    앱뷰냐 웹뷰냐에 따라 해결방안이 전혀 달라질 수 있기 때문입니다.

    또한 웹뷰를 구성할 때는 페이지 로딩 시에 페이지 제목을 자동으로 읽어줄 수 있도록 하는 접근성 구현이 필요합니다.

    이번에도 두 차례에 걸쳐 웹뷰에 관련된 팁을 공유해 보려고 합니다.

    오늘은 톡백으로 접근성 테스트 시에 현재 테스트하고 있는 요소가 웹뷰인지 앱뷰인지 확인하는 방법 몇 가지를 공유하고 다음 팁에서는 웹뷰 구현 시에 페이지 로딩 시 제목을 자동으로 읽어주도록 하는 구현 방법에 대해 다룰 것입니다.

    톡백으로 접근성 테스트 시에 현재 탐색하고 있는 요소들이 앱뷰인지 웹뷰인지 확인하는 방법은 다음과 같습니다.

    1. 앱뷰에는 제목에 대한 레벨이 없습니다. 따라서 제목 3, 제목 2와 같이 읽어준다면 웹뷰 콘텐츠 입니다.

    2. 현재 포커스 하고 있는 객체에서 한 손가락 아래 쓸기를 했을 때 웹뷰와 앱 뷰의 탐색설정 순서가 다릅니다. 앱뷰에서는 문자, 단어,, 줄(줄은 없을 수도 있음), 단락, 제목, 링크, 컨트롤 기본값 순으로 탐색값이 변경됩니다.

    웹뷰는 제목, 링크, 컨트롤, 문자, 단어, 줄, 기본값 순으로 탐색 단위가 변경됩니다.

    이러한 탐색 순서를 통해서 웹뷰인지 앱뷰인지를 판단한 다음 접근성 테스트를 진행한다면 좀 더 실질적인 해결방안을 도출할 수 있을 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] NestedScrollView 접근성 적용 예시 코드
    Webacc NV 2020-10-27 09:21:42

    지난 팁에서는 NestedScrollView 사용 시에 스크롤 동작에 대한 접근성을 구현하지 않으면 스크린 리더 사용자가 한 손가락 쓸기를 이용해서 탐색 시에 화면이 자동으로 스크롤 되지 않는다는 것에 대해 살폈습니다. 

    오늘은 이에 대한 접근성을 적용하기 위한 샘플 코드를 공유하려고 합니다.

    하나의 화면에 1, 2, 3, 4의 각 텍스트뷰가 있다고 가정해 봅시다.

    해당 텍스트뷰는 NestEdScrolView 안에 자식으로 포함되어 있고 현재 화면에는 1과 2만 표시되고 있습니다.

    기본적으로는 앞에서 말씀드린 바와 같이 톡백 사용자가 한 손가락 쓸기로 탐색을 하면 마치 화면에는 1과 2만 있는 것처럼 표시되고 더 이상 스크롤이 되지 않습니다.

    이것을 우리는 한 손가락 쓸기를 통해 숫자 2에 포커스가 되면 자동으로 스크롤이 되면서 3, 4가 보이게 하고 3에 포커스를 하면 역시 자동으로 스크롤이 되면서 이전 숫자들인 1과 2가 표시되도록 해볼 것입니다.

    1. 우선 위로 스크롤과 아래-로 스크롤 두 개의 메소드를 만듭니다.

    위로 스크롤 메소드:

    private void expandBottomBoard() {
        final NestedScrollView bottomBoard = findViewById(R.id.bottom_board);
        bottomBoard.post(new Runnable() {
            @Override
            public void run() {
                final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomBoard);
                if (behavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
                    behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                }
            }
        });
    }아래로 스크롤:
    private void collapseBottomBoard() {
        final NestedScrollView bottomBoard = findViewById(R.id.bottom_board);
        bottomBoard.post(new Runnable() {
            @Override
            public void run() {
                final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomBoard);
                if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }
        });
    }
    

    2. 이제 AccessibilityNode가 숫자 2에 포커스를 하면 위로 스크롤을 하게하고 3에 포커스를 하면 아래로 스크롤을 하도록 위의 두 메소드를 AccessibilityNodeInfo에 다음과 같이 적용시킵니다.

    TextView text1View = findViewById(R.id.text1);
    text1View.setAccessibilityDelegate(accessibilityDelegate1);
    TextView text2View = findViewById(R.id.text2);
    text2View.setAccessibilityDelegate(accessibilityDelegate2);
    
    final View.AccessibilityDelegate accessibilityDelegate2 = new View.AccessibilityDelegate() {
        @Override
        public boolean performAccessibilityAction(View host, int action, Bundle args) {
            if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
                expandBottomBoard();
            }
            if (super.performAccessibilityAction(host, action, args)) {
                return true;
            }
            return false;
        }
    

    };

    이렇게 하면 2라는 숫자에 포커스가 되면 자동으로 화면을 스크롤하게 되며 3이라는 숫자에 포커스 했을 때의 방법도 위와 유사합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] NestedScrollView와 스크롤 접근성 문제
    Webacc NV 2020-10-21 17:19:12

    스크롤뷰나 리스트뷰 등을 가진 콘텐츠는 기본적으로 스크롤 동작을 지원합니다. 

    톡백에서도 이러한 뷰 안에서는 사용자가 스크롤 제스처를 하지 않아도 자동 스크롤 기능을 오프 하지 않은 이상 한 손가락 쓸기를 통하여 화면 탐색 시에 보여지는 화면 그 이상의 콘텐츠가 존재할 경우 자동으로 스크롤 동작을 수행하게 됩니다.

    그런데 NestedScrollView 사용 시에는 하위에 여러 뷰가 존재하고 스크롤이 가능함에도 톡백에서 두 스크롤 제스처를 별도로 하지 않는 이상 화면이 스크롤되지 못합니다.

    따라서 스크린 리더 사용자는 NestedScrollView로 구현된 콘텐츠 탐색 시에 스크롤 되는 화면인지를 알기 어렵습니다.

    이 문제를 해결하려면 AccessibilityNodeInfo의 AccessibilityAction ACTION_FOCUS 속성을 사용하면 됩니다.

    즉 화면 하단의 NestedScrollView 하위 뷰에 포커스 되는 경우 자동으로 스크롤을 확장하도록 하는 것입니다.

    다음 팁에서는 이에 대한 샘플 코드를 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WAI-ARIA & NVDA] 크롬 브라우저에서의 aria-current 처리 관련
    Webacc NV 2020-10-19 10:07:25

    aria-current 속성은 주메뉴 중 어떤 페이지가 선택되어 있는지, 회원가입 단계에서 현재 어느 단계에 있는지, 롤링 배너 영역 중 현재 어느 배너가 롤링되고 있는지 등을 표시할 때 활용됩니다.

    aria-current 속성을 지원하는 NVDA와 같은 스크린 리더에서는 해당 속성이 부여된 요소에 포커스 하면 현재 페이지, 현재 단계 등의 정보를 읽어주게 되므로 스크린 리더 사용자가 여러 요소 중 무엇이 현재 선택되어 있는지를 바로 알 수 있게 됩니다.

    그런데 현재 NVDA와 크롬 브라우저 사용시 DOM 새로고침 없이 aria-current 속성 변경이 스크립트를 통해 이루어질 경우 NVDA 가상커서, 즉 브라우즈모드가 이를 캐치하지 못하는 이슈가 있습니다.

    이 부분에 대해서는 샘플 페이지를 통해 테스트해보실 수 있습니다.

    또한 이 부분에 대한 NVDA 버그를 리포트 한 상태이며 NVDA 깃허브 이슈 페이지를 통해 수정을 위한 논의 과정을 확인할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [웹접근성] firefox 추가기능 axSHammer 개발과 WAI-ARIA 오남용 관련
    Webacc NV 2020-10-16 12:43:36

    웹접근성에서 빠질 수 없는 것이 WAI-ARIA 입니다.

    WAI-ARIA를 잘 접목하여 접근성을 개선하면 다양한 상황에서 스크린 리더 피드백을 컨트롤 할 수 있어 스크린 리더 사용자가 웹페이지를 효율적으로 탐색할 수 있도록 도울 수 있습니다.

    그러나 WAI-ARIA를 잘못 사용하면 사용하지 않는 것보다 더 사용성을 해치게 됩니다.

    특정 페이지를 언급할 수는 없지만 aria-hidden 속성이 잘못 마크업되어 있어 꼭 읽어야 할 콘텐츠를 읽지 못하거나 aria-live를 잘못 사용하여 수시로 광고 콘텐츠를 들어야 하는 등의 경험을 자주 하곤 합니다.

    WAI-ARIA를 사용할 때는 최소한으로, 적절한 유형으로 사용해야 하겠습니다.

    WAI-ARIA를 잘못 사용하는 것은 비단 우리나라 뿐만은 아닌 것 같습니다.

    그래서 James Teh 개발자가 axSHammer라는 firefox 브라우저 추가 기능을 개발하고 있다고 합니다.

    해당 추가기능을 사용하면 스크린 리더 사용자가 페이지를 탐색하다가 뭔가 WAI-ARIA를 잘못 사용하여 읽어주지 않거나 과하게 읽어준다고 판단되는 것이 있을 때 aria-live, aria-hidden, role aplication 등의 속성을 disable 시켜 페이지를 탐색할 수 있도록 돕는 것입니다.

    그럼 또 다음에 더 좋은 팁으로 찾아뵙겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 탭컨트롤 접근성 적용 예제
    Webacc NV 2020-10-15 18:53:51

    지지난 시간에는 탭레이아웃으로 구현된 화면에서 각 탭에 접근성을 적용해야 하는 이유와 안드로이드 네이티브에서는 접근성과 관련된 탭 롤이 없다는 것, role description 속성을 이용하여 접근성 노드 인포의 정보를 변경할 수 있다는 것에 대해 살폈습니다.

    오늘은 샘플 코드 예시를 제공하려고 합니다.

    탭을 구현하는 방법은 다양하겠지만 구글 material 디자인에서 제공하는 TabLayout을 기준으로 설명합니다.

    각각의 탭을 눌렀을 때 탭이 전환되는 커스텀 내비게이션바를 createTabView 메소드로 정의하여 만들었다고 가정하고 톡백 스크린 리더가 탭 요소 유형을 읽을 수 있도록 다음 예시와 같이 구현할 수 있습니다.

    private View createTabView(String tabName) { View tabView = LayoutInflater.from(mContext).inflate(R.layout.custom_tab, null); TextView txt_name = (TextView) tabView.findViewById(R.id.txt_name); txt_name.setText(tabName); ViewCompat.setAccessibilityDelegate(txt_name, new AccessibilityDelegateCompat() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); info.setRoleDescription("tab"); } });

    참고로 public void onTabSelected(TabLayout.Tab tab) 메소드를 오버라이드 하면 선택된 탭에는 selected 속성이 삽입되므로 선택됨을 읽게 하는 메소드는 별도 구현을 하지 않아도 됩니다.

    물론 이러한 메소드를 사용하지 않는다면 AccessibilityNodeInfo의 정보를 변경해 주어야 하며 info.setSelected = "true"를 사용할 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML - WAI-ARIA] aria-hidden 속성의 이해
    Webacc NV 2020-10-14 12:09:30

    WAI-ARIA의 속성에는 모든 요소에 적용가능한 aria-hidden이라는 속성이 존재합니다. 이름 그대로 hidden, 숨김처리와 관련된 속성으로, 시각적으로는 보이지만, 스크린리더 상에서 해당 요소를 인식하지 못하도록 접근성 트리에서 숨기는 속성입니다.

    aria-hidden을 true로 요소에 적용하면 눈으로는 보고 접근할 수 있지만 마치 css에서 display:none이나 visibility:hidden 속성을 사용해서 숨긴 것처럼 스크린리더 상에서는 전혀 읽을 수 없는 상태가 됩니다.

    이번 글에서는 aria-hidden에 대한 오해나 다른 플랫폼에서는 가능하지만 aria-hidden 특성으로 인해 불가능한 기능을 구현하려고 한 사례 중점으로 aria-hidden에 대한 얘기를 Q&A 형식으로 풀어보고자 합니다.

     

    Q1. aria-hidden="true" 처리된 div 안에있는 자식 div에 aria-hidden = "false"를 사용했는데 스크린리더로 읽지 못해요. 왜 그런건가요?

    모바일, 안드로이드를 기준으로 설명드리자면, Android 접근성 객체에서는 importantForAccessibility의 값을 "no"로 지정하면 특정 자식만을 접근성 요소로 나타나게 하고, 이 외의 모든 것을 숨길 수 있습니다. 그런데, 아직 HTML에서는 이 기능을 손쉽게 구현할 수 없습니다. aria-hidden 특성상 aria-hidden="true"로 지정된 컨테이너의 하위 요소에 아무리 aria-hidden="false"값을 주어도 해당 요소를 표시할 수 없는 것이지요. 이 실수는 국내 웹페이지에서 레이어 팝업이나 대화상자를 커스텀으로 제작했을 때, 가장 많이 발견되는 사례입니다.

    따라서 이를 해결하려면 대화상자를 구현할 때에는 주 콘텐츠를 담는 wrapper 영역과 분리된 형제 구조의 div로 dimmed처리를 진행하고, absolute 포지셔닝으로 화면을 덮는 방식으로 시각적 효과를 구현한 다음, dialog가 펼처젔을 때, wrapper에 aria-hidden을 적용해 주는 것이 좋습니다.

     

    Q2. 사용자분께서 Tab 키로 저희 웹사이트를 탐색하던 아무런 음성도 출력하지 않을 때가 있다고 합니다. 왜 그럴까요?

    aria-hidden은 접근성 트리 상에서만 숨기는 것이지, 키보드 초점을 제거해주지는 않습니다. 따라서, 키보드 초점은 여전히 이동 가능한 상태이므로, Tab 키로 aria-hidden이 있는 요소를 탐색하게 되면 아무것도 안 읽게 되는 것이지요.

     

    Q3. 대화상자를 만들 때, 키보드 초점이 바깥 요소로 빠져나가는 것은 막았는데요. 모바일 스크린리더 초점이 대화상자 dimmed 레이어 아래로 빠저나가는 것은 어떻게 막나요?

    대화상자(role="dialog")를 사용하면, PC 스크린리더인 NVDA에서는 기본적으로 해당 요소 외의 다른 요소를 가상커서로 탐색할 수 없도록 자동으로 방지해줍니다. 하지만, Talkback은 현재 그렇지 않은 것으로 알고있습니다. 이럴 때 aria-hidden을 사용하면 됩니다.

    aria-hidden을 true값으로 설정하게 되면 화면을 쓸어 탐색하는 임의탐색이나, 한 손가락 오른쪽 또는 왼쪽으로 쓸어서 탐색하는 순차탐색 제스처에서 요소를 감지하지 않습니다. 물론, 아무리 모바일 페이지라고 하더래도, aria-hidden만을 적용해서는 안 됩니다. 터치가 불편한 지체장애인 사용자는 모바일도 블루투스 키보드를 통해 사용하는 분들이 많으며, 시각장애인 사용자 또한 마찬가지입니다.

     

    Q4. aria-hidden으로 처리된 특정 HTML 속성이나 aria 속성으로 id 참조가 가능한가요? 참조가 불가능하다면 어떤 문제가 생기나요?

    id로 참조하는 aria 속성(labelledby, describedby)은 참조가 가능하며, 아무런 문제가 생기지 않습니다. 하지만, table의 caption이나 figure의 figcaption, input이나 select, textarea에 사용되는 label 태그는 aria-hidden으로 숨기게 되면 특정 디바이스에서 해당 텍스트를 인식하지 못해 레이블을 읽지 못하는 문제가 생깁니다. 덧붙여서 aria-labelledby나 aria-describedby 속성은 CSS의 display:none; visibility:hidden; 또는 최신의 랜더링 숨김 처리 방식을 지정하는 content-visibility 속성을 사용하여 숨기더라도 문제없이 참조가 가능합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 탭컨트롤 접근성 적용하기
    Webacc NV 2020-10-13 15:20:35

    안드로이드 네이티브는 iOS나 웹과는 달리 접근성에서 탭 컨트롤을 정의하는 클래스가 없습니다. 

    그러나 탭컨트롤에 대한 유형 정보가 없으면 스크린 리더 사용자는 레이아웃 파악이 어렵기 때문에 대부분 이러한 역할 정보를 제공하기 위해 탭이라는 요소를 대체 텍스트로 제공하는 경우가 많습니다.

    요소 유형을 대체 텍스트로 제공하는 것은 요소 유형 정보가 제공이 되지 않는 것보다는 당연히 좋은 것이지만 이왕이면 조금 더 사용성을 고려하여 스크린 리더, 즉 톡백이 요소 유형으로 인식할 수 있도록 개발하는 것이 좋다는 것에 대해 지난 번 팁에서 언급을 했습니다.

    탭컨트롤 역시 이 관점에서 생각해 보면 일단 안타깝게도 탭에 대한 유형 정보 자체가 없지만 안드로이드 접근성 API에서 roleDescription 메소드를 통해 tab이라는 요소의 역할을 정의해 줄 수 있습니다.

    roleDescription 메소드를 사용하려면 탭컨트롤에 AccessibilityDelegateCompat 객체를 만들어 적용이 가능하며 다음 팁에서 코드 예실ㄹ 말씀드리도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML5 & 스크린 리더] required 속성 처리에 관하여
    Webacc NV 2020-10-12 12:01:16

    회원가입과 같은 폼컨트롤을  사용할 때 스크린 리더 사용자가 각 체크박스, 편집창과 같은 요소가 필수인지 아닌지를 알아야 하는 것은 두말할 나위가 없습니다.

    각 요소가 필수인지를 스크린 리더 사용자에게 알리는 방법은 '필수'와 같이 텍스트를 써주거나 aria-required, aria-invalid 속성을 사용하는 것 혹은 HTML5의 required 속성을 사용하는 방법 등이 있겠습니다.

    오늘은 HTML5의 required 속성 사용 시 스크린 리더의 읽어주는 것 관련하여 도움이 되실만한 팁을 몇 가지 정리해 보도록 하겠습니다.

    1. aria-required의 경우에는 aria-invalid 속성을 함께 사용해야 스크린 리더가 해당 요소가 필수 입력이라는 것과 조건에 맞게 입력하지 않았을 경우 입력 값이 유효하지 않음을 함께 읽어줍니다.

    하지만 HTML5의 required 속성을 사용하면 invalid 속성이 함께 포함되어 있습니다.

    즉 아이디 편집창이 있고 최소 입력값을 설정하는 minlength 값이 8로 설정되어 있으며 required 속성이 들어가 있으면 스크린 리더는 아이디 편집창이라는 것과 필수 입력이라는 것, 입력값이 유효하지 않다는 것을 함께 읽어주게 됩니다.

    다만 minlength 값을 충족하여 8자 이상이 되면 입력 값이 유효하지 않다는 invalid 속성은 살아지고 필수 입력이라는 정보만 남게 됩니다.

    2. 모바일 스크린 리더인 보이스오버와 톡백은 HTML5 required 속성을 일부만 지원합니다.

    즉 톡백의 경우는 invailid, 보이스오버는 required 속성만 읽어줍니다.

    이 부분은 각 스크린 리더에서 빠르게 개선되기를 바라봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 톡백에서 지원하는 요소 이름, 유형, 상태 말하기 순서 설정과 AccessibilityNodeInfo 정보변경 관련
    Webacc NV 2020-10-07 09:46:12

    지난 팁에서는 AccessibilityNodeInfo 컨트롤 유형 변경시에 반드시 컨테이너뷰가 아닌, 텍스트 혹은 대체 텍스트를 포함하는 커스텀 컨트롤을 사용한 뷰 자체를 수정해야 한다는 것에 대해 다루었습니다.

    이번 팁에서는 왜 그렇게 해야 하는지에 대해 다루어 보려고 합니다.

    톡백에서는 요소의 레이블, 유형, 상태정보가 있다고 가정했을 때 그것을 어떤 순서로 읽어줄 것인지를 설정하는 기능을 가지고 있습니다.

    만약 사용자가 유형, 이름, 상태 순으로 읽도록 설정하였다면 '체크박스, 햄버거 주문, 선택함'과 같이 읽어주게 됩니다.

    따라서 스크린 리더 사용자는 본인의 취향 및 정보 탐색의 성격에 따라 읽기 방식을 변경하여 사용합니다.

    그런데 요소 유형을 컨테이너뷰에 제공하게 되면 해당 컨테이너뷰에는 레이블을 포함하지 않기 때문에 사용자가 읽기 순서를 변경하더라도 무조건 요소 유형을 앞에 읽을 수밖에 없으며 사용자의 설정이 제대로 동작하지 않게 됩니다.

    즉 컨트롤, 레이블, 상태정보가 하나의 뷰에 포함되어 있어야만 톡백이 사용자의 읽기 순서 설정을 반영할 수 있습니다.

    위의 내용을 바탕으로 생각해보면 요소 유형이나 상태정보를 대체 텍스트로 넣지 않아야 하는 이유에 대해서도 우리는 알게 됩니다.

    요소 유형이나 상태 정보를 대체 텍스트로 포함하는 경우에도 톡백은 이를 레이블로만 인식하므로 사용자의 읽기 ㅓㄹ정을 반영하지 못합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] AccessibilityNodeInfo 클래스 사용시 주의해야 할 사항
    Webacc NV 2020-10-06 14:13:06

    커스텀 컨트롤을 스크린 리더에서 체크박스, 버튼 등으로 읽도록 하기 위해 AccessibilityNodeInfo setClassName 메소드를 사용해야 할 경우가 종종 있습니다.

    그런데 해당 AccessibilityNodeInfo 객체를 만들 때는 반드시 컨테이너 뷰가 아닌, 그 컨트롤을 가지고 있는 뷰 자체, 즉 텍스트 혹은 대체 텍스트가 있는 뷰에 선언을 해 주어야 합니다.

    예를 들어보겠습니다.

    체크박스를 ImageView를 사용하여 커스텀으로 제공했다고 가정해 봅시다.

    그런데 그 이미지뷰 상위에는 <LinearLayout> 컨테이너가 있으며 해당 컨테이너에는 ImageView 하나만 있다고 가정해 봅시다.

    <LinearLayout>

     <ImageView>

     android:text="햄버거 먹기"

    </>

    </LinearLayout>

    이때 체크박스로 읽도록 AccessibilityNodeInfo 객체를 선언해 주어야 하는 것은 LinearLayout이 아니라 ImageView입니다.

    컨테이너 뷰에 선언을 해도 체크박스라고 읽긴 하지만 사용성에서 약간의 문제가 있습니다. 

    이 문제에 대해서는 다음 팁에서 말씀드리겠습니다.

    댓글을 작성하려면 해주세요.
  • qna
    table tr마크업 문의
    eirene100999 2020-10-05 17:31:09

     table을 작업하다가 문의사항이 있어 올려봅니다

    아래 <html 마크업 이미지>에 (빨간라인참고) table 첫번째 tr의 태그에 rowspan값을 tr갯수로 넣고 웹화면을 보았는데 깨지지 않아서요

    <html 마크업 이미지>에 있는 마크업처럼 접근성과 사용성방면으로 사용해도 되는지 궁금합니다

    (웹화면의 결과는 ie, FF 모두 동일하게 깨지지 않습니다)

     

    <html 마크업 이미지>

    샘플 html

     

    <웹 결과 화면>

    샘플 크롬화면

     

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] iOS 14.2에서의 특정 웹뷰 포커스 튀는 버그 해결 관련
    Webacc NV 2020-10-05 15:37:25

    얼마전 iOS 14로 업데이트 되면서 특정 웹뷰에서 스크린 리더 사용자가 한 손가락 쓸기로 화면 탐색 시에 보이스오버 포커스가 첫 요소로 튀는 이슈에 대해 공유한 적이 있습니다.

    지난 번에도 말씀드린 바와 같이 해당 이슈는 보이스오버에서의 특정 웹뷰 처리 시 버그이며 현재 해당 버그는 14.2 버전에서 해결되었습니다.

    물론 14.2 버전은 아직 개발자 버전으로만 배포되어 정식 출시되지는 않았지만 접근성 테스트 시에 특정 웹뷰에서 포커스가 상단으로 튀는 이슈 발생 시 참고 부탁드리며 이후 정식 버전 출시 때 업데이트 하시면 해당 이슈는 없을 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 체크박스 접근성 적용 예제
    Webacc NV 2020-09-28 12:49:33

    지난 팁에 이어서 오늘은 안드로이드 커스텀 체크박스를 스크린 리더에서 체크박스 및 체크됨, 체크안 됨을 읽을 수 있도록 하는 예제 코드를 공유하려고 합니다.

    먼저 체크박스는 ImageView로 구현하였다고 가정하며 isChecked 라는 boolean 메소드를 통해서 상태가 변경됩니다.

    checkBox.setAccessibilityDelegate(checkBoxAccessibilityDelegate);
    final View.AccessibilityDelegate checkBoxAccessibilityDelegate = new View.AccessibilityDelegate() {
        @Override
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(host, info);
            info.setClassName("android.widget.CheckBox");
            //setCheckable을 적용해야 음성 안내
            info.setCheckable(true);
            info.setChecked(isChecked);
        }
    };
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 체크박스에 접근성 적용하기
    Webacc NV 2020-09-25 12:40:52

    여러 번 말씀드린 바와 같이 항상 커스텀 컨트롤을 사용해서 체크박스, 버튼, 라디오버튼 등을 구현하는 경우는 접근성 고려에 더 많은 고민이 필요합니다.

    그래서 접근성 구현 관점에서 생각해본다면 각 요소의 네이티브 컨트롤을 사용하는 것이 가장 좋은 것임은 두말할 나위가 없습니다.

    그 중 하나가 체크박스입니다. 

    체크박스 역시 네이티브 CheckBox 클래스를 사용하게 되면 접근성 구현에 대한 별다른 대응을 하지 않아도 됩니다.

    체크박스라는 유형, 체크가 가능하는 것, 현재 상태 등을 스크린 리더가 사용자에게 정확하게 알려주기 때문입니다.

    그런데 체크박스를 표준 컨트롤을 사용하지 않고 ImageView를 사용하여 구현했다고 가정해 봅시다.

    체크가 되었는지 그렇지 않은지는 이미지뷰에서 백그라운드 이미지 파일을 변경해서 표시했다고 가정합시다.

    그러면 스크린 리더 사용자는 해당 뷰가 체크박스인지 조차 알 수 없으며 

    체크가 되었는지도 알 수 없을 것입니다.

    이때 접근성을 구현하기 위해 AccessibilityNodeInfo 속성을 수정할 수 있습니다.

    1. 실제로 해당 뷰는 이미지뷰이지만 체크박스라는 것을 알려 주어야 하므로 AccessibilityNodeInfo 객체 안에서 클래스 네임을 체크박스로 변경해야 하고

    2. 이미지뷰는 아무런 상태를 가지지 않지만 해당 체크박스는 체크 혹은 체크해제를 할 수 있는 상태정보를 가진다는 것을 알려주기 위해 setCheckable을 true로 설정해야 하며 마지막으로

    3. 체크가 되었는지 혹은 해제가 되었는지를 알려주기 위해 setChecked를 조건문에 맞게 변경해 주어야 합니다.

    다음 팀에서는 샘플 예시 코드를 공유하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] iOS 14 특정 웹뷰 콘텐츠 보이스오버 초점 이슈 관련
    Webacc NV 2020-09-21 16:11:40

    얼마전에 iOS 14 정식 버전이 출시되었으며 이번 버전에서도 여러 접근성과 관련된 업데이트가 있었습니다.

    그런데 현재 크롬, 사파리 등을 제외한 특정 웹뷰에서 보이스오버 초점이 웹뷰의 첫 요소로 튀어 버리는 이슈가 발생하고 있습니다.

    구체적인 증상은 다음과 같습니다.

    1. 특정 웹뷰에서 한 손가락 쓸기로 이동 시에 두 번째 링크에 포커스를 하면 초점이 첫 링크로 튀어버리는 이슈가 발생합니다.

    2. 한 손가락 오른쪽 혹은 왼쪽 쓸기가 아닌 특정 링크를 임의 터치를 통해 선택한 경우에는 포커스가 튀지 않습니다.

    해당 이슈를 애플 접근성팀에 문의한 결과 보이스오버로 특정 웹뷰에 포커스 한 경우 발생되는 버그이며 다음 버전에서 해당 이슈는 수정될 것이라 합니다.

    접근성 테스트 하시는 분들은 참고하시면 좋겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] RatingBar class 접근성 적용 예시
    Webacc NV 2020-09-16 18:44:23

    지난 글에서 RatingBar 클래스만 사용하게 되면 스크린 리더 사용자가 볼륨키로 점수를 조절할 수 없다는 것에 대해 말씀드렸습니다.

    그래서 톡백에서 이 RatingBar를 SeekBar로 인식할 수 있도록 다음 예시와 같이 수정할 수 있습니다.

    ratingbar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(host, info);
            info.setClassName(SeekBar.class.getName());
        }
    });

    댓글을 작성하려면 해주세요.
  • tip
    [스크린리더] 가상커서의 이해 - 특정 요소에서 가상커서가 자동으로 꺼지는 이유
    Webacc NV 2020-09-14 15:06:56

    학습장애나 시각장애가 있는 개발자분도 이 세상엔 분명 많지만, 대다수는 그렇지 않습니다. 이는 스크린리더를 사용하는 사용자가 적다는 뜻으로도 이해할 수 있습니다. HTML5에서는 시멘틱이 강조되고, UI 형태에 따라 WAI-ARIA의 role을 사용하여 네이티브 앱에 있는 더 많은 유형을 지원할 수 있게 되었습니다.

    웹과 앱은 근본적인 차이점이 있습니다. Windows 응용프로그램에서는 가상커서라는 개념을 사용하지 않습니다. 대부분 스크린리더에서 가상커서와 동등한 개념을 가진 기능들은 웹 콘텐츠와 웹엔진을 기반으로 만들어진 소프트웨어(앱)에서만 동작합니다(예외적으로 Microsoft Narrator의 스켄 모드는 Windows 네이티브에서도 동작합니다).

    웹기반이 아니라면, 읽기포인터나 객체 탐색와 같은 스크린리더의 다른 탐색 기능을 활용하여 UI 텍스트를 읽고, Tab과 방향키, Space와 Enter만으로 모든 컨트롤을 조작하지요.

    하지만 웹에서는 가상커서라는 웹을 탐색하는 별도의 모드와 Windows의 기본 커서가 같이 동작합니다. 그리고 가상커서는 위, 아래, 왼쪽 오른쪽 화살표 키를 텍스트와 요소를 탐색하는 용도로 사용하며, 헤딩(h키) 헤딩 레빌 1부터 6까지(상단 숫자 1키부터 6키) 등, 일반적으로 텍스트를 입력하는 것 외의 아무 기능이 없는 키에 가상커서의 빠른 탐색키를 배치합니다.

    role은 단순히 스크린리더에 유형 이름만을 제공하지 않습니다. role은 요소를 읽고 사용자가 조작할 수 있는지(Read & Write), 혹은 읽기만 가능한지(Read Only). 간단하게 말해서 사용자가 상호작용할 수 있는 요소인지를 스크린리더에게 전달합니다. 그리고, 사용자가 조작이 가능하다면 기본 설정상 가상커서를 자동으로 끄게끔 되어있습니다.

    가상커서가 동작한다면 글자단위 읽기와 문단 읽기가 화살표 키에 할당되어 있어 라디오버튼을 방향키로 선택할 수 없고, 입력창에서 가상커서가 동작한다면 제목으로 바로가거나, 링크로 바로가는 키가 동작하여 글자를 입력할 수 없기 때문입니다. 

    한 문장으로 압축하여 설명드리자면, "이 요소 내에서는 다른 조작방법이 필요합니다"입니다.

    Windows에서 제공하는 tab, menu, slider 등 마우스로 클릭하여 선택하거나, 드래그하고, 특정 지점을 눌러 수치를 변경하는 작업 등을 수행하는 요소에는 반드시 키보드 조작법이 있으며, 이러한 키보드 조작이 필요한 요소를 위젯 요소(widget role)라고 합니다.

    이러한 위젯 요소는 대부분 키보드 조작이 있으므로 가상커서가 꺼짐을 유의하고, 가상커서가 꺼지는 요소라면 반드시 네이티브와 동일하거나 유사한 조작방법을 꼭 제공해 주세요.

    재생시간 조절 슬라이더라고 알려주고, 가상커서도 꺼주는데 오른쪽/왼쪽 방향키로 되감기 기능을 수행할 수 없으면 혼란스러우니까요 :(

     

    댓글을 작성하려면 해주세요.
  • tip
    [JavaScript] 스크린리더 사용자를 위한 재생 슬라이더 시간정보 업데이트에 관하여
    Webacc NV 2020-09-10 13:46:43

    웹페이지에서 재생 슬라이더를 만들 때, input[type="range"]과 맥을 같이 하는 role="slider" 역할을 사용하여 커스텀 슬라이더를 만들게 됩니다.

    role="slider"에는 총 네가지 보조 속성이 있는데, 실질적으로 스크린리더에 영향을 미치는 속성과 개발자 편의를 위한 속성이 있습니다.

    [개발자 편의를 위한 속성]에는 aria-valuemin, aria-valuemax가 있으며, [스크린리더에 영향을 미치는 속성]에는 aria-valuenow와 aria-valuetext가 있습니다.

    HTML 5에 추가된 input의 유형중 range에는 max와 min 속성값이 있습니다. 이와 마찬가지로 개발자가 slider를 개발할 때, 최소값 제한과 최댓값 제한을 Number(Element.getAttribute('aria-valuemin')), Number(Element.getAttribute('aria-valuemax')) 이렇게 가져와 이 밑으로 슬라이더의 현재값의 재한을 구현할 때 참조용으로 사용하는 것입니다.

    이 중에서 우리가 주목해야할 것은 aria-valuenow와 aria-valuetext입니다.

    먼저, aria-valuenow는 input에 value속성을 넣는 것과 같은 역할을 수행합니다. 값으로는 숫자만을 받으며, aria-valuemin과 vlauemax가 설정되있더라도, 강제로 유효하지 않은 값이 들어갈 수 있으므로, 재생 슬라이더를 구현할 때는 반드시 두 속성을 참조하여 인터렉션에 재한을 둬야합니다.

    aria-valuetext는 단위 등의 텍스트 보조 정보가 들어와야 할 때 사용하는 속성으로, 재생 슬라이더에서는 다음과 같은 텍스트를 전달하게 됩니다. 이 속성이 없다면, aria-valuenow의 숫자 값을 전달합니다.

    const volumeSlider = document.getElementById('slider-vol');
    const currentVolume = Number(slider.getAttribute('aria-valuenow'));
    volumeSlider.setAttribute('aria-valuetext',`${currentVolume} percent`);

    접근성 기능을 제공하고자 이들을 제공한 많은 슬라이더에서 오디오/비디오가 재생하는 동안 이 valuenow / valuetext를 개속 갱신하게끔 해놓은 것을 볼 수 있습니다. 그런데, 그렇게 개속 valuenow / valuetext를 갱신하면 약간의 문제가 있습니다.

    aria-valuenow /aria-valuetext는 aria-label처럼 초점을 받은 요소의 해당 속성값이 갱신될 때 마다 스크린리더 사용자에게 정보를 알립니다. 즉, 재생 슬라이더를 조절하려고 초점을 두고 있으면, aria-valuetext가 바뀔 때마다 정신이 없을 정도로 재생시간을 읽어주게 되는 것입니다.

    이 aria-valuenow / aria-valuetext는 일반적인 슬라이더처럼 키보드 동작이 발생했을 때(keydown, keyup)나 해당 요소에 초점이 갔을 때(focusin) 상태에서만 갱신이 되어야 합니다.

    따라서 위 문제를 해결하려면, 오디오가 업데이트되었을 때에는 valuetext를 갱신하지 않고, 슬라이더에서 값을 조절 좌/우 방향키 또는 Youtube 등에서 제공하는 되감기 키를 눌렀을 때나, 해당 슬라이더를 사용자가 조작하려고 초점을 보냈을 때만 이 값이 바뀌게끔 하면, 이러한 문제를 해결할 수 있으며, 스크린리더 사용자가 편하게 사용할 수 있는 커스텀 슬라이더를 만들 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] RatingBar 클래스 적용시 접근성 대응
    Webacc NV 2020-09-10 12:21:01

    만족도를 평가하는 별점을 구현할때 일반적으로 RatingBar 클래스를 사용합니다.

    그런데 RatingBar 클래스로 구현 시에는 스크린 리더 사용자가 별점을 주기 위해 해당 요소를 길게 이중탭한 상태로 오른쪽 혹은 왼쪽으로 스크롤해서 정확하게 원하는 점수에 맞추어 주어야 합니다.

    사실 이중탭한 상태로 왼쪽 오른쪽으로 슬라이드하면서 정확한 값에 맞추는 것이 스크린 리더 사용자에게 쉽지 않기 때문에 이에 대한 접근성 개선이 필요합니다.

    개선 방법은 접근성 노드 정보에서 해당 RatingBar 클래스를 SeekBar로 인식하도록 변경해 주면 됩니다.

    그렇게 변경하면 실제 클래스는 RatingBar이지만 접근성 노드 정보의 클래스는 SeekBar이기 때문에 볼륨키를 통해서 스크롤이 가능합니다.

    이는 SeekBar로 변경하는 순간 SeekBar가 가지고 있는 AccessibilityAction 정보, performAccessibilityAction 이벤트가 부여되기 때문입니다.

    코드 예시에 대해서는 다음 팁에서 다루겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WAI-ARIA]aria-label보단 aria-labelledby를 사용해야하는 이유
    Webacc NV 2020-09-08 12:36:07

    아이콘 버튼에 스크린리더용 대체텍스트를 제공하는 방법에는 IR기법으로 버튼 내부에 있는 텍스트를 숨기는 방법과 aria-label로 버튼에 대체텍스트를 제공하는 방법이 있습니다.

    aria-label은 마치 aria-live를 제공한 것 처럼, 버튼을 눌렀을 때, 레이블 텍스트가 변경되면 이를 알리는 좋은 기능을 수행합니다. 단점도 존재하는데, 바로 웹사이트 번역기능을 사용하는 외국인에게 불친절하다는 점입니다.

    aria-label 속성값은 많은 웹사이트 번역기에서 번역하지 못합니다. 일반적인 텍스트 노드만을 번역하는 경우가 많기 때문으로 보입니다.

    반면에 aria-labelledby는 이미 입력된 html 요소의 값을 가져오기 때문에 번역이 가능한 대체텍스트를 제공하기 쉽습니다. 웹사이트 번역 API에서 이 텍스트들을 번역할 수 있도록 스크립트로 동적으로 레이블 요소를 생성하지 않고, 정적으로 작성하여 display:none으로 숨겨두는 것이지요.

    레이블용 요소들을 display:none으로 숨기더라도, id 레퍼런스에서 가저온 텍스트 값은 유효하기 때문에 시각적으로는 텍스트를 공개하지 않고 편리하게 레이블을 제공할 수 있습니다.

    만약, aria-roledescription같이 아이디 레퍼런스로 연결하는 속성이 별도로 없다면 자바스크립트에서 aria-labelledby처럼 스크립트 내부에서 미리 작성된 요소의 아이디를 document.querySelector('#...').innerText나 document.getElementById('id').innerText, JQuery에서 $('#id').text()등으로 불러와서 넣어주면 비슷한 효과를 낼 수 있을 것 같습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] accessibilityViewIsModal에 관하여
    Webacc NV 2020-09-07 20:46:01

    중첩된 화면에서 가려진 화면에 스크린 리더 초점이 가지 않게 하는 것은 접근성에서 굉장히 중요합니다.

    얼마전에 안드로이드 플랫폼에서의 적용 방법에 대해서는 이미 포럼에 공유를 했습니다.

    iOS에는 accessibilityViewIsModal 이라는 속성이 있습니다.

    이것을 true로 설정하면 같은 계층의 다른 형제들은 스크린 리더에서 포커스가 안 됩니다.

    따라서 A, B 컨테이너가 있다고 가정했을 때 현재 화면 상에서 B 컨테이너만 보여지는 경우에는 B 컨테이너에 accessibilityViewIsModal을 true로 설정함으로써 가려진 A 컨테이너에 포커스가 되는 것을 막을 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] ViewController와 화면 변경 알림
    Webacc NV 2020-09-04 12:20:43

    보이스오버는 화면이 전환될 때 화면 전환 사운드를 출력함으로써 사용자에게 이를 알립니다.

    기본적으로 안드로이드에서는 액티비티가 변경될 때 화면이 변경되었음을 스크린 리더 사용자에게 자동으로 알린다고 말씀드린 적이 있습니다.

    그러면 아이폰은 언제 화면이 전환됨을 자동으로 알릴까요?

    다른 ViewController로 전환되는 경우에는 특별한 접근성 구현 없이도 보이스오버가 사용자에게 화면이 전환되었음을 알립니다.

    그러나 같은 ViewController 안에서 화면이 전환되는 경우에는 screenChangedNotification 메소드를 사용하지 않는 한 보이스오버는 화면이 전환됨을 알리지 않습니다.

    따라서 같은 ViewController 안에서 화면이 전환되는 기능을 구현하실 경우에는 반드시 screenChangedNotification 메소드를 적용해 주는 것이 스크린 리더 사용자에게 큰 도움이 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    개발자가 자주하는 웹 접근성 실수
    에어류 2020-09-03 13:20:23

    안녕하세요! 에어류입니다. 

    오늘은 웹 접근성을 적용하는데 있어서 주요 적용직군인 퍼블리셔가 아닌 개발자분들이 자주하는  접근성 실수를 정리해볼까합니다.

    우선  아래 그림을 한번 보시겠습니다.

    이미지 자체에 자바스크립트를 이용하여 기능을 연결하여 마우스로만 기능실행이 되는 사례(아이디비밀번호 입력창에서 가상키보드 버튼을 자바스크립트로만 구현한 사례와 공인인증서 로그인에서 주민등록번호 입력을 위해 가상키패드 버튼을 자바스크립트로 마우스 이용만을 위해 기능을 제공한 사례)

    해당 이미지에서 가상키보드 버튼에 대해 <img src="이미지 경로" alt="가상키보드" onclick="javascript:popup();" /> 

    로 제공했다면 키보드가 접근할 수 없는 요소인 이미지 자체에 자바스크립트를 이용하여 기능을 연결하였기 때문에 키보드 사용보장이 되지 않습니다. 

    퍼블리셔분들은 쉽게 이해하고 있는 내용이지만 개발자분들이 자주 실수하는 내용 중에 TOP1 입니다. 

     

    이를 해결하기 위해서는 <a>, <button> 등의 초점을 받을 수 있는 요소를 사용해주는 것이 가장 좋습니다. 

    <a onclick="javascript:popup();"><img src="이미지 경로" alt="가상키보드"></a>

    또 가장 많이 실수하는 사례 하나 소개합니다. 

    id 값 변경 시 label for 와 함께 변경해달라는 이야기입니다. 

    이게 무슨 말이냐고요?

    종종 우리는 금융권이나 대형 사이트의 웹 접근성을 적용하는 큰 프로젝트에서 2~3개월에 걸쳐서 퍼블리셔들이 입력도움 검사항목에 따라 입력서식과 레이블을 1:1로 매칭해줍니다. 이를 매칭해주면 입력창이 무엇에 대한 입력창인지 설명이 함께 적용되고, 마우스로 입력창을 찍어 커서를 활성화시킬때에도 레이블 영역까지도 연결이 되어 있어 좁은 입력서식에 커서 활성화가 어려운 운동장애 사용자를 위해서도 적절한 조치가 됩니다. 

    그런데 가장 마지막 작업을 하는 개발자분들이 이러한 사실을 잘 이해하지 못하고, id 값 변경에만 신경을 쓴 나머지 label의 값과 매칭 없이 자체적으로 적용해버리는 경우가 있습니다. 이러면 2~3개월간 적용했던 레이블 접근성이 모두 날아가게 되버리죠. ㅠㅠ

    입력도움을 위해 label for 값과 input id 값을 다르게 설정한 소스 화면 예시(label for =resident, input id=내챠미_ㅑㅇ1

    예시 사례 처럼 레이블의 for 값이 "resident"면 첫번째 input 창의 id 값을 똑같이 "resident"로 해주어야 하는데 "social id_1"로 제공한 사례입니다. 원래 퍼블리셔가 이를 맞춰놓았더라도 개발자가 웹 접근성 지식이 없다면 이런 대참사가 발생할 수 있습니다. 

    따라서 퍼블리싱 작업이 끝나면 개발자분에게 상세하게 접근성에 관련되어 실수 가능성이 있는 소스 부분에 대해서는 제대로 커뮤니케이션 하고 진행하시는 것이 중요합니다. 

    댓글을 작성하려면 해주세요.
  • tip
    [HTML & WAI-ARIA] 콤보상자에 aria-activedescendant를 써야하는 이유
    Webacc NV 2020-09-02 12:57:33

    국내, 국외 막론하고 현재의 많은 웹페이지에서 자동완성(autocomplete) 기능이 있는 검색 편집창을 사용합니다.
    자동완성 편집창이나, 커스컴 콤보 자는 크게, 컨트롤러와 목록 요소 그룹으로 나눌 수 있습니다.
    컨트롤러 그룹은 크게 확장 축소를 할 수 있게 돕는 버튼, 키보드 입력이 가능한 경우, 편집창이 있을 수 있으며, 목록 요소 그룹은 말 그대로, 목록 컨테이너와 option 항목이 됩니다.

    하지만, 특별한 방법을 사용하지 않는다면 자동완성만을 구현한다고 하여 이 두 요소가 서로 자동으로 연관있는 요소가 되진 않습니다.
    WAI-ARIA에는 두 요소 사이의 연관관계를 명시하는 속성이 있습니다. aria-owns, aria-controls, aria-activedescendant 등이 그것이지요.

    그중, 콤보상자나, 자동완성 편집창의 컨트롤러에는 aria-activedescendant를 사용합니다. aria-activedescendant는 HTML의 id 레퍼런스를 속성값으로 받으며, 실제 초점과 관계 없이 마치 특정 요소에 초점을 보낸 것과 같은 효과를 줄 때 사용합니다.

    웹브라우저의 네이티브 콤보상자를 예로 들어, 실제 초점은 콤보상자에 있지만, 콤보상자를 확장하면, 목록 컨테이너가 표시되며, 목록에는
    선택 포커스가 따로 존재합니다.

    브라우저에서 Tab을 통한 초점은 하나일 수 밖에 없기 때문에 이런 형태의 UI를 커스텀으로 만들 때,
    스크린리더 사용자를 위한 접근성을 지키기 어려운 부분이 있습니다.


    이럴 때, 실제 DOM의 초점은 위에서 설명한 컨트롤러에 있으나, 마치 실제 초점이 option 항목에 가 있는 것 처럼 스크린리더 사용자가 콤보상자를 위 또는 아래 화살표키로 탐색했을 때, 읽고있는 항목의 텍스트와 인덱스 정보를 전달해주는 것이 aria-activedescendant입니다.

    aria-activedescendant를 꼭 써야하는 이유가 뭔가요?

    aria-activedescendant는 단순히 id로 연결된 요소에 대한 정보만을 스크린리더로 전달하지 않습니다.
    콤보상자의 경우, 잘 사용하지는 않지만, optgroup이라는 하위 컨테이너가 있습니다. 이는 role="group"과 같은 역할을 합니다. option group을 줄여서 표시한 태그임을 알 수 있지요.

    aria-activedescendant는 role="option"의 상위에 있는 부모 요소가 누구인가에 따라, 부모의 정보또한 전달합니다.

    <div class="ac-control">
        <input type="text" autocomplete="off" aria-autocomplete="both" aria-controls="select-list" aria- 
        activedescendant="focused-option" role="combobox" aria-label="Search" />
        <button aria-expanded="true">Search</button>
    </div>
    <ul role="listbox"><!--input에 Br를 입력한 상황을 가정-->
        <div class="optgroup" role="group" aria-label="suggestions">
            <li role="option" data-key="2309">Bravo</li>
            <li role="option" data-key="2310">Braile</li>
            <li role="option" data-key="2311" id="focused-option">Bronze</li>
            <li role="option" data-key="2312">Brother</li>
            <li role="option" data-key="2313">Browser</li>
        </div>
        <div class="optgroup" role="group" aria-label="website-links">
            ...생략...
        </div>
    </ul>

    위 마크업처럼, option 항목의 부모가 role="group"이라면 마치 optgroup처럼 현재 선택 초점이 간 요소의 그룹또한 읽습니다.

    좋은 것은 알겠는데, 그래도 와닿지 않아요. 또 다른 무언가가 있나요?

    위처럼 항목에 그룹이 포함되있지 않고, 위/아래 화살표 키를 누르면 바로 자동완성된 텍스트가 편집창에 들어오는 구조라면 스크린리더의 특성상, 편집창에 들어온 글자를 읽습니다. 그래서 안 와닿을 수 있어요.

    하지만, 위와 같이 자동완성된 목록이 단순히 글자를 체워넣는 것을 넘어 선택하면 바로 해당 웹페이지로 넘어가는 항목 그룹이 있는 경우, 이에 대한 안내를 지원할 방법이 없으며, 몇 개의 자동완성 제안이 화면에 표시되어 있는지 스크린리더 사용자는 알 수 없습니다.

    그리고, 모든 검색 편집창에서 탐색과 동시에 편집창에 텍스트가 체워지는 형태만 있는 것은 아닙니다. 특정 키워드를 입력했을 때, 콤보상자처럼 선택을 진행하는 형태도 있으니까요. 이러한 경우에는 편집창에 아무런 글자도 체워지지 않기 때문에 스크린리더는 "빈줄(blank)"이라는 내용만 사용자에게 전달하게 됩니다. 편집창에 내용이 곧바로 체워지지 않는 형태라면 더욱 더 이를 방지하기 위해 aria-activedescendant가 필요한 것이지요.

     

    --참고--

    [자동완성편집창 예제] : 자동완성 편집창 예제로 aria-activedescendant가 스크린리더에게 어떤 정보를 전달하는지 체험해보세요. 한글 "올" 자를 편집창에 입력하면 항목이 표시되며, 위/아래 화살표 키로 탐색할 수 있어요.

    콤보상자 예제 : 현재 옵션 상자 내 스크롤이 작동되지 않는 버그가 있습니다. HTML의 select 요소를 커스텀으로 구현한 예제입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] LinearLayout, RelativeLayout 컨테이너에도 필요 시 대체 텍스트 추가 가능
    Webacc NV 2020-09-01 16:47:55

    접근성을 고려할 때는 스크린 리더 사용자가 화면을 탐색할 때 현재 사용자가 탐색하는 영역이 어떤 영역인지를 알수 있을까를 고민하는 것이 중요합니다.

    ListView, GridView 컨테이너에 대체 텍스트를 삽입해 주면 상황에 따라 편리하다는 팁을 공유한 적이 있는데 읽어주는 방식이 좀 다르긴 하지만 LinearLayout, RelativeLayout 등에도 대체 텍스트를 삽입하여 해당 레이아웃이 어떤 영역임을 알릴 수 있습니다.

    다만 ListView, GridView 자체에 대체 텍스트를 삽입한 것과는 읽어주는 방식이 좀 다릅니다.

    ListView, GridView는 목록, 테이블로서 시맨틱한 레이아웃으로 인식을 하기 때문에 다른 컨테이너에 있다가 해당 그리드, 리스트 레이아웃에 포커스가 되면 대체 텍스트와 함께 해당 하위 요소를 읽습니다.

    예: 사과, 과일 리스트 목록모드 항목 15개.

    그러나 LinearLayout, RelativeLayout 등은 시맨틱한 의미를 가지지 않았으므로 대체 텍스트를 삽입하면 메인 콘텐츠에 포커스 되기 전에 초점이 없는 하나의 TextView가 더 포커스 됩니다.

    그래서 상황에 따라서는 그 대체 텍스트에 해당 레이아웃의 숨김 제목을 삽입할 수 있으며 android:accessibilityHeading = "true" 속성을 넣음으로써 제목으로 읽어주도록 할 수 있습니다.

    쉽게 말해서 HTML에서 숨김 헤딩을 제공했다고 이해하시면 됩니다.

    정리하면, 미니 플레이어와 같이 화면에 제목이 없는 다른 레이아웃이 표시되어 숨김 제목을 주는 것이 탐색에 도움이 된다고 판단되는 경우에는 해당 레이아웃 컨테이너 자체에 대체 텍스트를 삽입할 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML & CSS] 레이아웃 테이블에 관하여 with 회원가입 폼
    Webacc NV 2020-08-31 12:11:12

    마크업을 할 때, 테이블은 두 가지 종류로 구분하고 있습니다.

    테이블이 만들어진 원래 목적인 자료 전달을 주로 하는 데이터 테이블이 있으며, 레이아웃을 잡기위해 사용하는 레이아웃 테이블로 나눕니다.

    데이터 테이블은 th(헤더 셀)과 td(데이터 셀)을 모두 사용하며, Office에서 사용하는 표와 같이 자료를 보기쉽게 정리하여 전달하는 본래 표의 용도입니다. 반면, 레이아웃 테이블은 th를 사용하지 않고, td만을 사용하여, 웹페이지나 특정 영역의 레이아웃을 잡는 용도로 사용하는 테이블을 말합니다. 원래 표를 그리는 목적과는 거리가 멀고, 그다지 시멘틱하지 않다고 볼 수 있습니다.

    그럼 레이아웃 테이블은 왜 사용했었나요?

    HTML5 이전에는 웹에서는 시멘틱이 그리 중요한 위치에 있지 않았습니다. 오로지 개발자 입장에서 구분하기 위해 HTML의 요소 아이디나 클래스로 div나 데이터 셀에 이름을 부여하는 것이 고작이었습니다. 그 당시에 레이아웃을 만드는 방법에는 div와 css 속성인 float을 사용한 방법과 오늘 주로 얘기할 테이블 셀로 만드는 레이아웃이 있었습니다.

    레이아웃 테이블은 div에 float 속성을 사용하는 것 보다 비교적 손쉽게 그리드 형태의 레이아웃을 만들 수 있었습니다. 아직도 서비스 기간이 오래된 페이지나 회원가입 폼 등에서 이 레이아웃 테이블을 아직도 많이 볼 수 있어요.

    그런데, 일찍이 스크린리더로 웹을 사용해왔던 시각장애 사용자나 글을 집중하여 읽기 힘든 삭습장애를 겪는 사용자들은 시멘틱한 웹이 필요했습니다. 스크린리더가 요소의 이름을 읽기 때문에였어요.

    당연히 그 당시에 테이블로 작성된 레이아웃은 스크린리더에서 "표"라고 읽어줬었고, 사용자는 이상하다는 생각을 하면서도, "원래 그런 것인가 보다"하고 넘겼을 겁니다.  하지만 HTML5이 보편화된 지금, 레이아웃을 전부 테이블로 만드는 것은 바람직하지 않은 행동이라고 볼 수 있겠지요.

    레이아웃 테이블의 문제점

    (1). 스크린리더 특성

    NVDA와 같은 해외 스크린 리더에서는 헤더 셀(th)을 사용하지 않을 경우, 레이아웃 테이블로 간주되어 테이블 정보를 읽지 않아 괜찮습니다. 하지만, 국산 스크린리더인 센스리더의 경우, 헤더 셀이 있건 없건간에 테이블 정보를 모두 읽는 문제점이 있으며, 가상커서로 웹페이지의 요소를 하나 씩 탐색할 때, 테이블 정보와 함께 읽게되는 행과 열에 대한 정보도 읽게됩니다.

    이것이 무슨 문제이냐 싶겠지만, 스크린리더에 익숙하지 않거나, 필요한 정보만을 듣고싶어하는 사용자 입장에서 행과 열에 대한 정보는 웹을 탐색하는 시간을 늘릴 뿐인 짐덩어리일 것입니다.

    (2). 부적절한 레이아웃 테이블 사용

    NVDA에서는 th태그가 없는 태그는 위에서 언급하였듯 데이터 테이블로 인식하지 않으며, 테이블 정보를 읽지 않습니다. 하지만, 디자인 목적으로 인해, th 태그를 사용하는 경우도 종종 있습니다. 대표적으로 회원가입 폼에서 th태그의 굵은 글씨체를 활용하는 것을 말씀드릴 수 있겠네요.

    이러한 경우, 스크린리더에서는 바로 데이터 테이블로 인식하여, 테이블에 관한 모든 정보를 읽게 됩니다. NVDA의 경우, 빠른 탐색키를 활용하여, 특정 요소에 진입 처음 진입했을 때, 아래와 같이 컨테이너 정보를 모두 읽는 특성을 가지고 있어서, 매우 긴 정보를 한번에 읽게되는 문제가 생기며, 회원가입 폼의 경우, label과 th 정보를 모두 읽기 때문에, 레이블을 두 번 읽는 문제도 생깁니다.

     

    음성출력뷰어 화면, 맨 밑줄에 [2행 3열 목록 항목 수 1개 네이버 방문함 링크]라는 출력된 내용이 쓰여있다.

    그럼, 이미 테이블 레이아웃으로 만들어진 페이지는 어떻게 하나요? 다시 만들려면 너무나 많은 노력과 시간이 필요해요.

    WAI-ARIA의 role 중에는 요소 유형을 없애는 none 값이 있습니다. 이를 통해 스크린리더 사용자가 웹페이지를 탐색할 때 "표"라고 읽는 것을 방지할 수 있으며, 다른 시멘틱 role과 함께 사용하여, 시멘틱한 웹페이지를 만들 수 있습니다.

    <!doctype html>
    <html lang="ko">
    <head>...생략...</head>
    <body>
    
    <table role="none">
        <tr>
            <td role="banner">banner는 HTML5의 header 태그와 동일합니다. 이곳은 배너 영역입니다.</td>
        </tr>
        <tr>
            <td role="complementary">complementary는 HTML5의 aside 태그와 동일합니다. 이곳은 보조정보 영역입니다.</td>
        <td role="main">값 이름과 같이 HTML5의 main태그와 같습니다. 주요 콘텐츠 영역입니다.</td>
        </tr>
        <tr>
            <td role="contentinfo">contentinfo는 HTML5의 footer태그와 같습니다. 콘텐츠정보 영역입니다.</td>
        </tr>
    </table>
    
    </body>
    </html>

    NVDA, 센스리더 할 것 없이 Chrome 기반의 브라우저에서 role="none"을 사용하여 테이블 요소 유형을 제거할 경우, 테이블 자식으로 어떤 셀을 사용하건 간에 마치 div 태그를 쓴 것과 같이 아무런 표 정보도 읽지 않는 것을 경험해 보실 수 있습니다.

    다만, 이 방법의 경우, Internet Explorer를 지원하지 않는 문제점이 있다는 점을 주의하셔야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    pointer-event를 활용하여 ::after와 ::before를 디자인 요소로 활용해보세요.
    Webacc NV 2020-08-27 12:35:53

    종종 CSS로 원하는 스타일을 만들기 위해 종종 after나 before 요소를 만들고, 요소 위에 덮는 경우가 있습니다.

    특히 이미지 위에 오버레이 효과를 낼 때 자주 사용하는데, 이 방법을 사용합니다. 그런데, Javascript를 사용하여 Click 이밴트를 줄 때, 버그를 방지하기 위해 버블링(자손으로의 이벤트 전파)을 막는 경우가 있습니다.

    이런 경우, after나 before 요소에 클릭 이벤트가 걸리지 않습니다. 만약 position:absolute와 z-index로 요소에 오버레이 효과를 줬을 경우, 버튼에 제대로 클릭이 닿지 않을 수 있다는 것이지요 이럴 때는 덮고있는 before나 after 요소에 'pointer-events'를 none 값으로 설정하면 z-index 계층상 해당 요소 밑에 있는 요소도 클릭할 수 있는 상태로 만들 수 있습니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [스크린리더] NVDA 음성 말하기 끔/켬 사용 관련
    Webacc NV 2020-08-27 09:36:35

    PC에서 웹페이지를 테스트할 때 NVDA 스크린 리더를 사용하는 경우 말하기 끔/켬을 토글하는 단축키를 사용하면 편리합니다.

    기본 단축키는 nvda key + s이며 nvda 설정 카테고리 중 키보드 섹션에서 nvda 키를 무엇으로 할 것인지 정의할 수 있습니다.

    참고로 만약 caps lock 키를 nvda 키로 설정할 경우 대소문자 변경은 caps lock 키를 빠르게 두 번 누르면 됩니다.

    해당 키를 누를 때마다 말하기 끔, 비프음으로 말하기, 말하기 켬 세 단계로 토글되는데 NoBeepsSpeechMode NVDA 추가기능을 설치하면 말하기 끔/켬 두 가지로만 사용이 가능합니다.

    이 단축키를 잘 활용하면 nvda를 수시로 온/오프 하지 않더라도 간단한 테스트 및 추가 작업이 가능할 것이라 생각됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 미디어 플레이어 일시정지, 재생은 performMagicTap 펑션 오버라이드 적용 필수
    Webacc NV 2020-08-26 10:45:21

    스크린 리더 사용자가 음악 감상을 하고 있다고 가정해 봅시다.

    다음 곡으로 넘기거나 슬라이더를 조절하는 등의 기능을 수행하려면 각 요소를 찾아가서 이중탭을 하거나 슬라이더 조절 제스처를 해야 할 것입니다.

    그런데 적어도 일시정지, 재생 버튼만큼은 일일이 요소를 찾아가서 이중탭을 하지 않더라도 특정 제스처를 하면 접근성 포커스가 어디에 있던 간에 무조건 그 기능이 실행되게끔 해준다면 어떨까요?

    iOS VoiceOver에서는 두 손가락 두 번 탭 제스처를 통해 특정 화면에서 필요한 경우 해당 기능을 매핑할 수 있도록 지원하고 있습니다.

    이것이 바로 accessibilityPerformMagicTap 펑션 입니다.

    스크린 리더 사용자는 뮤직 플레이어에서는 두 손가락 이중탭을 하면 일시정지와 재생이 될 것이라는 일반적인 생각을 가지고 있습니다.

    마치 두 손가락 문지르기를 하면 뒤로 버튼 기능이 동작할 것이라는 추측과 비슷한 것이라고 보면 되겠습니다.

    그러나 이 accessibilityPerformMagicTap 펑션을 구현해 놓지 않으면 이 기능은 동작하지 않습니다.

    따라서 다음 예시와 같이 뮤직 플레이어, 비디오 플레이어 등에서는 일시정지와 재생 펑션을 매핑해 주면 큰 도움이 됩니다.

    override func accessibilityPerformMagicTap() -> Bool {
            let isPlaying = avplayer?.rate == 1
            if isPlaying {
                pause()
                playPauseButton.setImage(#imageLiteral(resourceName: "icPlay"), for: .normal)
                playPauseButton.accessibilityLabel = "재생"
            } else {
                play()
                playPauseButton.setImage(#imageLiteral(resourceName: "icPause"), for: .normal)
                playPauseButton.accessibilityLabel = "일시정지"
            }
            return true
        }

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 한국어 네이티브 앱에서 화면 스크롤시 VoiceOver 메시지가 영어로 나온다면
    Webacc NV 2020-08-25 09:21:45

    지난번에 HTML lang 속성을 정확하게 사용하지 않으면 모바일 스크린 리더 사용자는 사용자의 환경에 따라 한국어를 영문 음성 엔진으로 읽는 문제를 겪게 된다는 것에 대해 공유한 적이 있습니다.

    그런데 iOS 앱을 개발할 때 base 언어가 영어로 되어 있는 상태에서 한국어 언어 로컬라이징 추가 없이 한국어로만 개발을 했을 때도 HTML의 lang 속성을 사용했을 때만큼은 아니지만 스크린 리더 사용자는 앱을 탐색할 때 약간의 어려움이 있습니다.

    그것은 사용자의 언어 시스템이 한국어임에도 일부 보이스오버 음성이 영문 메시지로 출력된다는 것인데요.

    특히 스크롤 제스처를 할 때 그렇습니다.

    예를 들어 UIPageView로 여러 페이지를 구현했다고 가정합시다.

    페이지를 전환하면 보이스오버 자체적으로 알림 이벤트 스트링을 언어별로 가지고 있어서 한국어의 경우 '5페이지 중 2페이지'와 같이 음성을 출력합니다.

    그런데 사용자의 시스템이 한국어라 하더라도 base 언어가 영어이면 이러한 스트링이 다 영어로 출력되어 'page 2 of 5'와 같이 음성 출력하게 됩니다.

    따라서 앱 개발 시에는 한국어는 반드시 한국어 로컬 언어 안에서 개발을 하는 것이 스크린 리더 사용자에게도 도움이 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UISlider로 구현된 슬라이더 조절과 스크린 리더 접근성 해결
    Webacc NV 2020-08-24 10:23:42

    여러 차례에 걸쳐서 안드로이드의 SeekBar에 대해 언급을 했었습니다.

    그런데 안드로이드에 SeekBar가 있다면 iOS에는 UISlider가 있습니다.

    이 UISlider 역시 뮤직 플레이어 같은 곳에서 흔히 사용되는 요소이며 VoiceOver에서는 이 슬라이더를 만나면 한 손가락 쓸어 올리기 또는 쓸어 내리기로 값 조절이 가능하게끔 해 줍니다.

    그런데 문제는 iOS 역시도 안드로이드와 마찬가지로 슬라이더를 구현을 할 때 반드시 손가락으로 드래그를 했다가 손가락을 뗄 때만 밸류 값이 반영되도록 구현을 많이 합니다.

    그 이유를 살펴보니 보조기술을 사용하지 않는 사람들이 예를 들어 미디어 플레이어에서 시간 조절을 하기 위해 드래그를 하면 드래그를 하는 동시에 바로 시간이 조절되는 것을 원하지 않기 때문입니다.

    그러나 항상 이 문제는 또다시 접근성에 관점에서는 이슈를 야기합니다.

    VoiceOver에서 제공하는 한 손가락 위 또는 아래 쓸기로의 슬라이더 조절은 드래그 동작이 아니기 때문에 밸류 값이 변경되더라도 이에 상응하는 이벤트는 발생하지 않습니다.

    이를 해결하는 방법은 아주 간단합니다.

    isVoiceOverRunning 펑션을 활용하는 것인데 VoiceOver가 꺼져 있을 때는 기존 방식대로 구현을 하고 VoiceOver가 켜지면 드래그를 하지 않더라도 밸류 값을 보내게끔 구현을 하면 됩니다.

    댓글을 작성하려면 해주세요.
  • news
    2021 CSUN 접근성 컨퍼런스 온라인 대체 관련
    Webacc NV 2020-08-21 18:36:09

    CSUN 컨퍼런스는 매년 3월에 개최되는 국제적인 접근성 컨퍼런스로 캘리포니아 주립대학에서 주최하고 있습니다.

    접근성 향상을 위한 여러 노하우 및 소프트웨어 등을 공유하는 수많은 세션들과 보조기술 세미나, 박람회 등으로 구성되는 국제 행사입니다.

    그런데 내년에는 코로나 이슈로 인해 본 행사를 온라인으로 대체하여 진행하게 되었습니다.

    따라서 여러 여건상 참석하지 못하셨던 분들은 내년에 참여해서 접근성과 관련된 여러 정보를 교류하는 기회가 될 것이라 생각합니다.

    이와 관련된 자세한 내용은 Virtual Conference Announcement 내용을 참고하시면 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 한 화면에 콘텐츠를 중첩하여 화면 전환 시 초점 대응
    Webacc NV 2020-08-20 17:58:16

    얼마전에 화면 전환 시 overlay 형태로 중첩하여 전환되는 경우의 화면 전환 알림에 대한 접근성 팁을 공유했습니다.

    오늘은 초점에 대한 이야기를 해 보려고 합니다.

    MainActivity 레이아웃 파일 안에 A 화면 콘텐츠와 B 화면 콘텐츠가 들어 있다고 가정합시다.

    A 화면이 표시될 때는 B 화면은 숨깁니다. 

    그런데 A 화면에서 B라는 요소를 눌러 B 화면으로 전환되면 B 요소들이 A 요소 위에 중첩된 형태로 표시될 것입니다.

    그러면 화면 위에 화면이 또 있기 때문에 시각적으로는 A 화면은 보이지 않습니다.

    따라서 일반적으로는 A 화면은 숨기지 않습니다.

    그러나 스크린 리더 입장에서는 어떨까요?

    두 화면의 레이아웃이 다 표시될 것입니다. 

    화면 상으로만 콘텐츠가 가려졌을 뿐이기 때문입니다.

    따라서 이를 해결하려면 B 화면으로 전환될 때 다음 예시와 같이 A 화면 콘텐츠도 숨겨 주어야 합니다.

    ConstraintLayout aScreen = findViewById(R.id.main_screen);
    AScreen.setVisibility(View.GONE);

    그리고 B 화면에서 A 화면으로 돌아올 때는 다음 예시와 같이 B 화면을 숨기는 것 뿐 아니라 A 화면이 다시 표시되도록 해야 합니다.

    aScreen.setVisibility(View.VISIBLE);
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] SeekBar 클래스에 AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION 접근성 이벤트 블록 시 주의사항
    Webacc NV 2020-08-19 17:11:05

    어제는 SeekBar 클래스에 포커스 했을 때 재생 슬라이더가 변경될 때마다 자동으로 TalkBack이 퍼센트를 읽지 못하도록 하기 위한

    방법을 공유했습니다.

    그런데 어제 언급했듯이 이벤트 블록을 onProgressChanged 메소드 안에 적용을 하면 퍼센트를 사용자가 수동으로 증가 혹은 감소시켜도 변경되는 값을 읽어주지 않습니다.

    따라서 우리는 다음 예시와 같이 performAccessibilityAction 메소드를 통해서 사용자가 볼륨 키를 이용해서 값을 변경할 때는 변경된 값을 읽어줄 수 있도록 해 주어야 합니다.

    @Override
    public boolean performAccessibilityAction(View host, int action, Bundle args) {
        switch (action) {
            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
                super.sendAccessibilityEvent(host, AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);

    }

    }
        }
        return super.performAccessibilityAction(host, action, args);
    }
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 뮤직 플레이어 재생 슬라이더에 포커스 했을 때 증가되는 퍼센트 읽지 못하게 하기
    Webacc NV 2020-08-18 16:41:53

    미디어 플레이어 화면에서 곡을 재생하면 재생 슬라이더의 퍼센트는 계속 증가할 것입니다.

    그런데 재생 슬라이더에 포커스 하고 있으면 퍼센트가 증가될 때마다 자동으로 수치를 읽게 되는데 이는 음악 감상에 상당히 방해가 됩니다.

    물론 가장 좋은 것은 그 슬라이더에 포커스를 하지 않고 있으면 되긴 하지만 곡을 자주 넘겨야 하는 경우 일일이 포커스를 다른 곳으로 이동시켜 놓는 것도 번거로운 일입니다.

    따라서 우리는 특정 접근성 이벤트를 블록시킴으로써 이 문제를 해결할 수 있습니다.

    퍼센트가 증가하면 접근성 API에서 AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) 이벤트를 발생시킵니다.

    따라서 이 이벤트를 fire 하지 못하게 하면 문제는 간단히 해결됩니다.

    mSeekBar 프로그레스 객체를 예로 들어 다음 예시와 같이 구현할 수 있습니다.

    mSeekBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
                @Override
                public void sendAccessibilityEvent(View host, int eventType) {
                    if (eventType != AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) {
                        super.sendAccessibilityEvent(host, eventType);
                    }
                }

    여기까지 읽으시면 한 가지 의문이 제기될 것입니다.

    이것을 퍼센트가 증가될 때마다 적용을 하면 사용자가 볼륨키를 통해 퍼센트를 조절해도 아무 값도 읽지 않는 것 아닌지.

    맞습니다. 여기까지만 하면 수동으로 퍼센트를 조절해도 이벤트를 아예 발생시키지 않으므로 증가되는 값 자체를 읽지 못합니다. 

    이에 대한 해결 방안은 다음 팁에서 공유하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Javascript & LiveRegion] 웹에서 모바일 앱처럼 알림 제공하기
    Webacc NV 2020-08-14 10:59:30

    웹사이트를 개발하다보면 스크린리더 사용자가 이해하기 어려운 상황, 화면, 컴포넌트에 대해 무언가를 설명하거나 안내가 필요할때가 있습니다.  모바일에서는 이와 비슷한 경우를 대비하기 위한 메소드들이 존재합니다.  Android에서는 View.announceForAccessibility(message), iOS에서는  UIAccessibility.post(notification:.announcement,message) 가 있습니다.

    웹에서는 이와 비슷한 경우에 사용할 수 있는 수단으로 aria-live와 role="alert"이 있습니다.

    role="alert" 은 display나 visibility 속성으로 숨겨져있던 요소가 표시되거나, role="alert"속성을 가진 요소가 DOM 에 추가되었을 때 스크린리더 사용자에게 "알림"이라는 고정된 텍스트와 함꼐 메시지를 제공합니다. 읽고있던 텍스트가 있었다면, 무시하고 알림부터 읽습니다.

    aria-live="assertve / polite" 는 컨테이너 안의 텍스트가 변경되거나, alert처럼 자식 요소가 추가되거나 숨겨져있던 자식요소가 나타나면 업데이트된 요소를 읽습니다. 

    assertive는 role="alert"과 같이 무시하고 변경된 내용을 알립니다. ajax나 React와 같은 Virtual-DOM 기술을 사용허는 SPA 페이지에서 비동기 프로그래밍을 통해 페이지 로드, 화면 타이틀 스크린리더로 제공하기 좋으며, polite는 스크린리더가 먼저 읽던 내용이 끝나기를 기다렸다가 알립니다. 영역에 대한 알림이나, 힌트를 제공할 때 좋습니다.

     

    aria-relevant = "option1, option2 ...." 는 live region에서 변경을 감지할 유형들을 지정합니다. 여러 옵션이 있으며, 띄어쓰기로 구분하여, 여러 값을 적용할 수 있습니다. 기본값은 이 속성값을 주지않은 live region은 aria-live"additions text"가 기본 값으로 설정되있으며, removal, 즉 텍스트나 요소의 삭제 또는 숨김을 감지하는 옵션값이 있습니다. 

    assertive는 어떤 상황이든 바로 읽으므로 자세히 다루지 않으며, 이번 팁에서는 aria-live="polite"를 사용한 요소 힌트를 만들어 보도록 하겠습니다.

    HTML 마크업:

    <main id="favorite_naverPages">
        <!-- 버튼들을 스크립트로 불러오기 위한 컨테이너에 아이디를 줍니다. -->
        <h1>사이트 이동</h1>
        <!--type="button"은 굳이 필요하지않지만 넣어줬습니다.
        중요한 것은 live지만 어찌되었건 페이지 이동 버튼이니
        value값을 홈페이지명으로 합니다.-->
        <button type="button" value="http://www.naver.com">NAVER 홈</button>
        <button type="button" value="http://cafe.naver.com">NAVER 카페 홈</button>
        <button type="button" value="http://blog.naver.com">NAVER 블로그 홈</button>
        <button type="button" value="http://news.naver.com">NAVER 뉴스 홈</button>
    </main>
    <!--aria-live 콘테이너 두 개 입니다. 급보처럼 읽던 것을 중단하고 알리는 assertive 라이브 리전과
    읽던 것을 마치면 새로운 정보를 알리는 polite 라이브 리전 컨테이너입니다.
    안에 있는 p태그가 숨겨젔다 나타나는 것으로 live 정보를 스크린리더 사용자에게 전달할 겁니다-->
    <div id="assertive_announcer" aria-live="assertive">
        <p class="msgbox visible hide" id="assertive_message"></p>
    </div>
    <div id="polite_announcer" aria-live="polite">
        <p class="msgbox visible hide" id="polite_message"></p>
    </div>

    CSS 스타일링:

    :root{
        --main-background-color:#efefef;
        --main-foreground-color:#1f1f1f;
        --announce-background-color:#7fafff;
        --announce-foreground-color:var(--main-foreground-color);
    }
    *{box-sizing:border-box; margin:0; padding:0;}
    html,body{ position:relative; overflow:hidden; width:100%; height:100%;}
    body{color:var(--main-foreground-color); background-color:var(--main-background-color);}
    
    .msgbox.srOnly{/*.srOnly라는 클래스가 있으면 보이지 않는 live 알림을 제공합니다.*/
        border: 0; clip: rect(0 0 0 0);
        height: 1px; width: 1px;        
        margin: -1px; padding: 0;
        overflow: hidden; position: absolute;
    }
    
    .msgbox.visible{/*visible 클래스가 있으면 보이는 디자인을 제공합니다.*/
        position:absolute; padding:2rem; bottom:0; left:0;
        background-color:var(--announce-background-color);
        color:var(--announce-foreground-color); margin:1%;
        font-size:1.5em; max-width:40%; word-break:keep-all; word-wrap: break-word;
        border:solid 1px; border-radius:0.3rem;
    }
    
    /*classList.replace로 표시/숨김 처리할 때 쓸 class입니다. */
    .show {display:block;}
    .hide {display:none;}

    Javascript:

    /* (1) 요소 변수와 아래에서 쓸 timeout 제거용 변수 */
    const favoritePages = document.querySelectorAll('#favorite_naverPages>button')
    let hideAnnounce; let showAnnounce;
    
    /* (2) 이벤트 핸들러 등록 */
    for (let i = 0; i < favoritePages.length; i++) {
        const element = favoritePages[i];        
        element.addEventListener('click',function(){ location.href = this.value; })
        element.addEventListener('focusin',announceHintHandler)
        element.addEventListener('focusout',resetAnnounceHintHandler)
    }
    
    /*(3) 포커스 인/아웃 핸들러*/
    
    //(3-1) 초점을 받으면 맨 아래 정의된 announcement 함수를 실행합니다.
    function announceHintHandler(e){
        announcement('페이지로 이동하려면 Enter 또는 Space 키를 누르십시오.','polite')/*(3) 요소에
        초점이 가면, 요소명을 읽은 다음 안내메시지를 읽어야하기 때문에 polite를 사용합니다.
        */
    }
    
    //(3-3) 초점을 잃으면 announcement의 setTimeout들을 초기화시킵니다.
    function resetAnnounceHintHandler(e){
        clearTimeout(showAnnounce);
        clearTimeout(hideAnnounce);
    }
    
    /*(4) aria-live로 스크린리더에게 알림을 줄 함수를 만듭니다.*/
    
    function announcement(text,type,mils=4000){/*(4-1) type은 aria-live의 두 가지 속성값을 넣습니다. 
        mils는 기본값을 넣어 입력하지 않아도 기본값으로 작동하도록 합시다.*/
    
        type = (type === 'polite' ? 'polite' : 'assertive');/*(4-2) 삼항 연산자를 사용하여
        polite가 아니면 type의 기본값을 assertive로 설정합니다.*/
        
        const announcementElement = 'polite' ? document.getElementById('polite_announcer') 
        : document.getElementById('assertive_announcer'); /*(4-3) type에 따라 위에
        HTML에서 마크업한 요소를 삼항연산자로 announcementElement라는 변수에 담습니다.*/
        
        const msgbox = announcementElement.querySelector('.msgbox')/*(4-4) 메시지가 작성될 자식요소를
        announcementElement로부터 CSS Selector로 검색합니다. */
        
        showAnnounce = setTimeout(function(){/*(4-5) live 텍스트보다 요소 텍스트와 유형을 먼저
        읽게 하기 위해 메시지 출력에 약간의 딜레이를 줍니다.*/
            msgbox.classList.replace('hide','show')
            msgbox.innerHTML = text;
        },50)
    
        hideAnnounce = setTimeout(function(){/*(4-6) 눈에 보이는 안내 상자가 사라질 시간을
        지정합니다. 보이지 않는 알림은 mils를 짧게 주고, css로 보이지 않게끔 수정하면 됩니다. */
            msgbox.classList.replace('show','hide');
        },mils);
    }

    저는 눈에 보이는 알림을 만들었습니다. HTML에서 마크업된 버튼을 누르면, 네이버의 특정 사이트로 이동함을 알 수 있게끔, 스크린리더 힌트를 제공하는 예제입니다.

    주석으로 내용을 거의다 적어놓았지만 핵심만 정리를 해보겠습니다.

    focusout으로 setTimeout을 왜 초기화해야 하는가?

    제가 원하는 버튼에 초점이 갔을때, 항상 일정하게 메시지 박스가 시각적으로 표시되고, 스크린리더가 읽게 하기 위함입니다. 이 작업을 하지 않는다면 메시지 박스가 매번 정해진 시간초동안 보여지지 않습니다. 만약, 오로지 스크린리더 사용자만을 위한 알림을 제공할 경우에는 이 작업이 필요없으나, Android의 토스트와 같이 음성과 시각적 안내를 모두 지원할 경우에는 이 작업이 필요합니다.

    polite는 먼저 읽던 게 있다면 기다렸다 읽어주는데, 왜 setTimeout으로 딜레이를 줍니까?

    live 메시지는 focus된 요소를 읽는 것보다 반응이 빠릅니다. 저는 키보드 초점이 버튼에 갔을 때, VoiceOver나 Talkback의 힌트 메시지처럼 읽어주는 알림을 원하는데, (4-5)번에서 0.05초를 주지 않았다면 힌트를 먼저 읽고, 초점이 간 요소를 읽게됩니다.

    그러나, 위 코드와 같이 polite 영역의 텍스트를 늦게 업데이트하게끔 설정하게 되면, 마치 VoiceOver나 Talkback에서 버튼 요소에 초점이 갔을 때 안내되는 것 처럼 읽게됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] accessibilityPaneTitle 응용하여 구현해보기
    Webacc NV 2020-08-13 14:13:18

    한 activity에 여러 fragment를 만들고 fragment로 화면 전환 시 accessibilityPaneTitle 속성 사용을 통해 접근성 개선이 가능하다는 것에 대해 공유했습니다.

    그런데 접근성 테스트를 하다보면 여러 화면이 하나의 fragment에서 오버레이 형태로 전환되는 경우도 종종 보곤 합니다.

    즉 각 화면이 activity, 혹은 fragment로 나뉘어져 있는 것이 아니라 하나의 activity 안에 중첩된 화면을 만들고 상황에 따라 표시/숨겨지도록 하는 경우를 말합니다.

    이런 경우에는 화면 전환 시 접근성 구현을 어떻게 해야 할까요?

    이 때도 accessibilityPaneTitle 속성을 활용할 수 있습니다.

    사용자가 다른 화면으로 전환하는 버튼을 누르면(OnClick) 전환된 화면에 표시되는 한 view에 setAccessibilityPaneTitle을 추가하면 됩니다.

    예시:

    public void onBanana(View view) {
        //implement
        final TextView text = (TextView)findViewById(R.id.textView2);
        Hide();
        text.setText("바나나입니다");
        text.setAccessibilityPaneTitle("banana screen");
    }
    
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 한 activity에 여러 fragment로 구성된 화면
    Webacc NV 2020-08-12 18:37:05

    아래 내용은 예전에 널리 블로그 아티클로는 한번 포스팅한 적이 있지만 요약하여 해당 팁 코너에 다시한번 공유합니다.

    한 activity 안에 여러 fragment들이 있어서 화면 전환이 fragment 단위로 이루어 지는 경우에는 추가적인 접근성 구현을 하지 않으면 TalkBack에서 화면이 전환되는 것을 읽어주지 못합니다.

    따라서 각 fragment xml 파일의 LinearLayout과 같은 루트에 다음과 같이 마크업하여 스크린 리더 사용자가 화면 제목을 바로바로 인지할 수 있도록 도움을 줄 수 있습니다.

    android:accessibilityPaneTitle = "검색 화면"

    단 탭레이아웃, ViewPager class를 활용한 멀티 페이지뷰의 각 fragment에는 accessibilityPaneTitle 속성을 추가하지 않아도 됩니다. 

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 같은 화면 상에서 다른 요소로 초점 보낼 때 참고 사항
    Webacc NV 2020-08-11 19:11:40

    같은 화면 상에서 modal이 표시/닫히거나 화면 상의 레이아웃이 변경되었을 때 접근성을 고려하기 위해 초점을 해당 view로 보내주는 경우가 많습니다.

    이 때 다음의 두 메소드 중 하나를 사용하게 됩니다.

    UIAccessibility.post(notification: .screenChanged, argument: 초점 보내고자 하는 요소)

    혹은

    UIAccessibility.post(notification: .layoutChanged, argument: 초점을 보내고자 하는 요소)

     

    그런데 이미 view가 표시된 상태에서 해당 메소드를 사용하면 별 문제가 없지만 숨겨졌던 view가 다시 표시되는 경우에는 시간차로 인해 초점이 제대로 이동하지 못하는 경우가 종종 있습니다.

    이런 경우에는 약 0.7초 정도 딜레이를 주면 대부분의 문제는 해결이 됩니다.

    예시:

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {

    UIAccessibility.post(notification: .layoutChanged, argument: object)

    }

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] activity에 setTitle="" 추가와 관련하여
    Webacc NV 2020-08-10 20:46:23

    안드로이드에서는 각 화면을 activity로 구현하면 activity가 실행되면서 TalkBack이 activity label을 읽는다고 말씀드렸습니다.

    그런데 만약 특정 activity에서 onCreate 메소드 안에 setTitle="" 라고 코드를 작성하면 해당 activity가 실행되더라도 TalkBack은 화면 제목을 말하지 않을 뿐더러 화면 변경 사운드도 출력하지 않게 됩니다.

    거의 대부분 해당 메소드는 사용할 필요가 없지만 예외적으로 음성 검색 화면에서는 이 방법을 사용할 수 있습니다.

    왜냐하면 음성 검색이 실행되면서 마이크가 바로 켜 지는 경우에는 TalkBack이 화면의 제목을 읽는 것은 오히려 방해가 되기 때문입니다.

    다음 팁에서는 음성 검색에서의 초점 처리에 대해 다루겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] performAccessibilityAction 간단히 살펴보기
    Webacc NV 2020-08-06 17:47:21

    요 며칠 SeekBar의 접근성에 대해 다루면서 performAccessibilityAction 메소드를 언급했습니다.

    오늘은 이 메소드의 역할에 대해 알아보려고 합니다.

    웹개발 관점에서 접근하면 설명은 간단해집니다.

    우리는 접근성을 고려할때 마우스 사용자 뿐만 아니라 키보드 사용자도 고려해야 합니다.

    버튼, 링크, 라디오버튼, 체크박스 등을 구현할 때 HTML의 네이티브 컨트롤을 사용하면 키보드 접근성을 따로 구현할 필요가 없습니다.

    이는 각 태그들을 적용하면 브라우저에서 자체적으로 키보드 접근성을 지원하고 있기 때문입니다.

    그러나 버튼을 div 요소로 구현을 한다고 가정해 봅시다.

    그러면 스타일 및 기능은 버튼이지만 브라우저 입장에서는 버튼이 아닌 div일뿐입니다.

    따라서 접근성 구현을 위해 스크립트로 버튼에 대한 키보드 액션을 반드시 추가해 주어야 합니다.

    마찬가지로 안드로이드에서도 TalkBack을 사용할 때 네이티브 컨트롤을 사용하면 각 컨트롤마다 그 컨트롤들을 사용자가 조절할 수 있는 자체 액션들이 추가됩니다. 

    그러나 SeekBar에서 다룬 바와 같이 표준 컨트롤을 사용하지 못하거나 기본적인 접근성 API가 지원하는 부분이 아쉬운 경우 접근성 액션에 따른 추가 이벤트를 구현할 수 있는데 이것을 performAccessibilityAction이라고 합니다.

    여기서의 action은 사용자가 TalkBack을 통해서 수행하는 모든 제스처들입니다. 

    예를 들어 SeekBar 컨트롤에서 사용자가 볼륨키로 값을 조절하면 TalkBack에서 scroll forward 및 backward 액션을 실행하도록 되어 있습니다.

    따라서 볼륨 키를 조절할 때 값이 변경되지 않는다면 이 scroll forward, backward에 대한 acton 이벤트를 구현해 주어야 하는 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML Native & Screen Reader] 제목 태그에 다른 태그를 넣으면 생기는 일
    Webacc NV 2020-08-05 15:30:31

    웹 페이지 중에는 제목에 많은 정보를 담고자 span이나 em 등의 태그를 넣는 사례를 자주 볼 수 있습니다.
    제목 태그에 자식 태그를 넣는 것이 금지된 것은 아니나, 자식 태그가 있는 헤딩 요소와 없는 요소를 스크린리더로 탐색할 때는 매우 다르게 인식하게 됩니다.

    <h3>OO 검색 결과 <em>59</em></h3>

    다음의 코드는 시각적으로 아무 문제 없이 한 줄로 표시될 것입니다. 하지만 스크린리더 사용자가 가상커서를 켠 상태로
    위/또는 아래 화살표 키로 웹을 볼 때, "검색결과, 헤딩 레벨3"과 "59, 헤딩 레벨3"이 따로 읽힐 것입니다.

    이는 스크린리더의 특성중 하나로, span과 같이 네이티브 HTML 태그를 넣었을 때, 마치 헤딩이 두 개인 것처럼 읽히는 현상을 보입니다.
    웹을 스크린리더로 오랫동안 사용해왔던 사람은 이에 대해 금방 인지할 것이나, 웹을 사용한지 얼마 안된 초심자의 경우에는
    한 개의 헤딩 태그임에도 마치 두 개의 헤딩이 제공된 것처럼 인지할 수 밖에 없습니다.

    이를 해결하기 위해서는 자식 요소로 사용될 태그에 role="none" 속성을 삽힙해야 합니다.
    우선 em이나 strong은 시멘틱 태그로, role="none"으로 숨기는 것 자체가 그리 바람직하지 아닙니다.
    하지만 role="none"으로 숨기지 않은 경우에는 스크린리더와 브라우저 환경에 따라 버그가 발생할 수도 있습니다.
    따라서, 두 줄로 읽히는 문제 이전에 버그를 방지하기 위해서는 이와같이 role="none"을 반드시 적용해 주어야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] SeekBar 구현 시 stopTrackingTouch 메소드와 TalkBck 관련
    Webacc NV 2020-08-05 11:15:00

    스크린 리더 사용자가 SeekBar 컨트롤에서 볼륨키를 사용하여 값을 조절해도 실제 반영이 안 되는 경우를 보곤 합니다.

    이것은 어떤 이유 때문일까요?

    SeekBar 구현시에는 일반적으로 3개의 메소드를 오버라이드 합니다.

    1. onProgressChanged

    2. onStartTrackingTouch

    3. onStopTrackingTouch

    위의 3개의 메소드를 활용해서 사용자가 값 조절을 시도하면 이에 대한 액션 이벤트를 구현하게 됩니다.

    보조기술을 사용하지 않는 사용자는 SeekBar를 터치한 상태로 값을 조절합니다.

    그러나 보조기술을 사용하는 경우에는 보조기술에서 지원해주는 방법을 통해서도 값을 조절합니다.

    그런데 문제는 사용자가 값을 터치한 상태로 조절할 경우에만, 즉 onStart 혹은 onStop 메소드(터치 시작 혹은 끝 동작)이 실행되는 경우에 이벤트가 발생하도록 구현하는 경우가 많다는 것입니다.

    이렇게 구현하면 보조기술을 사용하여 값을 조절하는 것은 터치 동작이 아니므로 값 반영이 안 됩니다.

    따라서 특별한 경우가 아니라면 터치 동작이 실행되거나 종료될 때가 아닌, 값이 변경될 때 이벤트가 발생할 수 있도록 구현이 필요합니다.

    그럼에도 불구하고 반드시 터치 동작이 실행, 혹은 종료될 때 이벤트를 발생시켜야 한다면 이 때도 performAccessibilityAction 메소드 안에서 이를 해결할 수는 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [웹브라우저] 최근 변경된 크롬의 초점 표시 변경 관련
    Webacc NV 2020-08-04 10:43:55

    웹페이지를 탐색할 때 사용자의 상황에 따라 마우스 뿐만 아니라 키보드 탭 혹은 스위치 디바이스를 사용하여 페이지를 탐색하는 경우도 많습니다.

    따라서 링크, 버튼과 같이 상호작용이 가능한 요소에는 반드시 키보드로 접근하더라도 초점이 시각적으로 표시되어야 하는 것은 접근성의 기본 중의 기본입니다.

    예전 크롬 버전까지는 기본 초점을 연한 파란색으로 표시를 했는데 이때 비슷한 계열의 배경색을 사용하는 경우에는 사용자가 초점을 찾기 어렵다는 문제가 있었습니다.

    이를 해결하기 위해 최신 크롬 버전부터는 기본 초점이 두 겹의 윤곽선으로 변경되었습니다. 안쪽 윤곽선은 검정색이며, 바깥 윤곽선은 흰색으로, 가장 많이 사용하는 검정색 배경을 사용하는 웹페이지와 흰색 웹페이지에서 문제 없이 초점을 확인할 수 있게 되었습니다.

    댓글을 작성하려면 해주세요.
  • tip
    웹 접근성 향상을 위한 기획의도 분명히 하기
    에어류 2020-08-03 18:31:08

    안녕하세요! 에어류입니다. 

    오늘은 웹 접근성을 적용할 대상에 대해 정확하게 기획자가 의도에 맞추어 콘텐츠를 제공해주는 지침 외의 이야기를 소개할까합니다. 

     

    우선 아래 그림을 한번 보시겠습니다.

    제8회 삼성전기 인사이트 엣지 논문대상 공모 포스터

    위에 이미지에 대한 대체텍스트를 제공하려고 할 때 갑자기 헷갈리실 수 있습니다.

    '어? 안에 내용을 다 써야 하나?"

     

    대체텍스트를 제공할 때 생략하다가 각종 평가에서 쓴맛을 보신분들은 되도록 보여지는 텍스트에 대해서 다 기록하려고 하실 것 같습니다.

    결론부터 말씀드리면 해당 이미지의 대체텍스트는 "제8회 삼성전기 inside Edge 논문대상 공모 포스터"로 제공하시면 됩니다. 

    물론 추가적인 부연설명이 있으시다면 title로 제공하실 수 있습니다. 

     

    그런데 이 때 몇가지 글씨가 눈에 보이니 해당 텍스트가 정보를 주고 있다면 대체텍스트를 제공해야하지 않느냐 하는 논란이 있을 수 있습니다. 

    사실 포스터의 이미지 크기가 애매하게 자리를 차지하고 있고, 보이는 텍스트도 있고, 보이지 않는 텍스트도 있습니다. 

    그래서 기획자의 입장에서 웹 접근성 향상과 함께 여러가지 혼란을 막기 위해서는 이미지 크기를 정말 썸네일처럼 활용하셔서 아예 작게 하시거나 모든 포스터 내용의 정보를 보여주고자 한다면 더 크게 보실 수 있게 하시고 해당 대체텍스트를 제공해주시는 것이 좋습니다. 

    기획자는 콘텐츠를 제공할 때 그 콘텐츠가 단순하게 포스터의 장식적인 이미지로 제공할 것인지 구체적인 내용을 보여주기 위해 제공할 것인지를 먼저 판단하고 그 크기에 맞게 제공하실 때 접근성을 적용하고자 하는 그 취지를 쉽게 이해할 수 있어 웹 접근성 향상으로 이어질 수 있습니다. 

     

    색상으로 콘텐츠간의 의미를 주는 것처럼 보이나 의도가 분명치 않은 녹색경영추진 연혁 콘텐츠(연도별로 옅은 회색, 회색, 짖은회색, 녹색으로 그려진 것 같으나 콘텐츠 세부 내용에 색상이 통일되지 않아 결국 아무 색상적인 의미가 없는 연역 연대기)

     

     이 녹색경영 추진 연혁은 자세히 살펴보지 않으면 마치 연도별로 옅은 회색에서 회색, 짙은 회색에서 녹색으로 녹색경영이 강화되고 있는 것처럼 보입니다. 만약 그런 의도로 이미지가 제작되었다면 색상으로만 녹색경영이 강화되는 정보를 추가로 주고 있어 웹 접근성 준수 기준에 부합하는지 고민을 해야할 이미지가 될 수 있습니다. 그러나 자세히 보면 녹색의 표시가 연혁 전반에 있고, 해당 정보를 제공하는 텍스트가 모두 녹색으로 표시되어 색상이 주는 정보는 없습니다. 그럼에도 불구하고 평가오류를 일으킬만한 색상 사용이 있습니다. 

    기획자 또는 디자이너가 부분 강조를 위해 녹색을 사용하고 있지만 의미 부여가 혼란스럽네요. 

    콘텐츠의 의도를 분명히 하기 위해서 색상의 사용에도 조심스럽게 접근하면 더욱 좋겠습니다. 

     

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] SeekBar 클래스에 AccessibilityManager.interrupt() 메소드 적용 시 주의사항
    Webacc NV 2020-08-03 16:35:27

    며칠 전에 SeekBar를 스크린 리더 사용자가 조금 더 효율적으로 사용할 수 있는 접근성 팁을 공유했습니다.

    오늘은 interrupt 메소드 적용 시 주의해야 할 사항에 대해 공유하려고 합니다.

    지난 글에서 언급했듯이 progressBar가 증가 혹은 감소하는 이벤트가 발생할 때 interrupt 메소드를 사용하면 스크린 리더 사용자가 빠르게 퍼센트를 조절할 때 좀 더 자연스러운 조작이 가능합니다.

    그런데 조금 더 곰곰이 생각해 보면 한 가지 의문이 생깁니다.

    SeekBar는 퍼센트를 사용자가 직접 조절하는 경우도 있지만 음악 재생과 같이 자동으로 퍼센트가 증가하는 이벤트가 발생하는 SeekBar도 있습니다.

    이런 요소에 interrupt를 적용하면 어떻게 될까요?

    스크린 리더 사용자가 SeekBar가 아닌 다른 요소를 탐색하다가도 퍼센트가 변경되면 읽던 것을 interrupt 시켜 오히려 곤란한 상황이 발생합니다.

    그럼 이것에 대한 해결 방법은 무엇일까요?

    바로 performAccessibilityAction 메소드를 활용하는 것입니다.

    정리하면 재생과 같이 자동으로 SeekBar 퍼센트가 변경되는 요소에는 사용자가 퍼센트를 조절하는 경우에만 interrupt 이벤트가 실행되게끔 해야 합니다.

    기회가 될 때 performAccessibilityAction에 대해 다루어 보겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML] 버튼 텍스트 레이블 변경과 aria-live에 관하여
    Webacc NV 2020-07-31 13:38:16

    얼마전에 안드로이드 접근성 이벤트에 대해 기술하면서 사용자의 액션에 의한 텍스트 변경에 대해 살펴본 적이 있습니다.

    웹페이지에서도 스크린 리더 사용자가 키보드를 이용하여 버튼을 누르면 버튼의 텍스트가 변경되는 경우들을 종종 보곤 합니다.

    예를 들어 뮤직 플레이어에서 반복 컨트롤 버튼이 있다고 가정해 봅니다.

    '반복 해제됨'이라는 버튼 텍스트가 있다면 그 버튼을 누를 때마다 '한 곡 반복 설정됨', '전체 반복 설정됨' 등으로 레이블이 변경될 것입니다.

    기본적으로 스크린 리더에서는 버튼 요소의 속성 값(aria-label, value, aria-expanded 등)이 변경되면 특별히 접근성 구현을 하지 않더라도 변경되는 값이나 텍스트 정보를 읽어주게 됩니다.

    그러나 버튼 요소 자체의 텍스트가 변경되는 경우에는 변경되는 텍스트를 사용자에게 바로바로 읽어주지 못합니다.

    <button id="test">반복 해제됨</button>

    따라서 키보드 조작 등에 의해 버튼의 텍스트 정보가 변경되는 경우에는 aria-live 속성을 추가하여 변경되는 텍스트 정보를 바로바로 들을 수 있도록 접근성에 대한 고려가 필요합니다.

    그렇지 않으면 버튼을 눌러도 스크린 리더 피드백이 없으므로 버튼의 텍스트가 변경되었는지조차 알기 어렵습니다.

    물론 해당 구현을 하는 경우 스크린 리더와 브라우저의 특성에 따라 약간의 버그들이 있습니다.

    예를 들면 NVDA에서 크롬 브라우저로 테스트 했을 때 변경되는 aria-live 텍스트 값을 두 번식 읽는 경우가 발생하였으며

    페이지 로드 후 변경되는 텍스트 버튼에서 엔터를 눌러 aria-live 속성이 처음 추가되는 시점에는 변경된 텍스트 정보를 읽지 못하는 버그도 있습니다.

    그럼에도 불구하고 변경되는 텍스트를 읽어주도록 구현하는 것은 매우 중요하므로 해당 부분을 고려해 보시면 좋겠습니다.

    단 aria-live 속성을 사용하지 않으려면 위에서 언급한 aria-label을 사용할 수 있으며 aria-label="전체 반복 설정됨" 등과 같이 마크업 할 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] SeekBar와 AccessibilityManager class의 interrupt 메소드
    Webacc NV 2020-07-30 12:55:42

    앞으로 두 번에 걸쳐 SeekBar에 대한 팁을 공유하려고 합니다.

    SeekBar는 접근성에 문제가 없는 이상 TalkBack 사용자는 볼륨키를 사용해서 밸류 값을 조절하는 경우가 많습니다.

    그런데 볼륨키로 값을 아주 빠르게 조절하게 되면 볼륨 키를 누를 때마다 기존에 읽던 내용을 멈추고 새로 갱신된 값을 읽어주어야 하는데 기존에 읽던 밸류 및 레이블 정보를 다 읽고 갱신된 정보를 읽습니다.

    따라서 갱신된 정보를 듣기 위해 스크린 리더 사용자는 어느 정도 기다려야 하는 불편함이 발생합니다.

    게다가 SeekBar에 적용된 대체 텍스트 혹은 labelFor 정보가 긴 경우에는 이러한 불편함은 더 크게 느껴질 것입니다.

    이를 해결하기 위해 우리가 사용할 수 있는 메소드가 interrupt 입니다.

    interrupt 메소드를 적용하면 이벤트가 연속적으로 발생할 때 접근성 서비스가 기존에 읽던 내용을 말 그대로 가로채고 새로 갱신된 내용을 읽도록 하는 것입니다.

    따라서 이것을 다음과 같이 적용할 수 있겠습니다.

    public void onProgressChanged(SeekBar seekBar, int progresValue, boolean fromUser) {
    
    final AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
        accessibilityManager.interrupt();
        progress = progresValue;
    }
    댓글을 작성하려면 해주세요.
  • tip
    HTML] aria-live, display:none, 그리고 display:block 관련
    Webacc NV 2020-07-29 16:15:41

    웹페이지 환경에 따라 사용자의 키보드 엔터와 같은 액션이 있을 경우 페이지가 새로고침되지 않고 display:none으로 숨겨진 '전송 완료'와 같은 텍스트가 display:block 되면서 화면에 표시되는 경우가 있습니다. 

    이때 접근성을 구현하기 위해서 aria-live 속성을 적용하게 됩니다.

    물론 role="alert" 속성을 사용해도 되지만 센스리더 사용자의 환경까지 고려한다면 aria-live 속성이 더 범용적입니다.

    그렇지 않으면 스크린 리더 사용자가 화면에 나타난 텍스트가 있는지 알지 못하기 때문입니다.

    그런데 이때 유의해야 할 것은 반드시 aria-live는 display:none에서 block이 되는 요소 상위에 있어야 한다는 것입니다. 

    만약 display:block으로 변경되는 요소 자체에 aria-live 속성이 있으면 이를 스크린 리더가 읽어주지 못합니다.

    다음은 접근성이 잘 적용된 예시라 할 수 있습니다.

    <div aria-live="polite">

        <p id="paragraph" style="display: none;">This is a new paragraph.</p>

       </div>

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 확장/축소 접근성 구현 관련
    Webacc NV 2020-07-28 18:39:46

    앱을 구현하다보면 특정 요소를 탭했을 때 이에 해당하는 하위 콘텐츠가 확장되거나 축소되는 객체들을 만드는 경우들이 있습니다.

    예를 들어 과일 리스트 버튼을 탭하면 그 아래로 사과, 오렌지, 배 등의 과일들이 표시되고 다시 한번 더 과일 리스트 버튼을 누르면 하위 요소들이 숨겨지는 UI 입니다.

    그런데 이때 확장/축소가 되는 버튼에 추가 접근성 구현을 하지 않으면 스크린 리더 사용자는 이중탭했을 때 해당 요소가 확장이 되는 것인지 축소가 되는 것인지를 알 수 없습니다.

    안드로이드에서는 이를 지원하기 위한 메소들 제공하고 있는데 

    (AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE) 혹은 EXPAND 입니다.

    이것을 AccessibilityNodeInfo 정보에 추가해 주면 축소된 경우에는 접음, 확장된 경우에는 펼침 이라고 음성을 출력해 주게 됩니다.

    마치 HTML에서 aria-expanded 속성을 추가한 것과 비슷합니다.

    다만 이 AccessibilityAction을 제대로 적용하려면 performAccessibilityAction 메소드를 오버라이드 하여 expand, collapse 속성에 따른 확장/축소 구현을 한 다음 이를 onClick과 같은 요소에 적용해 주어야 합니다. 

    조만간 이에 대한 아티클을 발행해 보도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [메일] Windows 오피스 365 아웃룩에서 제공하는 대체 텍스트 삽입
    Webacc NV 2020-07-27 14:53:26

    스크린 리더 사용자 입장에서 여러 회사에서 발송되는 메일을 받았을 때 느끼는 접근성 이슈 중 하나는 대부분 메일의 이미지에 대한 대체 텍스트가 삽입되어 있지 않다는 것입니다.

    메일도 하나의 웹페이지에 해당하므로 이미지에 대한 대체 텍스트 삽입은 웹피이지 뿐만 아니라 메일에서도 중요합니다.

    그런데 메일 작성 시 오피스 365 아웃룩을 사용하게 되면 메일 프로그램 내에서 이미지에 대한 대체 텍스트 삽입이 가능합니다.

    첨부한 이미지를 선택하고 마우스 오른쪽 버튼 혹은 팝업키를 누르면 대체 텍스트 편집 혹은 삽입이 있습니다.

    오피스 아웃룩을 사용하신다면 해당 기능을 이용해 보시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML Native & Screen Reader] 가상커서와 title 속성
    Webacc NV 2020-07-23 11:52:11

    input, button과 같은 form의 컨트롤 요소, 링크 등과 같이 키보드의 tab 키를 눌러 이동할 수 있는 요소에는 title 속성을 작성할 수 있습니다.

    title 속성이 제공된 컨트롤에 마우스 포인터가 올라가면 말풍선을 제공하며, title 속성에 작성된 텍스트가 표시됩니다. 이처럼 스크린리더를 사용할 때, Tab 키로 title이 있는 컨트롤로 초점을 이동하면 스크린리더가 타이틀 텍스트를 읽게됩니다.

    이 title 속성은 국내에서는 링크의 새 창 안내와 같은 이슈로 인해 앵커 태그에 "새 창" 이라는 텍스트로 많이 사용중인 것을 볼 수 있습니다. 하지만, title 속성에는 맹점이 있습니다. 

    HTML에서 컨트롤 요소가 아닌 기본 요소는 초점이 없습니다. 따라서 커서 브라우징과 같은 별도의 키보드 접근성 기능을 사용하지 않고서는 키보드로 텍스트에 접근할 수 없습니다. 이와 마찬가지로 스크린리더 사용자들은 가상커서를 통해 텍스트에 접근하는데, 당연히 이 가상 커서로 버튼과 같은 컨트롤에 접근할 수 있습니다.

    하지만, 시스템 커서와 스크린리더의 가상 커서는 별도의 프로그램이기 때문에 방향키나 스크린리더의 탐색 단축키로 이동하면 스크린리더 특성으로 인해 Tab 키로 컨트롤에 접근한 것과 다르게 읽습니다.

    그 특성때문에 생기는 대표적인 문제 중 하나가 화살표 키나 스크린리더의 빠른 탐색 단축키를 사용하여 컨트롤에 접근하면 title 속성을 읽지 않는다는 것입니다. 추가로 aria-describedby도 이와 동일한 문제를 갖고 있습니다.

    따라서 중요한 내용을 스크린리더 사용자에게 전달하고자 할 때에는 title 속성이나 aria의 describedby보다는 요소의 특성에 따라 숨김 텍스트나 live region 같은 대체 수단을 사용하는 것이 바람직힙니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] setSelected 메소드와 스크린리더 관련
    Webacc NV 2020-07-22 17:08:10

    일반적으로 setSelected 메소드는 여러 요소 중 하나가 선택되었을 때 사용합니다. 

    예를 들어 과일 리스트, 채소 리스트, 육류 리스트 중 하나가 선택되어 있다면 선택된 요소에 setSelected="true" 속성을 사용하게 됩니다.

    setSelected true가 적용되면 TalkBack에서 해당 요소에 포커스 했을 때 '선택됨'이라고 읽어줍니다.

    그런데 setSelected 속성이 선택됨, 즉 상태정보를 표시하는 요소가 아닌 경우에도 사용되는 경우를 가끔 보곤 합니다.

    그렇게 되면 스크린 리더 사용자는 해당 요소에 포커스 했을 때 선택됨 이라는 상태정보를 듣게 되어 레이아웃 파악이 혼란스럽습니다.

    따라서 setSelected 속성은 상태정보를 나타내는 영역에만 사용하는 것이 좋습니다.

    만약 부득이 상태정보를 나타내는 요소가 아님에도 사용해야 한다면 접근성 API가 해당 속성을 무시할 수 있도록 AccessibilityNodeInfo 정보 수정이 필요합니다.

    AccessibilityNodeInfo 정보를 수정하려면 수정하고자 하는 객체에 setAccessibilityDelegate 객체를 만들어 onInitializeAccessibilityNodeInfo 메소드를 선언한 다음 info.setSelected="false"와 같이 수정할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 화면 상단의 메인 제목을 머리말로 읽어주지 않는다면
    Webacc NV 2020-07-21 18:52:59

    안드로이드 플랫폼과 달리 iOS에서는 각 화면에 제목이 표시되는 경우 제목 요소는 VoiceOver가 '머리말'이라고 읽어주는 것이 스크린 리더 사용자에게 자연스럽습니다.

    접근성을 테스트 하다보면 화면 메인 제목을 '머리말'이라고 읽어주지 않는 경우를 종종 보곤 합니다.

    NavigationController를 사용하여 각 화면에 제목을 지정하는 경우에는 특별한 작업을 하지 않아도 VoiceOver가 제목 요소로 인식하여 '머리말'이라고 읽어줍니다.

    그러나 UILabel을 사용하여 커스텀 제목을 삽입하면 당연하게도 UILabel은 일반 텍스트 요소이므로 화면의 제목임을 VoiceOver가 인식하지 못합니다.

    따라서 화면 메인 제목이 UILabel이라면 UIAccessibilityTrait header를 사용함으로써 좀 더 의미에 맞는 요소 유형을 제공할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • news
    톡백 베타 버전에 추가된 multi finger gesture 도입 및 기타 기능 추가에 관하여
    Webacc NV 2020-07-20 18:53:28

    현재 안드로이드 11 베타 버전이 공개되어 픽셀 단말기를 가지고 있다면 베타 버전을 사용해볼 수 있습니다.

    현재 베타 버전에는 TalkBack 9점때 버전이 탑재되어 있는데 TalkBack 개발자 설정에서 multi finger 제스처 기능을 활성화 할 수 있습니다.

    이렇게 되면 기존 아이폰에서 사용하던 두 손가락 두 번탭/세 번탭/두 번탭하고 길게 누르기, 세 손가락, 네 손가락 관련 여러 손가락 제스처를 활용할 수 있습니다.

    갤럭시폰에 탑재된 VoiceAssistant에서 사용 가능했던 제스처들을 TalkBack에서도 사용 가능하다고 보시면 됩니다.

    또한 할당할 수 있는 제스처에도 예전에는 없던 음성 피드백 중지, 미디어 일시정지 및 재생 기능이 추가되었습니다.

    현재 안드로이드 베타 버전이 업데이트 될때마다 기능이 추가되고 있으므로 실제 버전이 출시되었을 때를 기대해 보아도 좋을 것으로 보입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML 공통] WAI-ARIA로 탭컨트롤 구현 시 탭패널 시작 콘텐츠가 텍스트인 경우
    Webacc NV 2020-07-17 14:29:52

    탭키를 눌러 WAI-ARIA로 구현된 탭컨트롤에 포커스 하면 스크린 리더의 가상커서가 자동으로 꺼집니다.

    이는 과일 리스트, 채소 리스트, 육류 리스트와 같은 각 탭 요소 이동은 탭키가 아닌 화살표 키를 이용하도록 하고 있기 때문입니다.

    그리고 다시 탭키를 누르면 탭 본문 콘텐츠로 이동하고 스크린 리더 가상커서는 자동으로 켜집니다.

    탭 본문 시작이 '사과 링크, 포도 링크'와 같이 포커스가 가능한 요소이면 탭을 눌렀을 때 링크가 탭 본문의 첫 번째 내용이므로 문제가 없습니다. 

    그러나 탭 본문 시작이 '과일은 우리의 건강에 많은 도움을 줍니다'와 같이 텍스트라면 탭 컨트롤에서 탭키를 누르는 순간 탭 본문 콘텐츠의 첫 텍스트가 아닌 다른 영역의 링크 요소로 포커스 되기 때문에 본문 시작지점으로 이동하기 위해 여러번 키조작을 해야 한다는 문제가 있습니다.

    기본적으로 텍스트 콘텐츠에는 탭키로 포커스 할 수 없는 것이 일반적이지만 탭 콘텐츠가 텍스트로 시작한다면 예외적으로 텍스트 시작 지점에 tabindex="0" 속성을 주어 포커스가 가능하도록 구현해 주는 것이 스크린 리더 사용자의 효율적인 웹 탐색을 도울 수 있습니다.

    단 tabindex는 본문 콘텐츠가 시작되는 div 영역에 주기보다는 첫 번째 텍스트가 들어가는 p 요소에 주는 것이 좋습니다.

    이는 현재 안드로이드 TalkBack의 경우 탭패널, 즉 탭 본문 콘텐츠에 aria-labelledby 속성이 포함되고 본문 전체를 감싸는 div에 tabindex를 주는 경우 탭콘텐츠 내의 텍스트 영역을 읽지 못하는 문제가 발생하기 때문입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 사용자의 이중탭에 의해 버튼의 텍스트가 변경되는 경우
    Webacc NV 2020-07-16 18:42:20

    사용자가 특정 객체를 이중탭했을 때 이중탭한 요소의 텍스트가 변경된다면 스크린 리더가 변경된 내용을 바로 읽을 수 있도록 접근성을 구현해 주어야 합니다.

    대부분 추가 작업을 해 주지 않아도 TalkBack에서 알아서 변경된 내용이나 상태정보를 읽어주는 것들이 많지만 그렇지 못한 상황도 있습니다.

    재생/일시정지, 랜덤재생/순차 재생과 같이 사용자가 탭할 때마다 버튼의 텍스트가 변경되는 객체가 있다고 가정해 보겠습니다.

    객체의 텍스트가 대체 텍스트, 즉 contentDescription을 사용하여 구현한 경우라면 스크린 리더 사용자가 이중탭할 때마다 바뀐 텍스트 내용을 자동으로 읽습니다.

    그러나 화면에 보여지는 텍스트, 즉 android:text로 구현한 경우에는 이중탭을 해도 바뀐 텍스트 내용을 읽어주지 못합니다.

    이를 해결하려면 화면의 텍스트가 변경되었을 때 sendAccessibilityEvent 메소드의 컨스턴트 중에서 TYPE_VIEW_SELECTED를 사용하면 됩니다.

    지난 번에 소개해 드린 TYPE_VIEW_FOCUSED 컨스턴트와 다른 점은 SELECTED를 사용하면 변경된 텍스트만 읽게 된다는 것입니다.

    즉 랜덤 재생 버튼에 포커스한 상태에서 이중탭했을 때 순차 재생으로 변경되었다면 스크린 리더 사용자에게 필요한 정보는 '순차 재생 버튼'이 아니라 '순차 재생'이므로 TYPE_VIEW_SELECTED 컨스턴트를 사용하는 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] iOS 14에서 TabBar 구현시 VoiceOver가 읽어주는 방식 변경 관련
    Webacc NV 2020-07-15 18:18:45

    iOS 14에서는 TabBar를 구현할 경우 VoiceOver 포커스가 탭 영역에 진입하면 탭막대라고 음성을 출력하여 영역에 대한 정보를 조금 더 자세하게 알려줍니다.

    예를 들어 하단 탭바에 과일 리스트, 채소 리스트, 육류 리스트가 있고 VoiceOver 포커스가 일반 콘텐츠에서 탭 컨트롤 영역에 진입하면 영역 정보가 변경되었으므로 '탭막대, 과일리스트'라고 음성을 출력합니다.

    물론 아직은 개발자 버전이라 버그도 있습니다. 

    일반 콘텐츠에서 탭바로 진입할 경우에는 현재 선택된 탭 정보의 선택됨 상태정보를 읽지 못합니다.

    이 문제는 정식 업데이트 때에는 해결되길 바라봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] VoiceOver 포커스가 시간, 배터리 등이 표시되는 상태바 영역으로 날아가는 접근성 이슈 해결하기
    Webacc NV 2020-07-14 12:13:34

    iOS 앱 접근성을 테스트하다보면 가끔 VoiceOver 포커스가 의도치 않게 배터리, 시간 등을 표시하는 상태 막대 영역으로 튀어버리는 경우를 발견하곤 합니다.

    해당 이슈에 대한 원인은 기존에 포커스하고 있던 A 요소가 숨겨져서 VoiceOver에서 더 이상 A 요소에 포커스를 할 수 없게 되었을 때 VoiceOver 포커스가 이동할 곳에 대한 포커스 이벤트 처리를 하지 않았기 때문에 발생하는 것입니다.

    기본적으로 특정 요소가 화면에서 사라지면 VoiceOver는 포커스를 잃어버리게 되는데 이때 바로 위의 포커스 할 곳을 찾아 이동합니다.

    이때 다행히 사라진 요소 위에 ㄷ른 포커스할 요소가 있다면 다행이지만 그렇지 않으면 있을 곳이 없어 산태바 영역으로 포커스가 이동하는 것입니다.

    따라서 사용자의 인터랙션이나 혹은 자동으로 특정 요소가 화면에서 사라지는 경우에는 layoutChangedNotification 메소드를 활용하여 VoiceOver가 새롭게 포커스할 대상을 지정해 주는 것이 좋습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML] 여러 <ul> 및 <li> 요소를 사용한 내비게이션 형 링크 제공 시
    Webacc NV 2020-07-13 17:09:24

    웹문서에서 일반적으로 탭키를 누르면 링크나 버튼, 편집창, 라디오버튼과 같은 상호작용이 가능한 컨트롤에만 포커스 됩니다. 

    따라서 스크린 리더 사용자는 내비게이션 위주로 페이지 탐색시 탭키를 주로 사용하며 텍스트를 읽을 때는 스크린 리더에서 제공하는 가상커서를 사용하게 됩니다.

    그런데 특정 페이지에 과일, 채소, 육류 리스트와 같은 여러 <ul> 그룹이 있고 각 ul의 li 요소는 링크를 포함하고 있다고 가정할 때 본 영역은 내비게이션 영역이므로 주로 탭키를 사용하는 경우가 많습니다.

     

    예:

    <p>과일리스트</p>

    <ul>

     <li><a href="http://www.example1.com">사과</a></li>

     <li><a href="http://www.example2com">오렌지</a></li>

    </ul>

    <p>육류 리스트</p>

    <ul>

     <li><a href="http://www.example3.com">삼겹살</a></li>

     <li><a href="http://www.example4.com">등심</a></li>

    </ul>

     

    그런데 위의 마크업 예제로 된 목록을 탭키를 눌러 탐색할때 스크린 리더가 읽어주는 내용은 

    1. 각 링크의 레이블과 링크라는 컨트롤 요소.

    2. 목록 안에 있다는 정보. 

    3. 그 목록에서 몇 번째 요소인지에 대한 정보

    입니다.

    문제는 어떤 목록인지, 즉 과일 리스트인지, 육류 리스트인지 등에 대한 목록 제목을 알 수 없다는 것입니다.

    어떤 목록인지 알기 위해서는 탭키로 탐색하다가 다시 위쪽 화살표키를 이용하여 목록의 제목 레이블을 확인해야 합니다.

    이를 해결하는 것은 간단합니다.

    목록의 제목에 해당하는 요소에 id를 부여한 다음 각 ul 요소에 aria-labelledby="부여한 아이디"와 같이 연결만 해 주면 됩니다.

    이렇게 하면 탭키를 누르는 것만으로 목록의 첫 링크를 읽을 때 어떤 목록인지에 대한 제목을 함께 읽어주어 스크린 리더 사용자에게 편리합니다.

    샘플 페이지에서 테스트해보기

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] ImageView의 접근성 처리에 관하여
    Webacc NV 2020-07-10 18:23:33

    HTML에서 <img> 요소를 활용한 화면의 레이아웃 장식을 위해 사용된 이미지에는 대체 텍스트를 삽입할 필요가 없으며 이를 보조기술에서 숨기기 위해 일반적으로 alt="" 속성을 사용합니다.

    레이아웃을 위해 표시되어 콘텐츠 맥락과는 아무런 관계가 없는 이미지를 일일이 스크린 리더가 읽는 것도 탐색의 효율성이 떨어지기 때문입니다.

    이것은 모바일앱에도 그대로 적용되며 각 플랫폼에 따라 구현 방법이 다릅니다.

    그런데 안드로이드의 경우 클릭 이벤트가 없고 ImageView 요소에 contentDescription 텍스트를 삽입하지 않으면 TalkBack에서 해당 ImageView에는 포커스 자체가 되지 않습니다.

    따라서 장식용 이미지를 표시하기 위해 ImageView가 사용되었다면 contentDescription 자체를 삽입하지 않는 것만으로 보조기술에서 숨기는 것이 가능합니다. 

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 하나의 버튼에 이미지와 텍스트가 분리된 경우 접근성 이슈 및 해결방법
    Webacc NV 2020-07-09 11:12:51

    스크린 리더 사용자가 한 손가락 오른쪽 혹은 왼쪽 쓸기 제스처를 통해 앱을 탐색하다보면 버튼과 텍스트의 초점이 두 개로 분리되어 탐색되는 경우를 종종 보곤 합니다.

    연필 모양의 버튼과 편집 텍스트의 스크린 리더 포커스가 분리되는 캡처화면

    예를 들어 편집 버튼이 있다고 가정한다면 첫 번째 요소는 버튼, 두 번째 요소는 편집에 포커스가 되는 것입니다.

    이런 경우 '버튼'이라고 읽어주는 요소와 '편집'이라고 읽어주는 요소가 하나가 아닌 두 개의 다른 요소로 인식할 수 있으며 버튼 자체에도 대체 텍스트가 없으므로 문제가 됩니다.

    이를 해결하는 방법에는 두 가지가 있습니다.

    스토리 보드 사용 시:

    1. 텍스트 객체, 즉 UILabel은 identity inspector의 accessibility 섹션에서 AccessibilityElement 속성을 체크 해제합니다. 이렇게 하면 VoiceOver에서 더 이상 해당 레이블은 포커스 하지 않게 됩니다.

    2. 이미지 버튼 요소의 identity inspector > accessibility 섹션에서 accessibilityLabel 입력란에 초점이 가지 않게 된 UILabel의 텍스트를 입력합니다.

    위와 같이 구현하면 '편집 버튼'과 같이 하나의 초점으로만 포커스 되므로 접근성 이슈가 해결됩니다.

    Swift 코드를 사용하는 경우:

    1. 스토리보드와 마찬가지로 UILabel 객체를 스크린 리더가 포커스 하지 못하도록 하기 위해 isAccessibilityElement = false 속성을 삽입합니다.

    2. 이미지 버튼 객체에 accessibilityLabel을 추가합니다.

    댓글을 작성하려면 해주세요.
  • tip
    CSS3의 filter속성으로 간편하게 사용불가(disabled) 스타일을 만들어보세요.
    Webacc NV 2020-07-07 18:03:10

    disabled나 aria-disabled 속성은 이름만 봐도 알 수 있듯 무언가 동작이 불가능하다는 의미를 가지고 있는 상태 정보입니다.

    한국판 NVDA나 센스리더에서는 "사용 불가", 영문판에서는 "Unavailable"이라고 스크린리더 사용자에게 친절하게 안내해줍니다.

    게시판 하단의 "이전 페이징" 또는 "다음 페이징" 버튼을 예로 들 수 있는데, 더 이상 이전 또는 다음 페이징 링크 목록이 없으면 사용자에게 "더 이상 뒤로 갈 수 없어요!"라고 알려줘야할 때 사용합니다. 만약, 기능을 사용할 수 없는 상태의 요소에 이 속성이 없다면, 스크린 리더 사용자는 사용가능한 컨트롤으로 인식하고 누르게됩니다.

    반대로, disabled나 aria-disabed를 사용하여 스크린리더 사용자를 고려하였으나, 한 눈에 두드러지는 스타일을 사용하지 않아서 구분할 수 없는 페이지가 종종 보입니다. 이러한 페이지는 시각적으로 페이지를 사용하는 모든 사용자에게 적절하지 않습니다.

    그래서 활성화된 버튼과 비활성화된 버튼은 두드러진 차이가 있어야 합니다. 일일히 디자인을 새로 줄 수도 있지만, CSS3의 filter 속성과 함수를 사용하면 손쉽게 disabled 상태를 시각적으로 구현할 수 있습니다.

    메인: filter 속성으로 disabled를 손쉽게 표현하기

    filter 속성에는 여러 함수가 있습니다. 이 함수중에서 disabled 상태의 요소를 만들 때 유용하게 사용할 수 있는 요소는 다음과 같습니다.

    • contrast() : 값은 int값으로 0부터 소수점단위로 설정가능합니다. 높을 수록 색상의 대비가 높아집니다. 기본값은 1입니다.
    • brightness() : 역시 위와 같이 int값을 사용하며, 기본값은 1입니다. 높을수록 밝아집니다.
    • opacity() : 알파(투명도) 필터입니다. int를 사용하며, opacity로 대체 가능합니다. 최대값은 1이며 기본값이기도 합니다.
    • grayscale() : 그레이스케일(색조) 필터입니다. int를 사용하며, 위의 함수들과 반대로 높을수록 회색에 가까워집니다.

    이 중 한 가지만을 사용하여도 충분히 disabled 상태와 걸맞는 디자인을 줄 수가 있으며, 다음과 같이 동시에 여러 개의 함수를 쓸 수 있습니다.

    .filter:disabled,.filter[aria-disabled=true]{
        filter: grayscale(0.3) opacity(0.4) brightness(1.2) contrast(0.7);
    }

    이를 적절히 사용하여 버튼이나 라디오 버튼과 같은 컨트롤 요소에 disabled 상태를 색상을 손대지 않고 만들 수 있습니다.

    filter 속성은 대부분의 브라우저에서 지원하기 때문에 웹 디자이너는 간편한 방법으로 만들 수 있다는 점에서 만족할 수 있으며, 사용자는 더 양질의 서비스로 도움을 받을 수 있을것입니다.

    번외: 비활성화된 요소는 반드시 커서 속성을 수정해주세요!

    CSS의 cursor 속성은 매우 중요합니다. 분명 위 방법으로 disabled 속성을 멋지게 만들어주었음에도 커서 속성을 변경해주지 않으면, 사용자에게 혼란을 줄 수 있습니다. 기본값으로 마우스를 올렸을 때, cursor값이 pointer로 제공된 링크나, 임위로 cursor를 pointer로 준 요소는  마우스 포인터의 모양이 손 모양으로 변경됩니다. 사용자로 하여금 누를 수 있는 요소로 오인할 수 있는 여지를 주며, 머릿속에 자연스럽게 물음표가 떠오릅니다.

    따라서, disabled 상태인 요소에 마우스 포인터가 올라갔을 때는 cursor값을 아래와 같이 default로 만드는 것이 좋습니다.

    .filter:disabled:hover,.filter[aria-disabled=true]:hover{
        cursor:default;
    }

    disabled 요소 만들기 체험: [링크]

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] TalkBack에서의 activity_label, setTitle 텍스트 처리 방식
    Webacc NV 2020-07-07 10:44:36

    TalkBack을 활성화하고 접근성을 테스트 하다보면 화면이 전환될 때 대부분 화면 제목을 자동으로 읽는 것을 경험하셨을 것입니다.

    그것은 TalkBack에서 activity_label 텍스트 혹은 setTitle 텍스트를 가지고 와서 제목으로 읽어주는 것인데 화면 제목을 자동으로 읽어주면 일일이 제목을 터치해서 확인하지 않아도 화면의 내용을 대략 유추할 수 있어 큰 도움이 됩니다.

    문제는 UI에 따라서는 화면 제목을 사용하지 않거나 커스텀으로 제목을 사용하는 경우에는 화면 상의 제목 표시줄은 숨기기 때문에 activity_label 텍스트를 삽입하지 않는 경우가 많습니다.

    어차피 화면에서 사용하지 않기 때문입니다.

    그런데 그렇게 되면 스크린 리더 사용자에게는 문제가 있습니다.

    activity_label 텍스트가 없으므로 TalkBack이 화면 제목을 읽을 수 없어 애플리케이션의 제목을 읽게 되고 직관적인 피드백을 받을 수 없게 됩니다.

    따라서 화면에서 제목을 숨기더라도 스크린 리더 사용자를 위해 activity_label 텍스트를 각 activity마다 삽입하거나 혹은 setTitle 메소드를 추가해 주는 것이 큰 도움이 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    웹페이지 다크 모드 만들기 with CSS 변수
    Webacc NV 2020-07-06 12:28:44

     2019년부터 UI에는 한가지 큰 열풍이 불었습니다. 바로 다크모드라고 하는 멋진 친구입니다. 다른이름으로는 야간 모드라고도 부르지요. iOS 13, Android 10, Windows 10 1903 버전 등에서 이 다므코드를 지원하기 시작했습니다.

    웹에서도 이러한 사용자 환경에 따라 웹페이지의 색상을 다르게 표시할 수 있는데요. 오늘은 다크 모드를 지원하는 방법에 대해 짧게 설명하고자 합니다.

     

    어떻게 다크 모드를 브라우저가 인식하게 할까요?

    다크 모드는 아래의 미디어 쿼리 속성으로 지원할 수 있습니다.

    @media(prefers-color-scheme:dark){
        .selector {
           properties
        }
    }

    지난 아티클에서도 소개드린 적 있는 친구, prefers-color-scheme 입니다.  이 친구를 부르면 Android 10, iOS13, 1903 이상의 Windows 10, 그리고 모하비 이상의 Mac OS 환경에서의 최신 브라우저에서 어두운 테마를 별도로 제공할 수 있습니다.

     

    더 효율적인 미디어쿼리 관리를 위한 CSS 변수를 함께 사용하기

    조금 더 와닿을 수 있도록 짧게 한번 레이아웃 페이지를 만들어보도록 할텐데요. 그 전에 소개시켜드릴 친구가 더 있습니다. 어두운 테마를 작성할 때 조금 더 편리하게 처리해주는 친구인 CSS 변수로, Chrome이나 Firefox 등 다크모드를 지원하는 최신 브라우저에서 대부분 사용이 가능합니다.

    CSS 변수는 아래와 같이 선언할 수 있고, 사용할 수 있습니다.

    #wrapper {
       --landmark-BgColor:ivory;
       --landmark-FtColor:black;
    }
    
    #wrapper header{
       background-color:var(--landmark-BgColor);
       color:var(--landmark-FtColor);
    }

    동일한 색상 코드를 반복적으로 사용하는 것이 편하게 보일 수도 있으나, 조금더 의미를 부여하여 CSS 코드를 작성하는 대에 도움을 주고, 변수는 재활용이 가능하기 때문에 내가 원하는 곳에 적절하게 사용할 수 있으며, 선택한 색상이 영 마음에 들지 않을 경우, 위에 선언한 변수만 바꾸면 되는 장점이 생깁니다. 이는 CSS 코드가 길어질수록 더 빛납니다.

    CSS 변수는 이렇게 특정 요소부터 종속시킬 수도 있지만, 아래와 같이 전역 변수로 만들 수도 있습니다. 전역변수로 만들려면 :root이라는 가상 클래스 선택자(pseudo class)를 사용하면 됩니다.

    :root{
      --Global-CSS-Variable:linear-gradient(#00f, #a0f, #f0a);
    }

    전역이라는 이름에서 알 수 있듯, 이렇게 등록된 변수는 어떠한 곳에서든 사용이 가능합니다. 이번 다크테마 만들기에서는 이 전역변수를 활용할 것입니다.

    그럼 본격적으로 페이지를 어둠으로 물들여봅시다.

    :root{
     /*Body*/
      --body-bgColor:#fafafa;
      --body-ftColor:#2c2c2c;
     /*Landmark*/
      --landmark-bgColor:#5cba8f;
    }
    *{padding:0;margin:0; box-sizing:border-box;}
    html{width:100vw; height:100vh;  max-width:100%;}
    body{
      width:100%; height:100%;
    }
    
    /*중요하지 않은 부분입니다. 레이아웃 관련*/
    .wrapper{display:grid; width:100%; height:100%;grid-template-columns: 2fr 6fr 2fr;grid-template-rows: 1fr 8fr 1fr; gap:1rem;padding:0.5rem;}
    .wrapper>*{padding:0.5rem;}
    header{grid-column: span 3;} nav{grid-column:1/2;}
    main{grid-column: span 2;} footer{grid-column: span 3;}
    header,footer{
      display:flex; flex-flow:column wrap;
      align-items: center; justify-content: center;
    }
    .CopyLeft{
      display:inline-block;
      transform:rotate(180deg);
      vertical-align: middle;
      user-select:none;
    }
    .Light .sun, .Dark .moon{
      display: inline-block; width:2rem; height:2rem;
    } .Light, .Dark{ font-size:200%; }
    
    
    /*** 주요 코드! ***/
    body{ /*바디에 색상을 줍시다*/
      color:var(--body-ftColor); background-color:var(--body-bgColor);
    }
    
    .Dark{ /*밝은 화면(기본화면)에서 다크모드의 인사가 나오지 않도록 합니다*/
      display:none;
    }
    
    header,nav,footer{ /*배너 랜드마크나 내비게이션같은 부수적인 랜드마크에 색상을 다르게 줍시다*/
    background-color:var(--landmark-bgColor);
    color:var(--landmark-ftColor);
    border-radius:0.5rem;}
    
    .Light .sun{ /*라이트 모드에서 보여질 햇님 아이콘을 추가하고, 글자와 똑같은 색을 입혀줄게요~*/
      mask:url(https://image.flaticon.com/icons/svg/869/869818.svg) 100%;
      -webkit-mask:url(https://image.flaticon.com/icons/svg/869/869818.svg) 100%;
      background-color:var(--body-ftColor);
    }
    
    @media (prefers-color-scheme:dark){ /*이제 어두운 화면에서 보여질 색상들을 입혀줍시다*/
        :root{
        --body-bgColor:#2c2c2c;
        --body-ftColor:#fafafa;
        --landmark-bgColor:#191919;
    /*각각의 색상을 변수로 다시 지정해줍니다. 어두운 화면인 동안에만 이 색상으로 모두 교체되므로 아래에 중복된 코드를 입력할 필요가 없습니다.*/
      }
    
      .Dark{display:block;} /*라이트 모드에서 숨겼던 다크모드의 인사말을 표시해요!*/
      .Light{display:none;} /*반대로 라이트 모드에서 인사했던 햇님 친구를 숨겨줍시다.*/
    
      .Dark .moon{ /*라이트 모드에서 보여질 달님 아이콘을 추가하고, 글자와 똑같은 색을 입혀줄게요*/
        mask:url(https://image.flaticon.com/icons/svg/899/899555.svg) 100%;
        -webkit-mask:url(https://image.flaticon.com/icons/svg/899/899555.svg) 100%;
        background-color:var(--body-ftColor);
      }
    }

    한 코드에 모든걸 담아놓아 조금 복잡하지만, 핵심만을 말씀드리자면, prefers-color-scheme에서 변수의 값만 다시 대입해준다면, 중복된 코드 없이 손쉽게 다크모드 페이지를 지원할 수 있다는 점입니다. 이 둘을 함께쓴다면 조금도 효율적으로 색상 팔레트를 관리할 수 있겠지요?

     

    단순히 색상반전을 사용하면 안 되는 건가요?

    이 방법은 반전 기법과 비교했을 때 조금 복잡하고 불편해 보일 수도 있지만, CSS의 Filter를 이용한 반전 기법에도 단점이 있습니다.

    간편한 만큼 사용자와 디자이너의 욕구를 만족시키지 못한다는 점인데요. filter 프로퍼티의 Invert라는 반전 CSS함수를 사용하면, 손쉽게 밝은화면과 어두운 화면을 구현할 수 있지만, 하위에 위치한 모든 요소의 색상이 반전되므로 원하지 않는 색상이 표시되기도 합니다. 웃자고 쓰는 표현으로, 사람 얼굴이 나온 사진이 있다면, 스머프 처럼 보이게 됩니다.

    물론, 이에 대한 대처법도 존재하지만, 페이지에 따라서 오늘 소개해드린 prefers-color-scheme 만큼 또는 그 이상의 작업이 필요할 수도 있습니다. 따라서 어두운 화면을 구현할 때는 Invert보다 색상 팔레트를 따로 구성하여 만드는 것이 더 아름답고 깔끔할 수 있습니다.

     

    팁 치고는 긴 글 읽어주셔서 감사드리며, 아래는 위의 코드를 적용한 참고용 레이아웃 페이지입니다. 필요한 분께 좋은 자료가 되길 희망합니다.

    [적용 페이지]

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UILabel을 VoiceOver가 처리하는 방식
    Webacc NV 2020-07-03 16:40:12

    UILabel은 일반적으로 클릭 이벤트가 없는 텍스트를 화면에 표시할 때 사용하는 요소입니다.

    그래서 VoiceOver에서도 UILabel 요소로 된 텍스트는 요소 유형이 따로 지정되어 있지 않습니다.

    그런데 텍스트 자체를 클릭하여 다른 화면으로 이동하도록 구현하거나 기타 상황에 따라 UILabel에 클릭 이벤트를 삽입하는 경우도 있습니다.

    일반적으로 UILabel에 클릭 이벤트를 추가할 때는 다음과 같은 두 속성을 사용하게 됩니다.

    1. UITapGestureRecognizer(target: self, action: #selector((customClick_:)))

    2. object.UserInteractionEnabled = true

    그런데 클릭 이벤트를 추가하는 순간 VoiceOver는 해당 UILabel에 자동으로 버튼이라는 컨트롤 속성을 추가합니다.

    즉 정리하면 tapGestureRecognizer 이벤트를 통해서 UILabel에 클릭 이벤트를 주게 되면 UIAccessibilityTrait를 button 등으로 변경하지 않더라도 기본적으로 VoiceOver가 버튼이라는 컨트롤 유형을 삽입한다는 것입니다.

    UILabel에 클릭 이벤트 구현 시 참고가 되었으면 좋겠습니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [HTML 공통] 링크나 버튼 요소에 스크린 리더용 숨김 텍스트 제공시 주의사항
    Webacc NV 2020-07-02 11:27:07

    링크나 버튼 요소에 의미를 가진 아이콘과 텍스트가 함께 삽입된 경우 스크린 리더 사용자를 위한 아이콘에 대한 설명을 숨김 텍스트로 링크나 버튼 요소 내에 제공해 주는 경우를 종종 보곤 합니다.

    또한 새창으로 열리는 링크에도 '새창' 문구를 링크 내에 숨김 텍스트로 삽입하기도 합니다.

    그런데 스크린 리더 사용자가 이러한 요소를 탐색할 때 불편한 부분은 대부분 이러한 숨김 텍스트 앞 혹은 뒤에는 문맥에 맞는 띄어쓰기 혹은 문장부호가 없다는 것입니다.

    예를 들어 화면에 보여지는 링크 텍스트는 구매하기 이고 숨김 텍스트가 새창 이라면 문장부호나 띄어쓰기에 대한 고려를 하지 않을 경우 '구매하기새창 링크'라고 읽어주게 됩니다.

    문맥에 따라서 띄어쓰기 혹은 문장부호가 삽입되지 않은 숨김 텍스트는 듣기에 상당히 어색할 수 있으며 특히 음성과 점자로 함께 정보를 읽는 스크린 리더 사용자는 더 어색함을 느낄 것입니다.

    따라서 화면에 보여지는 레이블과 추가 설명 텍스트가 자연스럽게 읽어줄 수 있도록 띄어쓰기 및 문장부호 적용이 필요하겠습니다.

    좋은 예시:

    <a href="http://nuli.navercorp.com">널리로 가기<span class="blind">, 새창</span></a>

    <a href="http://nuli.navercorp.com" class="pdf"><span class="blind">pdf&nbsp;</span>다운로드</a>

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] EditText 요소에 contentDescription 사용은 금물
    Webacc NV 2020-07-01 12:26:24

    안드로이드에서는 EditText를 사용하여 여러 형태의 편집창을 구현합니다.

    EditText에는 android:hint 속성을 통해 무엇을 입력해야 하는지에 대한 레이블을 지정할 수 있는데 레이아웃에 따라 android:hint 속성을 사용하지 않는 경우도 종종 있습니다.

    문제는 이때 스크린 리더 사용자를 고려하기 위해 EditText에 contentDescription을 통한 보이지 않는 레이블을 삽입하는 경우를 종종 보곤 합니다.

    그러나 위와 같이 구현할 경우 접근성 API에서 해당 객체를 편집창으로 인식하지 못하게 되어 TalkBack에서 제공하는 여러 편집 옵션을 사용할 수 없게 됩니다.

    따라서 EditText에 android:hint 속성을 사용할 수 없는 경우에는 labelFor를 통해 인접한 객체의 텍스트를 가지고 오거나 이 또한 불가능한 경우에는 AccessibilityNodeInfo class에서 setHintText 메소드를 사용하여 스크린 리더 사용자를 위한 레이블을 삽입하는 것이 바람직합니다.

    댓글을 작성하려면 해주세요.
  • tip
    보조기술 이해하기
    에어류 2020-06-30 15:28:44

    안녕하세요! 에어류입니다.

    국내의 웹 접근성 관련 표준 및 지침은 국가에서 표준으로 정한 한국형 웹콘텐츠 접근성 지침 2.1이 공식표준입니다.

    물론 표준대로 웹 접근성을 구현해준다면 보다 많은 정보소외계층이 웹에 접근하기 수월해지겠죠?

     

    그래서 준비한 이번 팁은 국내 지침에서 주로 스크린리더에 관련된 검사항목이 많이 포진해 있기 때문에 콘텐츠를 제공하실 때 고려하실 부분에 대하여 예시와 함께 준비해봤습니다.

     

    단순히 규격만 준수하기 이전에 보조기술을 이해하고, 이러한 이해를 바탕으로 웹 접근성을 구현한다면 더욱 좋은 웹 사이트가 되시리라 믿습니다.

     

    띄어쓰기를 하지 않은 텍스트에 대한 대체텍스트 낭독 KDBWealth의 경우 크드브웰스로 낭독

    만약 이런 영어메뉴가 있는데 띄어쓰기를 하지 않은 경우라면 스크린리더에서 어떻게 낭독할까요?

     

    띄어쓰기를 하지 않은 텍스트에 대한 대체텍스트 낭독 KDBWealth의 경우 크드브웰스로 낭독

    보시는 것처럼 원래 의도한 발음대로 낭독되지 않는 것을 보실 수 있습니다. 

    그렇다면 정상적으로 띄어쓰기가 되었을 때의 낭독내용도 함께 확인해보겠습니다.

    띄어쓰기를 한 경우 텍스트에 대한 대체텍스트 낭독 KDB Wealth의 경우 케이디비 웰스와 같이 메뉴를 제대로 낭독

    보시는 것처럼 띄어쓰기 하나만으로 스크린리더 이용자들에게 더욱 편리하게 이해할 수 있는 텍스트를 낭독하여 접근성을 높힐 수 있습니다. 

     

    그래서 우리가 습관처럼 사용하는 -> 와 같은 화살표 조합도 스크린리더로는 "대쉬 그래이터 댄"과 같이 낭독하게 되므로 실제 오른쪽 화살표인 → 특수문자를 그대로 사용하시는 것이 좋습니다. 그러면 "오른쪽 화살표"로 낭독하게 됩니다. 

    센스리더에서는 캐럿, -는 대쉬, =은 이퀄, \은 백슬래쉬, |는 버티클바, <는 레스댄, >는 그레이터댄으로 낭독하고, NVDA에서는 캐럿, -는 다시, =은 등호, \은 백슬래쉬, |는 바, <는 레스, >는 그레이터로 낭독하고, 한글죠스11에서는 캐럿, -는 마이너스, =은 이퀄, \은 백슬래쉬, |는 버티클바, <는 레스댄, >는 그레이터댄으로 낭독하고, 실로암 보이스에서는 캐럿, -는 대쉬, =은 이퀄, \은 백슬래쉬, |는 버티클바, <는 레스댄, >는 그레이터댄으로 낭독함을 안내하는 표

     

    댓글을 작성하려면 해주세요.
  • tip
    [HTML 공통] 스크린 리더 사용 시 각 폼 컨트롤의 추가 설명을 바로 들을 수 있도록 구현하기
    Webacc NV 2020-06-30 14:38:53

    회원가입, 신청서 작성과 같은 폼 컨트롤을 사용할 때는 편집창, 라디오버튼, 체크박스와 같은 각각의 필드를 탭키로 이동하는 경우가 많습니다. 

    그런데 각 필드에 상황에 따라 추가 설명 텍스트가 삽입되는 경우가 있습니다.

    비밀번호 입력창에는 '8-16자 내외로 대문자를 반드시 섞어 사용하세요'와 같은 안내 문구가 들어가는 것이 한 예입니다.

    그런데 탭 키로만 폼 컨트롤들을 이동하면 각 필드에 해당하는 레이블 및 사용자가 입력한 값만 읽어주므로 부가 설명에 대한 텍스트를 놓치는 경우가 많습니다.

    이를 해결하기 위해 각 필드에 aria-describedby="sampleid" 속성을 추가하여 추가 설명이 들어 있는 요소와 연결시켜주면 스크린 리더는 탭키로 각 필드에 포커스 하는 경우 레이블과 함께 추가 설명 문구도 함께 읽어주게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 한 화면에 여러 <ListView> 혹은 <GridView>가 있다면 contentDescription을 통해 영역 제목 지정을 고려해 보세요.
    Webacc NV 2020-06-29 10:03:05

    며칠전 iOS UITableView에서의 accessibilityLabel 지정시 VoiceOver가 어떻게 동작하는지를 공유하였습니다.

    그런데 안드로이드에서도 ListView, GridView 속성에 contentDescription을 추가하면 스크린 리더로 목록뷰, 그리드뷰 영역에 접근 시에 어떤 목록 혹은 그리드 영역인지를 읽어주므로 한 화면에 여러 영역이 나뉘어져 있을 경우 레이아웃 파악이 훨씬 쉬워집니다.

    대체 텍스트이므로 화면에는 보이지 않으며 android:contentDescription="연락처 리스트" 등과 같이 삽입할 수 있습니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS Native] 커스텀 보안 키패드 사용시 VoiceOver 사용자를 위해 keyboardKey UIAccessibilityTrait 사용을 고려해 보세요.
    Webacc NV 2020-06-26 18:21:43

    아이폰에서 제공하는 네이티브 키보드는 사용자의 선호도에 따라 각 키보드 키에서 손을 떼면 바로 입력이 되게 하거나 혹은 두 번 탭하여 입력하게 하는 등의 키보드 타이핑 옵션 설정을 할 수 있습니다.

    그런데 간편 결제 비밀번호, 공인 인증서 비밀번호와 같은 키패드는 네이티브 키보드를 사용하지 않는 경우가 많습니다.

    그래서 사용자가 키를 입력하려면 무조건 이중탭을 해야만 입력이 됩니다.

    그러나 이러한 보안 키패드에 keyboardKey UIAccessibilityTrait 속성을 적용하면 사용자의 선호도에 따라 이중탭 혹은 한 번 탭 방식으로 각 키 입력이 가능하게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WEB 공통] 라디오 버튼 구현시 fieldset을 사용할 수 없을 경우에는 role="radiogroup" 사용을 고려해 보세요.
    Webacc NV 2020-06-25 11:06:34

    라디오 그룹의 캡션을 나타내는 fieldset, legend 태그를 사용하지 않을 경우 스크린 리더 사용자가 탭 키를 눌러 라디오 버튼에 포커스 했을 때 겪는 어려움 중 하나는 어떤 그룹의 라디오 버튼인지를 바로 알 수 없다는 것입니다.

    fieldset, legend 태그를 삽입하는 경우 탭키로만 라디오 버튼에 접근해도 '과일 라디오그룹, 사과 라디오버튼 체크됨'과 같이 읽어주기 때문에 어떤 종류의 라디오버튼인지 바로 알 수 있습니다.

    그런데 화면 상의 공간이 너무 좁은 경우나 기타 이유로 인해 fieldset 태그를 사용할 수 없는 경우가 있습니다.

    이런 경우에는 role="radiogroup", aria-label="과일 리스트"와 같이 마크업하여 본 문제를 해결할 수 있습니다.

    이렇게 하면 fieldset, legend 태그를 사용할 수 업는 경우라도 라디오 버튼에 탭키를 눌러 접근 시에 어떤 종류의 라디오버튼인지 바로 알 수 있어 웹 탐색이 더욱 편리해 집니다.

    댓글을 작성하려면 해주세요.
  • news
    iOS 14에서 제공될 back tap 기능에 관하여
    Webacc NV 2020-06-24 11:24:50

    현재 코로나로 인해 애플 개발자 회의의 여러 세션들이 온라인으로 진행중입니다.

    올 해에 배포될 iOS 14 버전에도 많은 접근성 기능들이 업데이트 될 것으로 예상하는 가운데 한 가지 주목할 기능 중 하나가 'back tap' 입니다.

    손쉬운 사용 섹션에서 해당 기능을 활성화 하면 아이폰 뒷면을 두 번 혹은 세 번 두드리는 동작을 특정 기능에 연결하여 사용할 수 있도록 하는 것으로 앱 전환기, 접근성 기능 실행, 제어센터 열기 등 다양한 기능을 연결하여 사용이 가능하다고 합니다.

    자세한 내용은 10월달 정도에 발행될 널리 아티클을 통해 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 시간 제한이 있는 팝업 구현 시 getRecommendedTimeoutMillis 메소드 적용을 고려해 보세요.
    Webacc NV 2020-06-23 11:07:17

    안드로이드 버전 10 이상부터는 접근성 서비스에 팝업이나 토스트 메시지 등의 시간 제한이 있는 콘텐츠가 출력되었을 때 접근성 서비스에서 강제로 이를 제어할 수 있는 기능이 추가되었습니다. 

    쉽게 말해 기본적으로 팝업이나 알림 메시지가 3초 후에 사라진다고 가정하면 접근성 서비스에서 강제로 표시되는 시간을 2분까지 늘려서 사용할 수 있도록 한 기능입니다.

    문제는 안드로이드의 토스트와 같은 시스템 자체의 알림을 활용하지 않고 대화상자를 통한 팝업을 띄우거나 커스텀으로 잠깐동안 알림을 띄우는 경우는 개발 시에 접근성 서비스에서 강제로 시간을 늘릴 수 있도록 구현을 해 주어야 합니다.

    이 때 사용하는 메소드가 바로 getRecommendedTimeoutMillis 입니다.

    자세한 활용방법은 추후 널리 아티클을 통해 다루도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UITableView에는 상황에 따라 accessibilityLabel 속성을 추가해 주세요.
    Webacc NV 2020-06-22 14:00:41

    iOS에서 UITableView를 활용하여 화면에 테이블 형태로 콘텐츠를 표시하는 경우가 많습니다.

    그런데 스크린 리더 사용자는 한 화면에 여러 영역의 레이아웃이 표시되고 있을 경우 테이블에 접근 시 현재 읽고 있는 영역 정보가 없어 레이아웃 파악이 어려운 경우가 발생하곤 합니다.

    HTML에서는 데이터 테이블 구현 시 caption 태그를 통해 테이블 제목을 제공해 줄 수 있는데 iOS에서도 UITableView 자체에 accessibilityLabel 속성을 통해 스크린 리더용 테이블 제목에 해당하는 정보를 삽입할 수 있습니다.

    예: tableView.accessibilityLabel = "recipe list"

    이렇게 하면 VoiceOver 포커스가 테이블 콘텐츠에 들어갈 때 테이블 제목을 먼저 읽게 되어 현재 어떤 영역에 포커스 하고 있는지 쉽게 파악이 가능합니다.

    따라서 화면의 제목만으로 표 영역에 대한 유추가 가능하다면 문제가 없으나 한 화면에 표를 두 개 이상 제공하거나 화면 제목과 관련이 없는 표를 제공할 때는 accessibilityLabel 속성 추가를 고려해 보시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WEB 공통] aria-label은 기존에 있던 레이블을 덮어씁니다.
    Webacc NV 2020-06-19 11:09:48

    스크린 리더 사용자를 위해 x(닫기 버튼), 5(알림 아이콘 포함) 버튼과 같이 화면에 보여지는 텍스트만으로는 해당 요소의 의미를 이해하기 어려운 경우 aria-label을 사용하는 경우를 종종 보곤 합니다.

    그런데 aria-label은 기존에 표시되는 레이블을 덮어씁니다.

    예를 들어 <button class="noti" aria-label="개의 알림 있음">5</button> 과 같이 마크업을 한 경우 스크린 리더 사용자는 화면에 표시되는 5라는 숫자는 읽지 못하게 됩니다.

    따라서 aria-label은 x, ?, 아이콘 등으로만 요소를 표시하는 경우이거나 <div contenteditable="true"> 와 같이 HTML의 기본 label 태그를 사용할 수 없는 경우 등에서만 사용해야 합니다.

    이것은 id를 연결하여 스크린 리더에서 읽을 수 있는 레이블을 표시하는 aria-labelledby 속성에도 그대로 적용됩니다.

    물론 화면에 보여지는 텍스트를 aria-label에 포함한다면 문제는 없겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] TalkBack 8.2에서부터 적용된 ListView 안의 항목개수 처리 관련
    Webacc NV 2020-06-18 14:36:39

    안드로이드 접근성을 대응하다보면 아쉬운 점 중 하나는 iOS와 달리 라디오버튼, 목록 리스트뷰와 같은 그룹화된 항목에 TalkBack이 포커스하더라도 몇 개 중 몇 번째 요소에 포커스하고 있는지를 알려주지 못한다는 것입니다.

    그런데 최근 업데이트 된 TalkBack 8.2 버전에서는 ListView로 그룹화 된 요소의 경우 목록 안의 요소에 포커스 하면 1/5, 3/5와 같이 총 개수 중 현재 포커스한 요소가 몇 번째임을 음성 출력하도록 수정되었습니다.

    따라서 우리가 유의해야 할 사항은 바로 개수에 대한 대체 텍스트 문제입니다.

    탭컨트롤의 접근성 구현 시 접근성 대응을 위해 대체 텍스트로 개수에 대한 정보를 주는 경우가 많습니다.

    예: 홈 탭(3개 중 첫 번째), 혹은 홈탭(1/3) 등.

    그런데 레이아웃에 따라 탭 컨트롤이 ListView로 그룹화되는 경우가 있는데 이 때는 TalkBack에서의 ListView 처리 방식의 변경으로 인해 개수에 대한 정보를 중복 읽게 되는 문제가 발생하게 됩니다.

    그렇게 되면 중복 정보로 인해 스크린 리더 사용자가 탐색할 때 불편을 겪게 될 것입니다.

    이 부분에 대해서도 이후 널리 아티클을 통해 깊이 다루어 보도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [PC web] NVDA로 웹페이지 접근성 테스트 시 브라우즈모드 포커스 하이라이트 기능을 사용해 보세요.
    Webacc NV 2020-06-17 10:48:46

    PC 웹접근성을 테스트 하기 위해 스크린 리더를 사용하는 경우 겪는 어려움 중 하나는 현재 스크린 리더가 읽고 있는 줄이 어디인지 찾기 어려울 때가 있다는 것입니다. 

    브라우저는 <input type="text">와 같은 편집 영역이 아닌 이상 커서를 표시하는 캐럿이 없기 때문에 스크린 리더가 가상으로 커서를 만들어서 마치 문서를 탐색하듯이 사용자가 읽을 수 있도록 기능을 제공합니다.

    이 커서를 스크린 리더 제조사마다 가상커서, 브라우즈모드 등으로 부르고 있습니다.

    그런데 말 그대로 가상커서다보니 스크린 리더에서 자체적으로 현재 읽고 있는 줄을 하이라이트 해 주지 않는 이상 시각적으로 현재 가상의 커서가 있는 위치를 알 수 없습니다.

     

    이를 해결하기 위해 NVDA, JAWS 등에서는 나름대로의 방식으로 가상커서 하이라이팅 기능을 추가하였습니다. 

    NVDA에서는 NVDA 설정 > vision > 브라우즈모드 커서 하이라이트 기능을 켜서 사용하시면 접근성 테스트 시에 현재 읽고 있는 부분이 노란색으로 표시되므로 조금 더 쉽게 시각적 구분이 가능합니다.

    브라우즈모드커서 포커스 하이라이트가 체크된 NVDA 환경설정 화면 스크린샷

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 음성 검색 시 마이크가 같은 화면에서 켜지는 경우네는 접근성 적용을 위해 startsMediaSession trait를 사용하세요.
    Webacc NV 2020-06-16 15:21:09

    지난 번에는 음성 검색을 누르면 화면이 전환되면서 마이크가 켜졌을 때 VoiceOver 소리가 들어가지 않도록 하기 위한 접근성 팁을 공유했습니다.

    이번에 공유할 팁은 음성 검색을 눌렀을 때 화면이 전환되지 않고 바로 마이크가 켜지는 경우입니다.

    이때 접근성 대응을 해주지 않으면 VoiceOver가 포커스 하고 있는 같은 레이블을 다시한번 더 읽기 때문에 해당 음성이 함께 들어가게 됩니다. 

    이를 해결하는 방법은 너무 간단합니다.

    사용자가 음성검색 버튼을 이중탭할 때 레이블을 다시 한번 읽지 못하도록 음성검색 버튼에 startsMediaSession trait를 추가하는 것입니다.

    이렇게 하면 사용자가 이중탭하더라도 아무런 내용도 읽지 않게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WEB 공통] <ul> 요소에 WAI-ARIA 마크업을 통한 tab 컨트롤 구현 시 <li> 요소에는 role="none"을 추가해 주세요.
    Webacc NV 2020-06-15 16:38:41

    웹페이지를 탐색하다보면 WAI-ARIA를 활용하여 tab 컨트롤을 구현한 사이트를 종종 보곤 합니다.

    그런데 <ul> 요소로 되어 있는 탭컨트롤을 WAI-ARIA로 마크업하는 경우 흔히 하는 실수 중 하나가 각각의 <li> 요소에 role="none"을 추가하지 않는 것입니다. 

    <ul>은 role="tablist", <li>는 role="none", 각 a 요소에는 role="tab"을 삽입해 주어야 tab 컨트롤을 제대로 읽을 수 있게 됩니다.

    즉 tab 컨트롤은 tablist, tab, tabpanel로 구성되는데 <li> 요소는 tab 컨트롤과 관련이 없으므로 접근성 API에게 관련이 없는 요소라는 것을 알려 주어야 합니다.

    댓글을 작성하려면 해주세요.