블로그

스크롤 애니메이션: IntersectionObserver 대신 animation-timeline

JavaScript 패럴랙스가 항상 조금 이상해 보이는 이유가 있습니다 — 약간 분리된 느낌, 레이어가 페이지보다 반 프레임 뒤에서 떠다니는 것 같은. 이것은 나쁜 코드가 아닙니다. 브라우저가 이미 스크롤을 그린 후에 스크롤 이벤트가 JavaScript에 도달하기 때문입니다. 브라우저는 이것을 한번 해결했습니다 — 고정 헤더를 position: sticky로 바꿀 때. Scroll-driven animations는 동일한 접근법을, 모든 스크롤 기반 효과에 한번에 적용한 것입니다.

View Transitions와 Next.js: 라이브러리 없이 페이지 애니메이션

10년 동안 썸네일을 히어로 이미지로 애니메이션하는 방법은 하나뿐이었습니다: 전후 위치를 측정하고, transform으로 차이를 속이고, 재생하세요. FLIP. 모든 애니메이션 라이브러리가 이것을 하고, 모두 최악의 순간에 메인 스레드의 JavaScript라는 대가를 치릅니다. View Transitions API는 그 트릭 전체를 브라우저로 옮깁니다 — React 19.2와 Next.js 16부터 전체 계약은 name이라는 하나의 prop으로만 구성됩니다.

content-visibility: 리스트 가상화 없이 화면 밖 렌더링을 건너뛰는 법

댓글 천 개짜리 목록이 느리게 렌더링되어 react-window를 꺼내 듭니다 – 그런데 부드러운 스크롤을 얻는 대가로 Ctrl+F, 페이지 인쇄, 그리고 스크린 리더 탐색 일부를 잃게 됩니다. 화면 밖 요소들이 DOM에서 물리적으로 사라져버리기 때문입니다. content-visibility는 같은 문제를 다른 방식으로 풉니다 – 요소는 DOM에 그대로 남아 있고, 브라우저는 화면 근처에 오기 전까지 비용이 큰 레이아웃과 페인트 작업만 건너뜁니다. 그리고 놀랍게도, 그 안의 텍스트를 검색할 때는 일시적으로 다시 드러내 줄 수도 있습니다.

Popover API와 네이티브 <dialog>: 처음부터 직접 만드는 모달의 종말

접근성 글에서 다룬, display:none으로 감췄지만 접근성 트리에는 여전히 남아 있던 모달은 div로 처음부터 직접 만든 모달에서 비롯된 실수의 한 예였습니다. 진짜 질문은 애초에 왜 그것을 처음부터 만들어야 했는가입니다. 네이티브 <dialog>와 popover 속성이 공짜로 제공하는 것 – 톱 레이어, 포커스 트랩, light dismiss – 이 무엇인지, 그리고 이 두 메커니즘이 서로 헷갈리면 곧바로 접근성이 떨어지는 인터페이스로 이어질 만큼 다른 지점이 어디인지 살펴봅니다.

CSS @layer: 캐스케이드 레이어가 특이성 전쟁을 끝내는 방법

유틸리티 클래스 .mt-4는 나중에 추가되었으니 논리적으로는 이겨야 할 것 같은데도 .card .card__header .card__title 규칙에 집니다. 이는 우연이 아니라 특이성이 설계된 대로 정확히 작동하고 있는 것입니다. @layer는 특이성과 함께가 아니라 그보다 앞서 작동하는, 충돌 해결의 완전히 새로운 축을 도입합니다 – 그리고 어찌나 직관에 어긋나는지, 웹상의 일부 문서조차 잘못 설명하는 함정이 하나 있습니다.

CSS :has() — 안에서 무슨 일이 일어나는지 아는 부모

폼 필드는 내부의 input이 유효하지 않을 때 빨간색으로 강조되어야 합니다. 간단해 보이는 일입니다 – 다만 CSS는 25년 동안 요소에게 그 내부에서 무슨 일이 일어나고 있는지 물어볼 방법이 없었고, 오직 그 반대 방향만 가능했습니다. :has()가 이 방향을 어떻게 뒤집는지, 일반적인 결합자와 어떻게 다른지, 그리고 이 새로운 힘이 실제로 성능에 어떤 대가를 치르게 하는지 살펴봅니다.