본문 바로가기
빌드노트 검색

빌드노트

display: contents를 줬더니 자식은 남고 배경과 테두리가 사라진다

display: contents에서 배경·테두리·padding 값이 남아도 wrapper의 박스는 생기지 않는다. 기존 요소의 display와 자식 크기를 짧게 조회하고, 공통 장식이 필요할 때 박스를 복원하는 방법과 실제 Chrome 관찰을 설명한다.

wrapper에 display: contents를 적용한 뒤 자식은 보이는데 배경·테두리·안쪽 여백이 사라졌다면, 그 선언부터 확인한다. contents는 DOM 요소를 지우지 않지만 wrapper 자신의 레이아웃 박스를 만들지 않는다. 자식들을 한 배경과 테두리로 감싸야 한다면 wrapper가 박스를 만드는 display를 유지해야 한다.

기존 wrapper에서 확인할 것

개발자 도구의 Elements에서 문제가 생긴 wrapper를 찾는다. Computed에 padding이나 border 값이 남아 있어도 실제로 그려질 영역이 있다는 뜻은 아니다. 먼저 display를 확인한다. Console에서는 다음처럼 조회할 수 있다. .wrapper는 실제 wrapper의 선택자로, .child는 그 안에서 보이는 자식의 선택자로 바꾼다. 두 요소가 있는 페이지에서 실행하며, null 오류가 나면 선택자와 현재 문서부터 확인한다.

var wrapper = document.querySelector('.wrapper');
var child = wrapper.querySelector('.child');
getComputedStyle(wrapper).display

결과가 "contents"라면 wrapper 자체의 박스가 생기지 않는 조건이다. CSS Display Level 3의 박스 생성 규칙 (새 탭에서 열림)은 일반 div에 이 값을 적용하면 자체 박스를 만들지 않고 자식의 박스는 계속 만들도록 정의한다. 배경이나 테두리 수치를 더 키우기 전에, 이를 표시할 wrapper의 박스가 필요한지 판단한다.

다음 조회는 같은 wrapper와 자식의 사각형을 차례로 출력한다. 앞에서 찾은 두 요소를 그대로 사용한다.

console.log(wrapper.getBoundingClientRect(), child.getBoundingClientRect());

출력의 width와 height를 비교한다. wrapper가 0×0이어도 자식은 양의 크기를 가질 수 있다. 다만 크기가 0인 이유는 여럿이므로 이 값만으로 원인을 단정하지 않는다. 앞의 display 결과, Elements에 남아 있는 요소와 실제 자식 화면을 함께 본다.

스타일 값은 남았지만 그릴 박스가 없었다

별도 합성 문서의 같은 두 div에서 wrapper의 display만 바꿔 직접 비교했다. Windows 11의 headless Chrome 156.0.8078.47, 960×480 뷰포트에서 실행하고 각 화면도 캡처했다. wrapper에는 노란 배경·4px 갈색 테두리·20px padding을, 자식에는 연한 파란 배경과 120×40px 크기를 줬다.

block일 때 wrapper는 360×88px였고 자식 주위에 배경·테두리·여백이 보였다. contents로 바꾸자 wrapper의 사각형은 0×0이 됐지만 자식은 120×40px로 남았다. 캡처에서도 노란 배경과 갈색 테두리만 사라졌고, 파란 자식과 글자는 계속 보였다. 자식은 왼쪽과 위로 각각 24px 이동했다. 이 입력에서 border 4px와 padding 20px가 더 이상 자식을 밀어내지 않은 결과와 맞는다.

그런데 contents 상태에서도 조회한 paddingTop은 "20px", borderTopWidth는 "4px", backgroundColor는 "rgb(255, 224, 138)"였다. 선언된 장식 값이 사라진 것이 아니라 그 장식을 그릴 자체 박스가 없어진 것이다. 자기 페이지에서도 Computed의 장식 값과 박스 생성을 구분해서 읽어야 한다.

같은 wrapper 객체는 문서에 연결돼 있었고 자식의 DOM 부모도 그대로였다. 박스 관계가 평평해졌다고 DOM에서 wrapper가 삭제된 것은 아니다. 또한 wrapper의 사각형 조회는 자식 영역을 합쳐 주지 않는다. 이번 조건에서 getClientRects()는 빈 목록이었고, CSSOM View 초안의 조회 규칙 (새 탭에서 열림)에 따라 getBoundingClientRect()의 위치와 크기가 모두 0으로 나왔다. 자식의 위치까지 화면 원점이 됐다는 뜻은 아니다.

함께 감싸야 한다면 박스를 복원한다

일반 블록 wrapper라면 같은 Console에서 다음 변경으로 배경·테두리·여백이 돌아오는지 확인한다.

wrapper.style.display = 'block';

직접 비교한 문서에서는 wrapper가 다시 360×88px가 되고 자식 주위의 장식과 여백도 복원됐다. 자기 페이지에서는 앞의 사각형 조회를 다시 실행해 화면과 함께 확인한다. 복원되지 않으면 최종 적용된 display, border의 스타일·색, 요소 크기와 가림 여부를 살펴본다.

Console 변경은 임시 확인이다. 실제 수정은 display: contents를 지정한 CSS 규칙에서 한다. 이 예제는 일반 블록 흐름이므로 block을 썼다. wrapper 자체가 flex나 grid로 자식을 배치해야 하는 코드라면 그 역할에 맞는 박스 생성 값을 선택한다.

카드처럼 여러 자식을 한 영역으로 감싸는 배경과 테두리가 필요하면 wrapper의 박스를 남긴다. 장식을 자식으로 옮길 수도 있지만 자식마다 그린 테두리가 전체를 한 테두리로 감싸는 화면을 대신하지는 않는다. 부모 flex/grid가 자식들을 직접 배치하도록 contents를 고려했다면 공통 영역도 필요한지 함께 판단한다. 이번 실험은 flex/grid 배치 자체를 검증한 것은 아니다.

직접 측정한 범위는 Chrome 한 버전의 일반 div와 자식 하나다. 이미지·폼 컨트롤 등 특수 요소에는 별도 규칙이 있다. DOM이 남는다는 사실로 접근성 의미 보존까지 보장할 수도 없다. MDN의 display: contents 접근성 설명 (새 탭에서 열림)은 일부 브라우저 구현에서 요소 자체가 접근성 트리에서 빠질 수 있다고 설명한다. 이번에는 접근성 트리와 스크린리더를 측정하지 않았다.

← 전체 글 보기