2025-2026년 CSS: 전처리기 없이도 강력한 시대

2025-2026년 CSS는 Sass, Less 없이도 중첩 선택자, 컨테이너 쿼리, 뷰 전환 애니메이션 등 고급 기능을 기본으로 제공합니다. 주요 브라우저의 지원이 완성되어 이제 프로덕션 환경에서 적극 활용할 수 있습니다.

CSS Nesting: 전처리기 없는 중첩 선택자

CSS Nesting은 모든 주요 브라우저에서 지원되며, 이제 Sass 없이도 컴포넌트 스타일을 구조적으로 작성할 수 있습니다.

/* 기존 방식 (중첩 없음) */
.card { background: white; border-radius: 8px; }
.card .card-header { font-size: 1.2rem; font-weight: bold; }
.card .card-header h2 { margin: 0; color: #333; }
.card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
.card.active { border: 2px solid #007bff; }

/* CSS Nesting 활용 */
.card {
  background: white;
  border-radius: 8px;
  
  &:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  }
  
  &.active {
    border: 2px solid #007bff;
  }
  
  .card-header {
    font-size: 1.2rem;
    font-weight: bold;
    
    h2 {
      margin: 0;
      color: #333;
    }
  }
  
  /* 미디어 쿼리도 중첩 가능 */
  @media (max-width: 768px) {
    padding: 12px;
    
    .card-header {
      font-size: 1rem;
    }
  }
}

Container Queries: 부모 크기 기반 반응형

기존 미디어 쿼리는 뷰포트 크기 기반이라 컴포넌트가 어디에 배치되느냐에 따라 다른 스타일을 적용하기 어려웠습니다. Container Queries는 부모 컨테이너의 크기를 기준으로 스타일을 변경합니다.

/* 컨테이너 정의 */
.card-container {
  container-type: inline-size;
  container-name: card;  /* 선택사항: 이름 지정 */
}

/* 컨테이너 크기에 따른 스타일 변경 */
.product-card {
  display: flex;
  flex-direction: column;
  
  img { width: 100%; aspect-ratio: 16/9; }
  .content { padding: 12px; }
}

/* 컨테이너가 600px 이상일 때 */
@container card (min-width: 600px) {
  .product-card {
    flex-direction: row;
    
    img { width: 240px; aspect-ratio: 1; }
    .content { padding: 24px; flex: 1; }
  }
}

/* :has()와 결합: 이미지 있는 카드만 다르게 */
@container (min-width: 400px) {
  .card:has(img) {
    grid-template-columns: 1fr 2fr;
  }
}

/* 컨테이너 단위 사용 (cqi, cqb, cqw, cqh) */
.card-title {
  font-size: clamp(1rem, 3cqi, 2rem); /* 컨테이너 너비의 3% */
}

View Transitions API: 부드러운 페이지 전환

/* CSS: 전환 이름 지정 */
.hero-image {
  view-transition-name: hero;
}

.page-title {
  view-transition-name: page-title;
}

/* 전환 애니메이션 커스터마이징 */
::view-transition-old(hero) {
  animation: 200ms ease-out fade-out;
}

::view-transition-new(hero) {
  animation: 300ms ease-in fade-in;
}

@keyframes fade-out {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.95); }
}

@keyframes fade-in {
  from { opacity: 0; transform: scale(1.05); }
  to   { opacity: 1; transform: scale(1); }
}
// JavaScript: View Transitions 활용
async function navigateToPage(url) {
  // 브라우저 지원 여부 확인
  if (!document.startViewTransition) {
    window.location.href = url;
    return;
  }
  
  const transition = document.startViewTransition(async () => {
    const response = await fetch(url);
    const html = await response.text();
    const parser = new DOMParser();
    const newDoc = parser.parseFromString(html, 'text/html');
    
    // 새 콘텐츠로 교체
    document.querySelector('main').replaceWith(
      newDoc.querySelector('main')
    );
    document.title = newDoc.title;
    history.pushState({}, '', url);
  });
  
  await transition.finished;
}

// SPA 라우팅 이벤트에 연결
document.querySelectorAll('a[data-spa]').forEach(link => {
  link.addEventListener('click', e => {
    e.preventDefault();
    navigateToPage(link.href);
  });
});

Scroll-driven Animations: JavaScript 없는 스크롤 애니메이션

/* 스크롤 타임라인 기반 애니메이션 */

