Contents
see ListJavaScript 없이 가능해진 CSS의 혁신
2025~2026년 CSS는 과거 JavaScript에 의존하던 많은 UI 패턴을 순수 CSS만으로 구현할 수 있게 되는 전환점을 맞이했습니다. View Transitions, Anchor Positioning, Scroll-Driven Animations, Popover API 등이 주요 브라우저에서 안정적으로 지원되기 시작했으며, 이를 활용하면 성능과 접근성을 동시에 개선할 수 있습니다.
1. View Transitions — 페이지 전환 애니메이션
View Transitions API는 SPA와 MPA(Multi-Page App) 모두에서 매끄러운 페이지 전환 효과를 구현합니다. Safari가 지원을 추가하면서 이제 주요 브라우저 전체에서 사용 가능합니다.
/* CSS: View Transition 스타일 정의 */
::view-transition-old(root) {
animation: slide-out 300ms ease-in;
}
::view-transition-new(root) {
animation: slide-in 300ms ease-out;
}
@keyframes slide-out {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(-100%); opacity: 0; }
}
@keyframes slide-in {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* 특정 요소에 이름 지정 (공유 요소 전환) */
.product-card {
view-transition-name: product-card;
}
.product-detail-image {
view-transition-name: product-card; /* 동일 이름으로 모핑 효과 */
}// JavaScript: View Transition 트리거
async function navigateTo(url) {
if (!document.startViewTransition) {
// 미지원 브라우저 폴백
window.location.href = url;
return;
}
const transition = document.startViewTransition(async () => {
const response = await fetch(url);
const html = await response.text();
document.getElementById('main-content').innerHTML =
new DOMParser().parseFromString(html, 'text/html')
.getElementById('main-content').innerHTML;
});
await transition.finished;
}
// MPA: next.config.js (Next.js 15+)
export default {
experimental: {
viewTransition: true, // 자동 처리
},
};2. CSS Anchor Positioning — JavaScript 없는 툴팁/드롭다운
기존에는 툴팁, 드롭다운, 팝오버의 위치 조정을 위해 JavaScript로 getBoundingClientRect()를 계산해야 했습니다. CSS Anchor Positioning은 이를 순수 CSS로 해결합니다.
/* 앵커 요소 정의 */
.trigger-button {
anchor-name: --my-trigger; /* 앵커 이름 등록 */
}
/* 앵커에 연결된 팝오버 위치 설정 */
.tooltip {
position: absolute;
position-anchor: --my-trigger; /* 앵커 참조 */
/* 앵커 기준 위치 설정 */
top: anchor(bottom); /* 앵커의 bottom에 맞춤 */
left: anchor(center); /* 앵커의 center에 맞춤 */
transform: translateX(-50%);
/* 자동 위치 조정: 뷰포트 밖으로 나가면 반대로 */
position-try-fallbacks: flip-block, flip-inline;
}
/* 실용적인 드롭다운 메뉴 */
.nav-item {
anchor-name: --nav-dropdown;
}
.dropdown-menu {
position: fixed;
position-anchor: --nav-dropdown;
top: anchor(bottom);
left: anchor(left);
margin-top: 4px;
/* 화면 밖으로 나가면 위로 표시 */
position-try-fallbacks: flip-block;
}3. Scroll-Driven Animations — 스크롤 기반 애니메이션
스크롤 위치에 애니메이션을 연결하는 기능이 CSS만으로 가능해졌습니다. JavaScript의 IntersectionObserver나 scroll 이벤트 리스너 없이 성능 친화적 애니메이션을 구현합니다.
/* 스크롤 진행도를 나타내는 프로그레스 바 */
@keyframes progress-grow {
from { width: 0%; }
to { width: 100%; }
}
.scroll-progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
animation: progress-grow linear;
animation-timeline: scroll(root block); /* 페이지 스크롤에 연결 */
}
/* 요소가 뷰포트에 진입하면서 페이드인 */
@keyframes fade-slide-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-on-scroll {
animation: fade-slide-up ease-out both;
animation-timeline: view(); /* 요소의 뷰포트 진입에 연결 */
animation-range: entry 0% entry 60%; /* 진입 구간 설정 */
}
/* 수평 스크롤 갤러리의 진행도 */
.gallery-container {
overflow-x: scroll;
scroll-snap-type: x mandatory;
timeline-scope: --gallery-scroll;
}
.gallery-progress {
animation-timeline: --gallery-scroll;
}4. CSS Nesting과 :has() 선택자
/* CSS Nesting (브라우저 네이티브 지원) */
.card {
padding: 1rem;
border-radius: 8px;
/* 중첩 선택자 */
.title {
font-size: 1.25rem;
font-weight: bold;
}
.description {
color: #666;
margin-top: 0.5rem;
}
/* 가상 클래스 중첩 */
&:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
transition: all 0.2s ease;
}
/* 미디어 쿼리 중첩 */
@media (max-width: 768px) {
padding: 0.75rem;
}
}
/* :has() 선택자 — 부모 선택 가능 */
/* 이미지를 포함한 카드에만 스타일 적용 */
.card:has(img) {
padding-top: 0;
}
/* 체크박스가 체크된 경우 형제 레이블 스타일 변경 */
.form-item:has(input:checked) label {
color: #2196F3;
font-weight: bold;
}
/* 에러가 있는 폼 필드 감지 */
form:has(.field:invalid) .submit-button {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}5. Popover API — 접근성을 갖춘 네이티브 팝오버
<!-- HTML: Popover API -->
<button popovertarget="my-tooltip">
도움말 보기
</button>
<div id="my-tooltip" popover="hint">
<p>여기에 도움말 내용이 들어갑니다.</p>
</div>
<!-- 확인 대화상자 -->
<button popovertarget="confirm-dialog">삭제하기</button>
<div id="confirm-dialog" popover>
<h2>정말 삭제하시겠습니까?</h2>
<button popovertargetaction="hide" popovertarget="confirm-dialog">취소</button>
<button onclick="handleDelete()">삭제 확인</button>
</div>/* CSS: Popover 스타일 */
[popover] {
/* 기본 popover 스타일 초기화 */
border: none;
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
padding: 1rem;
/* 진입/퇴장 애니메이션 */
transition: opacity 0.2s, transform 0.2s,
overlay 0.2s allow-discrete,
display 0.2s allow-discrete;
opacity: 1;
transform: scale(1);
}
[popover]:not(:popover-open) {
opacity: 0;
transform: scale(0.95);
}
@starting-style {
[popover]:popover-open {
opacity: 0;
transform: scale(0.95);
}
}6. 접근성 향상: WCAG 2.2와 forced-colors
/* 고대비 모드 지원 */
@media (forced-colors: active) {
.custom-button {
border: 2px solid ButtonText;
background: ButtonFace;
color: ButtonText;
}
.custom-button:hover {
background: Highlight;
color: HighlightText;
}
}
/* 포커스 가시성 강화 (WCAG 2.2 2.4.11) */
:focus-visible {
outline: 3px solid #0066cc;
outline-offset: 2px;
border-radius: 2px;
}
/* 동작 감소 선호 사용자를 위한 배려 */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
.scroll-progress-bar {
display: none; /* 스크롤 애니메이션 숨김 */
}
}
/* 색 대비 충족 체크 (미래: CSS Color Contrast) */
:root {
--primary: #0066cc;
--on-primary: white; /* WCAG AA 대비 비율 4.5:1 이상 유지 */
}2025~2026년 CSS는 단순한 스타일링 도구를 넘어, 인터랙션과 접근성을 함께 담당하는 핵심 기술로 진화했습니다. 특히 View Transitions와 Scroll-Driven Animations는 JavaScript 의존도를 낮추면서도 사용자 경험을 크게 향상시킬 수 있는 강력한 도구입니다. 브라우저 지원이 안정화된 지금이 실무 적용의 최적 시점입니다.