Flexbox, 제대로 알아보기
공간을 나누고 아이템을 배치하는 레이아웃의 원리
display: flex를 주면 자식들이 한 줄로 늘어선다. 이후에 쓰는 속성은 대부분 주축(main axis)과 교차축(cross axis) 위에서 정의된다. 두 축의 방향은 flex-direction이 정한다.
| 기준 | 속성 |
|---|---|
| 축을 어디로 놓을까 | flex-direction, flex-wrap |
| 주축에서 어떻게 놓을까 | justify-content, gap |
| 교차축에서 어떻게 놓을까 | align-items, align-content, align-self |
| 아이템이 얼마나 늘고 줄까 | flex-grow, flex-shrink, flex-basis |
align-self와 크기 관련 속성은 아이템에, 나머지는 컨테이너에 건다.
flex-direction, 축을 정하는 속성
주축은 아이템이 늘어서는 방향, 교차축은 그에 수직인 방향이다. 기본값 row에서는 주축이 가로, column에서는 세로다.
justify-*는 주축을, align-*은 교차축을 다룬다. 이 규칙은 방향과 무관하게 유지되므로 flex-direction: column에서는 justify-content: center가 세로 가운데 정렬이 된다.
row-reverse는 아이템의 순서가 아니라 주축의 시작점을 옮긴다. 시작점이 오른쪽이 되므로 flex-start도 오른쪽을 가리킨다.
justify-content, 주축의 남는 공간
아이템을 모두 배치하고 남은 주축 공간을 어떻게 나눌지 정한다.
flex-start,center,flex-end: 남는 공간을 한쪽에 몰아준다.space-between: 첫 아이템과 마지막 아이템을 양 끝에 붙이고 나머지를 사이에 균등하게 나눈다.space-around: 아이템마다 양옆에 같은 여백을 준다. 여백이 겹치므로 가장자리 여백은 사이 여백의 절반이 된다.space-evenly: 가장자리와 사이를 모두 같은 크기로 나눈다.
남는 공간이 없으면 아무 효과도 없다. flex-grow로 아이템이 공간을 다 채웠다면 값을 바꿔도 화면은 그대로다.
아이템 사이 간격은 gap으로 준다. margin과 달리 가장자리에는 여백이 생기지 않는다.
align-items, 한 줄 안의 교차축 정렬
한 줄 안에서 아이템을 교차축의 어디에 놓을지 정한다.
stretch: 기본값이다. 교차축 크기가auto인 아이템을 줄 높이만큼 늘린다. 높이를 준 적 없는 아이템이 컨테이너 높이만큼 커지는 이유다.flex-start,center,flex-end: 아이템이 자기 콘텐츠 크기가 된다.baseline: 첫 줄 텍스트의 기준선을 맞춘다.
아이템 하나만 다르게 두려면 그 아이템에 align-self를 준다. 값은 align-items와 같다.
flex-wrap, 줄을 넘길지
기본값 nowrap은 아이템이 몇 개든 한 줄에 밀어 넣는다. 공간이 모자라면 줄을 바꾸는 대신 아이템이 줄어들고 더 줄어들 수 없으면 컨테이너를 넘친다.
wrap은 들어가지 않는 아이템을 다음 줄로 넘긴다. wrap-reverse는 줄이 쌓이는 방향, 즉 교차축의 시작점을 뒤집는다.
align-content, 줄 뭉치의 정렬
여러 줄이 된 뒤 줄 자체를 교차축 어디에 놓을지 정한다. 값은 justify-content와 같다.
| 속성 | 무엇을 옮기나 | 축 |
|---|---|---|
justify-content | 줄 안의 아이템들 | 주축 |
align-items | 줄 안의 아이템 하나 | 교차축 |
align-content | 줄 전체 | 교차축 |
줄이 하나뿐이면 효과가 없다. flex-wrap: wrap을 주지 않은 컨테이너에서 이 속성이 동작하지 않는 이유다.
flex, 아이템의 크기
flex-grow, flex-shrink, flex-basis의 단축 속성이고 기본값은 0 1 auto다.
flex-grow: 남는 공간을 나눠 가질 비율이다. 기본값0이라 아이템은 저절로 늘어나지 않는다.flex-shrink: 공간이 모자랄 때 줄어들 비율이다. 기본값이1이라 아이템은 가만히 두면 줄어든다.flex-basis: 늘리고 줄이기 전의 기준 크기다.auto면 주축 방향의width(또는height)를 보고 그것도auto면 콘텐츠 크기가 된다.
| 값 | 풀어 쓰면 | 뜻 |
|---|---|---|
initial | 0 1 auto | 콘텐츠 크기에서 시작해 줄어들기만 한다 |
auto | 1 1 auto | 콘텐츠 크기에서 시작해 남는 공간도 나눠 갖는다 |
none | 0 0 auto | 콘텐츠 크기로 고정된다 |
1 | 1 1 0% | 기준 크기를 0으로 두고 전체 공간을 비율로 나눈다 |
flex: auto는 콘텐츠 크기가 기준이라 내용이 긴 아이템이 더 넓게 남고, flex: 1은 기준이 0이라 콘텐츠 길이와 관계없이 너비가 같아진다.
너비를 고정할 때 width: 240px만 주면 flex-shrink가 1로 남아 있어 공간이 부족하면 줄어든다. flex-basis로 적으면 그 문제가 없다.
.equal {flex: 1;}.fixed {flex: 0 0 240px;}
min-width, 줄어들지 않는 아이템
flex: 1을 준 아이템 안에서 긴 텍스트를 말줄임 처리했는데 말줄임표 대신 아이템이 컨테이너를 밀어내는 경우가 있다.
flex 아이템의 min-width 기본값이 0이 아니라 auto이기 때문이다. auto는 콘텐츠의 최소 크기(min-content) 아래로 줄어들지 않게 막고, white-space: nowrap이 걸린 텍스트는 min-content가 문장 전체 길이다.
min-width: 0을 명시하면 이 하한이 풀린다. 자동 최소 크기는 주축 방향 overflow가 visible일 때만 적용되므로 아이템 자신에 overflow: hidden을 줘도 결과는 같다. 말줄임이 아이템 안쪽 요소에 걸려 있다면 아이템의 overflow는 visible로 남으므로 min-width: 0이 필요하다. 주축이 세로라면 min-height: 0이다.
.cell {flex: 1;min-width: 0;}.cell .title {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
order와 margin: auto
order는 아이템이 그려지는 순서를 바꾼다. 기본값은 0이고 값이 작을수록 앞에 온다. 키보드 탭 이동과 스크린 리더는 DOM 순서를 따르므로 둘이 어긋나면 보이는 순서대로 이동할 수 없다.
flex 컨테이너에서 auto 마진은 남는 공간을 전부 가져간다. 아이템 하나만 반대편으로 밀 때 justify-content 대신 쓸 수 있다.
.profile {margin-left: auto;}
정렬이 이상하면 주축이 어디인지, 크기가 이상하면 flex-basis와 min-width를 확인하면 된다. 행과 열을 동시에 잡는 일은 grid의 몫이다.