[공유] Vertical-align 파헤치기! – 1부

들어가며

안녕하세요. 하이브랩 UI개발2팀 도재경, 우현주입니다.

각자 UI개발1팀과 SEP실에서 열심히 과제를 진행했지만 서로 같은 고충을 느꼈던 저희!!!
그 고충의 주인공은 도무지 알다가도 모르겠던 녀석… 바로 vertical-align 때문이었습니다.

특히, middle값을 지정 할 경우 정확하게 중앙으로 수직정렬되지 않아 음수 마진 값을 적용하거나 absolute 포지셔닝 등을 활용하여 위치를 잡는 경우가 많았습니다.

하지만, 아래의 예시와 같이 분명 vertical-align: middle을 선언했음에도 왜 중앙 정렬이 되지 않는 것인가??? 도대체 vertical-align이 적용되고 안되는 경우의 차이는 무엇인가?? 라는 의문이 생겨 vertical-align 속성을 한번 시원하게 파헤쳐 보기로 했습니다.​

예시1_03
 예시1_02예시1_01

 

본문 목차

1. Vertical-align 관련 개념알기
1.1. Vertical-align이란?
1.2. Vertical-align 관련 개념들
1.3. Vertical-align 속성값
2. Case Study
2.1. 대체된 요소(replaced element)의 크로스브라우징 테스트
2.2. Vertical-align: middle

 


 

본문

1. Vertical-align 관련 개념알기

1.1. Vertical-align이란?

먼저, Vertical-align은 인라인 블록 등을 포함한 모든 인라인요소의 수직 정렬을 위해 사용됩니다. 하지만 블록 레벨 요소의 수직 정렬에는 영향을 미치지 않는데요, 여기서 table-cell*의 수직 정렬은 예외라는 점! 아래는 vertical-align관련해 스펙에서 발췌한 내용입니다.
(참조: https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align)

