Contents
see List2025-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 페이지 전환을 매끄럽게 만들어 줍니다. 이 기능들을 조합하면 가볍고 성능 좋은 현대적 웹 인터페이스를 만들 수 있습니다.