Velog
2026년 프런트엔드 개발자라면 알아야 할 4가지 CSS 기능
sibling-index(), text-box, attr() 타입 인자, scroll-state() 컨테이너 쿼리 네 가지로 JS 가 하던 일이 CSS 로 내려온다. 핵심은 '어떤 JS 가 사라지는가'.
핵심 요약
Chrome/Safari 최신 버전이 네 개의 CSS 기능을 안정화했다. (1) sibling-index() / sibling-count() 함수: :nth-child 와 달리 계산식 안에서 인덱스를 값으로 쓸 수 있어 스태거드 애니메이션을 CSS 만으로 만든다. (2) text-box 계열: 폰트의 em-box 상하 공백을 잘라 실제 글자 높이 를 기준으로 정렬, 디자인 시스템의 'vertical rhythm' 을 JS 없이 정확히 맞춘다. (3) attr() 타입 확장: 이제 attr(data-progress type(<number>), 0) 로 HTML 속성을 숫자/길이/색상 타입으로 CSS 에 주입 가능. (4) scroll-state() 컨테이너 쿼리: container-type: scroll-state 와 함께 @container scroll-state(stuck: top) 으로 sticky 고정 여부를 스타일 조건으로 쓸 수 있다. 네 기능 모두 DOM 접근 없이 상태를 CSS 에 노출 하는 방향이다.
최신 CSS 기능을 '새 문법' 이 아니라 'JS 로 하던 일을 CSS 엔진이 가져가는 이관' 으로 본다. 각 기능이 제거하는 JS 코드 조각을 한 줄 예시로 떠올려야 실제 쓸모가 체감된다.
'신문법 왜 씀?' 질문에 '예뻐서' 라고 답하면 감점. 네 기능의 실용 가치는 각각 다음 JS 를 지우는 데 있다.
sibling-index()→ DOM 순회로:nth-child인덱스 계산text-box/text-box-trim→ 폰트 상/하 여백 수동 마진 조정attr(data-x type(<number>))→ dataset 을 읽어style.setPropertyscroll-state()→IntersectionObserver+ 상태 플래그
학습 포인트
면접 답변으로 연결할 학습 포인트입니다.
sibling-index() — 스태거를 CSS 로
:nth-child(n) 는 선택자일 뿐 값이 아니었다. sibling-index() 는 값 이라 calc() 안에서 쓸 수 있다.
li {
/* 각 항목이 50ms 씩 지연되어 등장 */
animation-delay: calc(sibling-index() * 50ms);
}
같은 효과를 과거엔 li:nth-child(1) { delay: 50ms } li:nth-child(2) { delay: 100ms } ... 로 20줄 쓰거나, JS 로 style.setProperty('--i', i) 를 뿌렸다.
:nth-child(n) 의 n 을 수식에서 참조할 수 있다고 오해하는 것. 그건 선택자 매칭용 변수일 뿐 계산 값이 아니다.
text-box — 폰트 여백의 제거
대부분의 폰트는 글자 실제 높이보다 상/하 여백(leading) 이 크다. 아이콘과 수직 정렬하면 항상 어긋나는 원인. text-box-edge / text-box-trim 이 이 여백을 잘라낸다.
.label {
text-box: trim-both cap alphabetic;
/* cap(대문자 위) 에서 alphabetic(기준선) 까지만 상자 */
}
결과: 디자이너가 Figma 에서 잡은 픽셀 단위 수직 정렬 이 그대로 재현된다. 과거엔 margin-top: -4px 같은 매직 넘버로 맞추던 문제.
line-height: 1 로 해결됐다고 여기는 것. 실제로는 폰트마다 em-box 비율이 달라 수치 기준 정렬 을 보장하지 못한다.
attr() 타입 확장 — 데이터 브리지
오래된 attr() 은 문자열만 반환해 content: 밖에서 쓸 수 없었다. 이제 타입 인자를 받아 숫자/길이/퍼센트/색상 을 반환한다.
<div class="progress" data-progress="42"></div>
.progress {
/* 과거: JS 로 element.style.setProperty('--p', 42) */
--p: attr(data-progress type(<number>), 0);
width: calc(var(--p) * 1%);
}
서버 렌더 시점에 HTML 에 값이 박혀 있으면, JS 없이 CSS 가 읽어 쓴다.
content: attr(...) 외에는 안 된다고 믿는 것. 최신 스펙에서 type(<length>) 등을 지정하면 어디서든 사용 가능.
읽는 순서
- 1이론
MDN 에서
sibling-index,text-box,attr()의type()문법,scroll-state()컨테이너 쿼리를 각각 한 문장으로 요약하세요. - 2구현
메뉴 리스트의 스태거 애니메이션을 기존 JS 구현에서
sibling-index()+calc()로 교체하고 코드 라인 수 비교. - 3실무
디자인 시스템의 Typography 컴포넌트에
text-box-trim을 적용해 토큰 기반 수직 리듬 을 맞춘 뒤 스크린샷 diff 로 검증. - 4설명
팀에 '2026 CSS 4종 = JS 얼마나 사라졌나' 데모를 만들고, 브라우저 지원표와
@supportsfallback 패턴을 정리해 공유.
면접 연결 질문
[좋은 답변] :nth-child 는 선택자 매칭 이고, sibling-index() 는 계산에 쓰는 값. 스태거 delay, grid-row 계산, per-item custom property 주입처럼 인덱스를 숫자로 써야 할 때 후자를 쓴다.
[좋은 답변] margin 은 매직 넘버 라 폰트 바뀌면 다시 틀어짐. text-box-trim: trim-both 는 폰트 메트릭 기반이라 굵기/패밀리가 바뀌어도 그대로. 디자인 토큰 단위로 관리하려면 후자가 정답.
[좋은 답변] (1) 서버 템플릿에서 style="--p: 42" 를 직접 렌더. (2) 클라이언트 마운트 후 element.style.setProperty('--p', dataset.progress). 각각 SSR/Hydration 비용이 다름.
자기 점검
'그냥 CSS 가 풍부해졌다' 식 추상 답.
기능을 쓰려면 브라우저 일괄 업그레이드가 필요하다는 오해. @supports 로 분기 처리 가 정답.