​* Table-cell에 적용된 vertical-align의 경우 해당 요소가 속한 table-row 사이에서 수직 정렬을 가능하게 합니다. 다만 수직 정렬의 기준이 되는 기준선 등은 인라인 요소의 수직 정렬과는 조금 다른데요, 자세한 사항은 아래 URL을 참고해주세요~
(참조: https://www.w3.org/TR/CSS2/tables.html#height-layout)

vertical-align
속성값 baseline | sub | super | top | text-top | middle | bottom | text-bottom | <percentage> | <length> | inherit
기본값 baseline
적용대상 인라인 요소와 테이블 셀
상속여부 No
퍼센트 요소의 line-height 값 참조
산출값 길이와 퍼센트 값은 절대 길이. 이외는 지정한 값

[명세 원문]
The following values only have meaning with respect to a parent inline element, or to the strut of a parent block container element. In the following definitions, for inline non-replaced elements, the box used for alignment is the box whose height is the ‘line-height’ (containing the box’s glyphs and the half-leading on each side).
For all other elements, the box used for alignment is the margin box.
[번역]
해당 속성값은 부모 인라인 요소 또는 부모 블록 요소의 strut와 연관해서만 의미가 있다. 정의 상, 대체되지 않은(non-replaced) 요소의 경우 수직정렬을 위해 사용되는 박스 영역의 높이는 line-height 값을 의미하며, 이 외의 요소의 경우 정렬을 위해 사용되는 박스는 마진 박스이다.

모르는 용어가 너무 많습니다. 울음 여자아기
여기서, 대체되지 않은(non-replaced) 요소와 그 외의 요소는 무엇을 의미하며 vertical-align을 위해 사용되는 박스 영역의 위치는 어떻게 다르고 어느 기준으로 정렬된다는 것일까요??
내용을 살펴보기에 앞서 strut, line-height, 대체된 요소 등 다양한 개념의 이해가 필요합니다!

1.2. Vertical-align 관련 개념들

Strut
Strut는 부모 요소의 font-size와 line-height 속성을 상속 받은 width가 0인 가상의 박스라고 볼 수 있는데요.
즉, 이 보이지 않지만 존재하는(!) 상상의 박스의 baseline에 따라 부모 요소 안의 인라인 요소가 수직 정렬됩니다.
이 상상의 박스의 위치를 눈으로 확인하고 싶다면 방법이 있습니다. 부모 요소에서 어떠한 태그도 감싸지 않고 바로 기입된 텍스트를 익명 텍스트라고도 부르는데요. 이 텍스트 영역이 strut와도 같다고 볼 수 있습니다.

익명 텍스트 (anonymous text​)
익명텍스트(anonymous text)는 인라인 요소 안에 포함되지 않은 텍스트를 의미합니다.
예를들어 다음의 마크업에서 ‘이불 밖은’은 anonymous text에 해당합니다. ​

<p>이불 밖은 <em>위험해!</em><p>

Line-height
Line-height값은 텍스트 위쪽에 있는 행 사이의 공간인 행간(leading)과 폰트의 크기를 통제하는 것으로 line-height에서 font-size를 뺀 값을 2로 나누어 각 글자의 위와 아래에 더해집니다. 요약하면 아래와 같습니다.​
(line-height – font-size) / 2 = 반-행간(half-leading) 값​

그림5

대체된 요소 (replaced element)

  • 해당 요소는 주로 고유의 크기(dimension), 즉 각 UA(사용자에이전트)에서 정의하는 고유의 넓이, 높이, 비율 등을 가지고 있습니다. 또, 각 UA에 따라 baseline이 다르게 정의 됩니다.
  • 대체된 요소: img, object, video, button, input, textarea, select 등
  • 대체된 요소의 콘텐츠 영역은 요소의 높이에 패딩, 마진, 보더를 더한 영역 (즉, 스펙에서 말하는 ‘마진 박스’) 입니다.
  • 인라인 박스 높이는 콘텐츠 영역 높이와 같습니다.

대체되지 않은 요소 (non-replaced element)​

  • 대체된 요소를 제외 한 모든 요소를 뜻합니다. (div, p, span, em 등)
  • 대체되지 않은 인라인 요소: span, em, strong 등
  • 대체되지 않은 인라인 요소의 콘텐츠 영역은 font-size의 크기 또는 사용자 에이전트에 따라 문자 도안들로 만들어지는 박스를 의미합니다.
  • 대체되지 않은 인라인 요소의 인라인 박스 높이는 콘텐츠 영역에 행간(leading)이 추가된 높이를 말합니다. (패딩, 마진, 보더 값은 고려되지 않음)

인라인 박스 (inline box)
인라인 박스(inline box)는 라인 박스 안에서 vertical-align 속성 값에 의해 수직 정렬되는 영역입니다.

라인 박스 (line box)
라인 박스는 여러 인라인 박스를 구성하는 영역으로 하나의 줄에 있는 인라인 박스의 가장 높은 지점과 낮은 지점을 둘러싸고 있는 박스입니다.​​ 아래는 스펙에서 발췌한 내용입니다.

[명세 원문]
A line box is always tall enough for all of the boxes it contains. However, it may be taller than the tallest box it contains (if, for example, boxes are aligned so that baselines line up). When the height of a box B is less than the height of the line box containing it, the vertical alignment of B within the line box is determined by the ‘vertical-align’ property. When several inline-level boxes cannot fit horizontally within a single line box, they are distributed among two or more vertically-stacked line boxes. Thus, a paragraph is a vertical stack of line boxes. Line boxes are stacked with no vertical separation (except as specified elsewhere) and they never overlap.
[번역]
라인박스는 자신이 포함하고 있는 박스들보다 항상 충분히 (높이가) 크다. (인라인레벨)박스 B의 height가 그것을 포함한 라인박스의 height보다 작을 때, 라인박스 내의 B의 수직 정렬은 ‘vertical-align’속성에 의해 결정된다. 몇 개의 인라인레벨박스가 한 개의 라인박스 안에 들어가지 않을 때, 두개 혹은 그 이상의 라인박스로 나뉘어진다. 따라서, 단락은 라인박스의 수직더미인 것이다. 라인박스는 수직적인 분리없이 쌓이며(다른 곳에서 명시된 경우를 제외하고) 절대로 겹쳐지지 않는다.line_box

 

1.3. Vertical-align 속성값

속성값 설명 예시
Baseline
(line-height가 지정된 대체되지 않은 요소)
  • 요소의 인라인박스의 baseline을, 라인박스의 baseline에 정렬​
  • 대체된요소의 baseline은 각 UA에 따라 다르게 계산됨으로 수직 정렬 시 baseline값을 가급적 사용하지 않고 그 외의 값을 사용
  • 인라인블록에 여러줄의 텍스트가 있는 경우에는 마지막 라인박스, 즉 마지막 줄의 baseline을 해당 요소의 baseline으로 봄
 baseline
Baseline
(텍스트가 없는 인라인 블록 요소)
  • 텍스트가 없거나, 텍스트가 있지만 overflow 속성 값이 visible인 인라인블록 요소의 경우 마진 박스의 하단 경계를 baseline으로 봄​
 baseline(inline-block)
Middle
  • 요소의 인라인박스의 수직 중앙 지점(콘텐츠 영역의 높이/2)을, 부모요소의 중앙 지점에 정렬
  • 부모요소의 중앙지점은 strut의 baseline 위 0.5ex 지점
  • ex의 높이는 일반적으로 ‘x’자의 높이를 의미하며 글꼴, 사용자 에이전트에 따라 0.5ex로 정의되는 위치에 차이가 있을 수 있음
 그림3middle
Text-top
  • 요소의 인라인박스의 상단을, 부모요소 콘텐츠영역의 상단에 정렬​
 text-top
Text-bottom
  • 요소의 인라인박스의 하단을, 부모요소 콘텐츠영역의 하단에 정렬​​
  text-bottom
Top
  • 요소의 인라인박스의 상단을, 라인박스의 상단에 정렬​​​
 top
Bottom
  • 요소의 인라인박스의 하단을, 라인박스의 하단에 정렬
 bottom
Sub
  • 요소의 인라인박스의 baseline을 부모박스의 아래첨자의 적절한 위치로 내림
    (이 값은 요소 텍스트의 font size에는 영향을 주지 않음)
Super
  • ​요소의 인라인박스의 baseline을 부모박스의 윗첨자의 적절한 위치로 올림
    (이 값은 요소 텍스트의 font size에는 영향을 주지 않음)

 


 

2. Case Study

2.1. 대체된 요소(replaced element)의 크로스브라우징 테스트

Form구조를 마크업할 때 input, button, select 등 대체된 요소의 수직 정렬에 애를 먹는 경우가 많은데요,
각 브라우저 별로 해당 요소의 baseline을 한번 비교해 보았습니다.

테스트 환경 :
– 대체된요소, 대체되지않은 요소를 포함한 모든 요소의 인라인박스 높이는 30px로 구현될 수 있도록 마크업
– 대체되지 않은 요소를 inline-block으로 지정​​​
– Font-size와 Line-height를 동일하게 30px로 지정하고 vertical-align 값은 baseline으로, padding 및 margin 값은 0으로 지정
(동일한 높이 구현을 위해 IE7과 Safari에 발생하는 input, select 요소의 extra margin값 각각 대응)

Chrome chrome
Firefox firefox
Windows Safari safari*-webkit-appearance: none 적용 후 렌더링
Opera opera
IE9~Edge ie9-edge
IE8 ie8
IE7 ie7

결론
– 대체된 요소인 Input이나 select 태그의 경우 브라우저마다 구현되는 baseline지점이 각기 다름
(Baseline은 글꼴에 따라서도 각기 다르게 구현됨)
– ​이와 같이 인라인 박스의 높이가 같은 인라인레벨 요소를 좌우 정렬하고자 하는 경우 top, bottom 또는 middle 값을 활용

2.1. Vertical-align: middle

왼쪽은 서두에 잠깐 보았던 예시입니다. 이 경우 텍스트는 ‘익명 텍스트(anonymous text)’로 항상 부모 요소의 baseline에 위치하게 됩니다. 앞서 언급한 것처럼 strut의 위치와 같다고 볼 수 있습니다. 하지만 이때 수직 중앙정렬 된 아이콘의 경우 익명 텍스트의 중앙지점, 즉 baseline에서 0.5ex 높이의 지점에 맞춰 정렬하게 돼 정확한 중앙지점에 위치하지 않게 됩니다.​

 그림7
 예시1_02예시1_01  예시2_01예시2_02

결론
– 우측의 예시와 같이 중앙정렬 되어야 하는 텍스트를 태그로 감싸고 해당 요소에 vertical-align: middle 값을 지정​해 수직 중앙정렬
(인라인 요소의 수직 중앙지점은 인라인 박스의 중앙 (높이의 절반 지점))​

 


 

마치며

여태까지 vertical-align을 선언하면서 원하는 데로 수직 중앙정렬이 되지 않을 경우 모든 속성값을 차례대로 선언하고 결과를 확인해 보는 작업을 무한으로 반복했습니다.
많은 시간을 뺏기기도 했지만 도무지 왜 그런 현상이 발생하는지 이해하지 못해 골머리를 앓았는데요,
이번 글을 작성하면서 vertical-align에 대해 그간 묵혀왔던 궁금증을 해소하는 계기가 되어 저희에겐 매우 유익한 시간이었습니다.

만약 저희와 같이 vertical-align을 적용해도 원했던 결과나 나타나지 않는 경우가 발생한다면
우선 각 인라인 레벨 요소의 baseline, top, bottom과 라인 박스의 상단과 하단 경계의 위치를 파악해 보는 것이 도움이 될 것 같습니다.
그리고 부모요소에 보이지 않는 상상의 박스인 strut가 있다는 사실, 그리고 인라인 레벨 요소들이 이 strut에 따라 수직정렬이 된다는 점도 항상 염두에 두면 좋을 것 같습니다.

그리고!!!!! 두구두구두구

혹시 이 글로는 부족하다! 수직정렬을 더 파헤치고 싶다! 하시는 분들을 위해, ‘Vertical-align 파헤치기! – 2부’도 준비 중에 있습니다.
많은 기대 부탁 드립니다! 긴 글 읽어 주셔서 감사합니다!

Coming Back Soon~ ㅎuㅎ​