/* 읽기 진행 표시 바 */
@keyframes progress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: #007bff;
  transform-origin: left;
  
  animation: progress linear;
  animation-timeline: scroll(root block); /* 루트 스크롤 기준 */
}

/* 스크롤 시 요소 나타나기 */
@keyframes slide-up {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}

.reveal-on-scroll {
  animation: slide-up ease-out both;
  animation-timeline: view();  /* 뷰포트에 들어올 때 */
  animation-range: entry 0% entry 30%; /* 진입 시작~30% 구간 */
}

/* 패럴랙스 효과 */
.parallax-image {
  animation: parallax linear;
  animation-timeline: scroll();
}

@keyframes parallax {
  from { transform: translateY(-20%); }
  to   { transform: translateY(20%); }
}

Scroll-State Queries: 스크롤 상태 CSS

/* Chrome 133+에서 지원 */

/* sticky 헤더가 고정되면 배경색 변경 */
.sticky-header {
  position: sticky;
  top: 0;
}

@container style(--is-stuck: true) {
  .sticky-header {
    background: rgba(255,255,255,0.9);
    backdrop-filter: blur(10px);
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  }
}

/* scroll-state() 직접 사용 */
.sticky-nav:stuck {
  background: rgba(0,0,0,0.8);
  color: white;
}

.snap-item:snapped {
  scale: 1.05;
  box-shadow: 0 8px 24px rgba(0,0,0,0.2);
}

CSS 레이어(@layer)로 스타일 충돌 관리

/* 레이어 순서 정의 (나중에 나온 레이어가 높은 우선순위) */
@layer reset, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; padding: 0; }
}

@layer base {
  body { font-family: system-ui, sans-serif; line-height: 1.6; }
  h1, h2, h3 { line-height: 1.2; }
}

@layer components {
  .btn {
    display: inline-flex;
    align-items: center;
    padding: 8px 16px;
    border-radius: 6px;
    cursor: pointer;
  }
  
  .btn-primary {
    background: #007bff;
    color: white;
  }
}

@layer utilities {
  .mt-auto { margin-top: auto; }
  .text-center { text-align: center; }
  /* utilities가 가장 높은 우선순위 */
}

최신 CSS 컬러 함수

/* oklch: 지각적으로 균일한 색상 공간 */
.primary { color: oklch(60% 0.15 240); }
.primary-light { color: oklch(80% 0.10 240); }
.primary-dark { color: oklch(40% 0.20 240); }

/* color-mix(): 두 색상 혼합 */
.blended {
  background: color-mix(in oklch, #007bff 30%, #ff6b6b);
}

/* 다크 모드: light-dark() 함수 (2025+) */
:root {
  color-scheme: light dark;
}

.element {
  background: light-dark(#ffffff, #1a1a1a);
  color: light-dark(#333333, #e0e0e0);
  /* prefers-color-scheme 미디어 쿼리 없이 처리 */
}

접근성(Accessibility) 필수 사항

/* 움직임 감소 설정 존중 */
@media (prefers-reduced-motion: reduce) {
  .animated-element {
    animation: none;
    transition: none;
  }
  
  .reading-progress {
    animation: none;
  }
}

/* 고대비 모드 지원 */
@media (prefers-contrast: high) {
  .btn { border: 2px solid currentColor; }
}

/* 포커스 스타일 (키보드 접근성) */
:focus-visible {
  outline: 3px solid #007bff;
  outline-offset: 2px;
  border-radius: 2px;
}

/* :focus (마우스) vs :focus-visible (키보드) 구분 */
.btn:focus { outline: none; }           /* 마우스 클릭 시 아웃라인 숨김 */
.btn:focus-visible { outline: 3px solid #007bff; } /* 키보드 탭 시 표시 */

정리

2025-2026년 CSS는 JavaScript와 전처리기에 의존하던 기능들을 네이티브로 지원합니다. CSS Nesting으로 Sass 의존도를 줄이고, Container Queries로 진정한 컴포넌트 기반 반응형 디자인을 구현하며, Scroll-driven Animations로 성능 좋은 스크롤 효과를 JavaScript 없이 구현할 수 있습니다. View Transitions API는 SPA 페이지 전환을 매끄럽게 만들어 줍니다. 이 기능들을 조합하면 가볍고 성능 좋은 현대적 웹 인터페이스를 만들 수 있습니다.