리액트 프로젝트에서 POST 기능을 구현하는데 파일을 업로드하는 부분도 만들어야 했다.
이 페이지는 내가 마크업을 하지 않아서 기능만 구현하고 있었다.
프로젝트 발표 후 웹접근성을 다시 고려해 리팩토링을 하고 있던 도중 문제를 발견했다.
파일업로드 부분은 tab key로 접근이 안된다는 점이다.
마크업을 살펴봤을 때 input이 display: none;
상태여서 접근이 불가능 했다.
그래서 고민을 하다가 tabIndex 를 주자 싶어서 코드를 수정했다.
코드를 수정한 부분은 div에 tabIndex 속성을 부여했다.
<div className={styles.imgBox} tabIndex={0}>
근데 여기서 tabIndex 만 주면 밑과 같이 eslint 가 경고를 한다.
이 오류를 해석했을 때 tabindex는 대화형 요소에서만 선언해야 합니다.
라고 알려주는 것이다.
잘 이해가 안되어서 링크로 들어가서 문제점을 해석할 수 있었다.
div에 tabindex를 주는 것 자체가 별로인 관점이긴 하다만... 일단 쓰려면 이런식으로 경고를 해결할 수 있다.
<div role="button" className={styles.imgBox} tabIndex={0}>
근데 여기서 또 두번째로 발생하는 문제가 있다.
tab key로 접근은 가능하지만 enter key를 눌렀을 때 이벤트가 작동하지 않는다.
어찌보면 당연한 결과다. input 태그가 숨겨져있으니 이벤트가 작동할리가 없다.
그래서 새로 enter key 이벤트를 추가해줘야 하나 고민을 하다가 하나의 조언을 얻었다!
div에 tabindex를 추가하고 또 거기에 이벤트를 거는 것은 너무 비효율적이며 접근성에도 맞지 않다.
따라서, input 을 보여지게 만들고 position 속성을 이용해서 겹치게 만들어버리자!
이렇게도 할 수 있다는 사실을 깨닫고 바로 적용시켜봤다.
div에 줬던 tabindex, role 속성을 모두 지우고 css 스타일을 변경했다.
먼저 부모 요소에게 position : relative를 준다.
.imgBox {
position: relative;
}
그리고 input을 이런식으로 변경한다.
.profile_form {
appearance: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: white;
}
display 대신 appearance를 사용해준다!
appearance
: 웹 브라우저 내장 컴포넌트 모양(appearance)을 표시하지 않도록 설정하는데 사용한다. 이 속성을 설정한 이유는 웹 브라우저마다 파일 업로드 인풋의 모양이 다를 수 있기 때문이다.
그리고 position : absolute; 를 사용해서 부모 크기 위치로 조정해주고 너비, 높이값을 줘서 tab key로 접근했을 때 전체가 잡히도록 만들어 준다. 그러면 결과적으로 이렇게 나오게 된다.
이 파일선택 버튼은 어떻게 없애는걸까.. 어떤 방법을 시도해도 안되어서 구글링을 해봤다.
바로 file-selector-button 속성을 사용해주면 된다!
input[type="file"]::file-selector-button {
border: 0;
font-size: 0;
background-color: transparent;
}
이 속성을 이용해서 파일 선택 버튼 스타일을 아예 안보이게 처리해줬다.
다 된줄 알았는데 사진 넣으면 폰트 색상이 white라 위에 나오게 됨.. 이부분을 다시 고쳐줬다.
color 속성을 삭제했더니 이젠 또 미리보기가 없을 때 글자가 나온다 😮
다시 침착하게 이번엔 그냥 font-size 를 0으로 처리해버리자
그러면 이렇게 정상적으로 작동이 된다!
접근성을 고려하기 위해서 tabindex를 div에 속성으로 추가한 짓은 바보같은 짓이였다.🥲
애초에 div에 tabindex 속성을 부여하는 것 자체가 별로고 input의 스타일을 수정해주는 방법이 옳았다.
+ 추가적으로 마우스로 클릭하는 경우 마우스 커서(cursor) 모양이 pointer가 되도록 CSS를 사용해 설정하면 사용자로 하여금 마우스로 클릭 가능함을 식별할 수 있다.
[추가 리팩토링]
1. 업로드 영역 모양이 자세히 보니까 살짝 여백이 남는다. 그래서 box-sizing 속성을 통해 거슬리는 점선을 보이지 않도록 설정했다.
.profile_label {
box-sizing: border-box;
overflow: hidden;
display: block;
width: 100px;
height: 100px;
border-radius: 5px;
border: 1px dashed var(--gray-500);
}
2. 업로드 인풋의 선택된 파일 이름 텍스트 값을 감출 목적으로 글자 크기(font-size)를 0으로 설정하는 것은 적합하지 않다고 한다. 일부 스크린 리더에서 콘텐츠를 읽지 못할 수 있기 때문이다. 이런 경우엔 불투명도(opacity) 속성을 사용해 화면에서 감추는 것이 적절하다고 해서 font-size: 0 에서 opacity: 0으로 바꿔주었다!
.profile_form {
opacity: 0;
cursor: pointer;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
input[type="file"]::file-selector-button {
opacity: 0;
}
3. 위의 과정(opacity: 0)으로 인해 초점이 표시되지 않는 문제가 발생한다. 이 문제는 업로드 인풋 요소 래퍼인 imgBox 요소에 :focus-within 가상 클래스를 설정해 해결할 수 있었다.
.imgBox {
position: relative;
}
.imgBox:focus-within {
box-shadow: 0 0 0 2px #015fcc;
border-radius: 4px;
}
'개발 > CSS' 카테고리의 다른 글
[CSS/웹접근성] tab key로 button 태그에 접근했을 때 focus 색상 변경하기 (초점, 포커스링의 개념과 중요성) (0) | 2023.04.04 |
---|---|
[CSS/UIUX] 페이지 콘텐츠에 따른 화면 밀림 현상(스크롤바 표시 유무) (0) | 2023.04.01 |
[CSS] Flex (Flexible Box)로 레이아웃 구성하기 (0) | 2022.12.03 |