truncate와 line-clamp은 둘 다 넘치는 텍스트를 …로 줄이는 Tailwind 유틸이지만, 동작 기반이 다릅니다.
그래서 flex 같은 레이아웃과 같이 쓰면 에러도 없이 조용히 안 잘리는 경우가 생깁니다.
두 유틸이 뭐가 다른지, 말줄임이 의도대로 안 먹을 때 어디부터 확인해야 하는지 정리합니다.
두 유틸은 동작 원리가 다릅니다
이름은 비슷하지만, 실제로 생성되는 CSS를 보면 기반이 다릅니다.
/* truncate */
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* line-clamp-1 */
.line-clamp-1 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}truncate는 white-space: nowrap으로 줄바꿈을 막고, 넘치는 부분을 text-overflow: ellipsis로 자릅니다. 한 줄 전용이고, display는 건드리지 않습니다.
line-clamp은 여러 줄을 자를 수 있는 대신, display: -webkit-box라는 옛날 webkit 박스 모델 위에서만 동작합니다. N줄 말줄임이 그 위에 구현돼 있기 때문입니다.
한 줄(line-clamp-1)도 되지만, 이걸 쓰는 순간 그 엘리먼트의 display를 차지한다는 게 핵심입니다.
webkit 박스 모델(
-webkit-box)이란?
지금 쓰는display: flex가 표준이 되기 전, 2009년경 webkit이 먼저 구현한 1세대 플렉스 레이아웃입니다. 표준에서는 밀려났지만-webkit-line-clamp(여러 줄 말줄임)이 이 박스 모델 위에 얹혀 있어서, 지금도 N줄 말줄임을 쓰려면display: -webkit-box를 함께 선언해야 합니다. 표준은 아니지만 모든 주요 브라우저가 지원합니다.
truncate | line-clamp-N | |
|---|---|---|
| 줄 수 | 한 줄만 | N줄 (1줄 포함) |
| 핵심 속성 | white-space: nowrap + text-overflow | display: -webkit-box + -webkit-line-clamp |
display 점유 | 안 함 | 함 (-webkit-box) |
| 동작 기반 | 블록 안 인라인 텍스트 | webkit 박스 모델 |
한 줄이면 둘 다 됩니다. 하지만 여러 줄을 자르려면 line-clamp뿐이고, 그 대가로 display를 내줘야 합니다.
바로 이 점이 flex와 충돌하는 지점입니다.
안 잘릴 때 1순위: 같은 엘리먼트의 display
가장 흔한 경우가 line-clamp과 flex를 같은 엘리먼트에 준 것입니다.
<span className="... flex items-center line-clamp-1">{label}</span>이러면 line-clamp-1을 붙여도 안 잘립니다. flex는 display: flex를 선언하고, line-clamp은 display: -webkit-box를 깔고 동작합니다.
display는 값이 하나뿐이라 두 선언이 같은 속성을 두고 경합하고, 보통 뒤에 선언된 쪽이 적용되면서 -webkit-box가 사라집니다. line-clamp이 기대던 기반이 없어지니 에러도 없이 조용히 무력화됩니다.
충돌이라기보다 “같은 display를 다른 값으로 덮어쓴” 것입니다.
flowchart TD
A["한 엘리먼트에 line-clamp + flex"] --> B["line-clamp → display: -webkit-box"]
A --> C["flex → display: flex"]
B --> D["display 값은 하나뿐, 뒤 선언이 이김"]
C --> D
D --> E["-webkit-box 제거 → 말줄임 무력화"]
truncate로 바꿔도 flex 컨테이너에 직접 주면 마찬가지입니다. text-overflow: ellipsis는 블록 안에서 넘치는 인라인 텍스트를 자르는데, display: flex가 되면 자식이 flex item으로 배치되어 ellipsis 적용 대상에서 빠집니다.
그래서 말줄임이 안 먹으면 같은 엘리먼트에 display를 바꾸는 다른 유틸이 있는지부터 봅니다. flex, grid가 대표적입니다.
해결은 레이어를 나누는 것
원인이 “한 엘리먼트가 display를 두 용도로 못 쓴다”는 것이라면, 해결도 단순합니다.
바깥은 레이아웃(flex)을 맡고, 안쪽 별도 span이 말줄임을 맡도록 역할을 나눕니다.
flowchart TD
O["바깥 span · flex items-center · min-w-0 (레이아웃 담당)"] --> I["안쪽 span · truncate (말줄임 담당)"]
I --> T["텍스트를 … 로 자름"]
<span className="... flex items-center min-w-0">
<span className="truncate" title={label}>
{label}
</span>
</span>- 바깥 span:
flex items-center로 세로 중앙 정렬 유지 - 안쪽 span:
truncate로 말줄임,title로 hover 시 전체 텍스트
여기서 flex item의 최소 너비도 같이 봐야 합니다.
flex item은 기본값이 min-width: auto라 콘텐츠의 min-content 아래로는 안 줄어듭니다. 긴 텍스트가 셀을 무시하고 밀고 나가서 애초에 overflow가 안 생깁니다.
푸는 방법은 둘입니다.
- 바깥에
min-w-0(min-width: 0)을 줘서 줄어들 수 있게 한다. - 스펙상 flex item의
overflow가visible이 아니면 자동 최소 너비가 0이 됩니다(CSS Flexbox §4.5).truncate엔overflow: hidden이 들어있으니, 안쪽 truncate span 자체가 이미 이 조건을 충족합니다.
즉 안쪽 truncate만으로도 동작하고, 바깥 min-w-0은 굳이 없어도 됩니다.
정리하기
- 한 줄이면
truncate, 여러 줄이면line-clamp-N. 한 줄은 둘 다 되지만line-clamp은display를 차지한다는 비용이 있습니다. 참고로-webkit-box는 표준이 아니라 벤더 프리픽스로 굳어진 구현이고, 여러 줄 말줄임은 이 방식에 의존합니다. - 말줄임이 에러 없이 안 먹으면, 같은 엘리먼트에
flex·grid처럼display를 바꾸는 유틸이 있는지부터 의심하세요. - 해결은 레이어 분리입니다. 바깥은 레이아웃, 안쪽은 말줄임으로 역할을 나눕니다. flex 안에서 너비가 안 줄면
min-w-0도 같이 봅니다.