채팅 입력창 만들다가 또 이 문제를 만났다. textarea에 줄이 늘어나면 입력창도 같이 커져야 하는데, 기본 textarea는 고정 높이라 스크롤이 생긴다. 슬랙이나 카톡처럼 내용 따라 부드럽게 늘어나게 하고 싶은데, 순수 CSS로는 안 되던 영역이었다.
지금까지는 JS로 해결했다
전통적인 방법은 input 이벤트마다 scrollHeight를 재서 높이를 다시 잡아주는 거다. 대충 이런 코드를 매번 짰다.
function autoResize(el) {
el.style.height = 'auto'; // 먼저 줄였다가
el.style.height = el.scrollHeight + 'px'; // 실제 높이로 다시 세팅
}
textarea.addEventListener('input', (e) => autoResize(e.target));
동작은 하는데 성가시다. height: auto로 리셋했다가 다시 재는 과정에서 리플로우가 두 번 일어나고, React에서는 useRef에 useEffect까지 물려야 한다. react-textarea-autosize 같은 라이브러리를 그냥 설치해버린 적도 많았다.
field-sizing: content
이걸 CSS 한 줄이 대체한다.
textarea {
field-sizing: content;
min-height: 3rem;
max-height: 12rem; /* 이 이상은 스크롤 */
}
field-sizing: content를 주면 폼 컨트롤이 고정 크기가 아니라 내용 크기에 맞춰 커지고 줄어든다. 타이핑하면 textarea가 실시간으로 늘어나고, 지우면 다시 줄어든다. 이벤트 리스너도, ref도, 리플로우 걱정도 없다.
min-height와 max-height를 같이 잡아주는 게 실전 포인트다. 빈 상태에서 너무 납작해지지 않게 최소 높이를 주고, 무한정 늘어나지 않게 최대 높이를 정하면 그 지점부터는 알아서 스크롤이 생긴다.
textarea만 되는 게 아니다
select에도 먹힌다. 기본 select는 가장 긴 옵션 기준으로 너비가 고정되는데, field-sizing: content를 주면 현재 선택된 옵션 길이에 맞춰 너비가 딱 붙는다.
select {
field-sizing: content;
}
짧은 옵션을 고르면 셀렉트 박스도 같이 짧아진다. input[type="text"]도 마찬가지로 입력한 글자 수만큼만 너비를 차지하게 만들 수 있다.
브라우저 지원
Chrome 123+, Edge, 그리고 최근 Firefox와 Safari도 지원이 들어왔다. 아직 안 되는 브라우저를 위한 폴백은 크게 걱정 안 해도 된다. 지원 안 하면 그냥 기존 고정 크기 textarea로 동작할 뿐, 깨지지 않으니까 점진적 향상(progressive enhancement)으로 그냥 얹으면 된다.
@supports (field-sizing: content) {
textarea { field-sizing: content; }
}
채팅창, 댓글 입력, 태그 입력 같은 데서 textarea 자동 리사이즈 로직을 매번 짜거나 라이브러리를 붙였다면, 이제 CSS 한 줄로 끝난다. 라이브러리 하나 지웠다.