Contents
see List2026년 CSS는 사상 최대 규모의 업데이트를 맞이했다. Sass 없이도 사용할 수 있는 네이티브 @mixin, 접근성을 자동으로 보장하는 contrast-color(), 드디어 커스터마이징이 가능해진 base-select, 그리고 DOM 구조를 CSS에서 직접 활용하는 sibling-index()/sibling-count()까지. JavaScript 의존도를 대폭 줄이는 CSS 신기능들을 실전 코드와 함께 살펴보자.
네이티브 CSS @mixin과 @apply
Sass의 킬러 기능이었던 mixin이 CSS에 네이티브로 도입되었다. 재사용 가능한 스타일 블록을 정의하고 어디서든 적용할 수 있다.
기본 사용법
/* mixin 정의 */
@mixin --reset-list {
margin: 0;
padding: 0;
list-style: none;
}
@mixin --flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin --visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* mixin 적용 */
.nav-list {
@apply --reset-list;
gap: 1rem;
}
.hero-section {
@apply --flex-center;
min-height: 100vh;
}
.sr-only {
@apply --visually-hidden;
}
매개변수가 있는 mixin
/* 매개변수를 받는 mixin */
@mixin --button(--bg, --color, --radius: 8px) {
background-color: var(--bg);
color: var(--color);
border-radius: var(--radius);
padding: 0.75rem 1.5rem;
border: none;
cursor: pointer;
font-weight: 600;
transition: opacity 0.2s ease;
}
@mixin --button:hover {
opacity: 0.85;
}
/* 매개변수 전달하여 적용 */
.btn-primary {
@apply --button(
--bg: #2563eb,
--color: #ffffff
);
}
.btn-danger {
@apply --button(
--bg: #dc2626,
--color: #ffffff,
--radius: 24px
);
}
.btn-outline {
@apply --button(
--bg: transparent,
--color: #1f2937
);
border: 2px solid currentColor;
}
contrast-color() - 자동 접근성 보장
contrast-color() 함수는 배경색의 밝기를 분석하여 WCAG AA 기준을 충족하는 텍스트 색상을 자동으로 선택한다.
/* 배경색에 따라 검정 또는 흰색 텍스트 자동 선택 */
.badge {
background-color: var(--badge-color);
color: contrast-color(var(--badge-color));
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.875rem;
}
/* 다양한 배경색에 적용 */
.badge-success { --badge-color: #22c55e; } /* -> 검정 텍스트 */
.badge-error { --badge-color: #dc2626; } /* -> 흰색 텍스트 */
.badge-warning { --badge-color: #facc15; } /* -> 검정 텍스트 */
.badge-info { --badge-color: #0ea5e9; } /* -> 흰색 텍스트 */
/* 테마 색상에 반응하는 버튼 */
:root {
--primary: #6366f1;
--secondary: #f59e0b;
}
.btn-themed {
background: var(--primary);
color: contrast-color(var(--primary));
/* 테마 색상이 바뀌어도 항상 가독성 보장 */
}
base-select - 커스터마이징 가능한 select
수년간 개발자들의 숙원이었던 select 요소의 완전한 스타일링이 가능해졌다.
/* opt-in으로 새 select 활성화 */
select {
appearance: base-select;
}
/* 전체 select 스타일링 */
select {
appearance: base-select;
font-size: 1rem;
padding: 0.75rem 1rem;
border: 2px solid #e5e7eb;
border-radius: 12px;
background: white;
color: #1f2937;
min-width: 200px;
transition: border-color 0.2s;
}
select:focus {
border-color: #6366f1;
outline: 3px solid rgba(99, 102, 241, 0.2);
}
/* 드롭다운 피커 스타일링 */
select::picker(select) {
background: white;
border: 1px solid #e5e7eb;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
padding: 0.5rem;
max-height: 300px;
overflow-y: auto;
}
/* 화살표 아이콘 커스터마이징 */
select::picker-icon {
content: url('data:image/svg+xml,...');
transition: rotate 0.2s;
}
select:open::picker-icon {
rotate: 180deg;
}
/* 개별 옵션 스타일링 */
select option {
padding: 0.75rem 1rem;
border-radius: 8px;
margin: 2px 0;
transition: background 0.15s;
}
select option:hover {
background: #f0f0ff;
}
select option:checked {
background: #6366f1;
color: white;
font-weight: 600;
}
sibling-index()와 sibling-count()
DOM 구조 정보를 CSS에서 직접 활용할 수 있다. nth-child보다 훨씬 유연하다.
/* 자식 요소의 인덱스를 활용한 순차 애니메이션 */
.card-grid > .card {
animation: fadeInUp 0.5s ease both;
/* 각 카드마다 0.1초씩 지연 */
animation-delay: calc(sibling-index() * 0.1s);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 전체 형제 수에 따른 반응형 레이아웃 */
.tab-list > .tab {
/* 탭 수에 따라 자동으로 너비 조절 */
flex-basis: calc(100% / sibling-count());
}
/* 인덱스 기반 그라데이션 */
.color-strip > span {
--progress: calc(sibling-index() / sibling-count());
background: color-mix(in oklch, #6366f1, #06b6d4 calc(var(--progress) * 100%));
width: 100%;
height: 4px;
}
/* 마지막 요소 감지를 sibling-count로 */
.list-item {
--is-last: calc(sibling-index() - sibling-count() + 1);
/* is-last가 0이면 마지막 요소 */
border-bottom: max(var(--is-last), 0) * 1px solid #e5e7eb;
}
corner-shape - 다양한 모서리 형태
/* 기존: 원형 모서리만 가능 */
.old-card {
border-radius: 16px; /* 항상 원형 */
}
/* CSS 2026: 다양한 모서리 형태 */
.squircle-card {
corner-shape: squircle;
border-radius: 24px;
/* iOS 스타일의 부드러운 사각형 */
}
.notch-badge {
corner-shape: notch;
border-radius: 8px;
/* 잘려나간 모서리 */
}
.scoop-card {
corner-shape: scoop;
border-radius: 20px;
/* 안쪽으로 파인 모서리 */
}
.bevel-button {
corner-shape: bevel;
border-radius: 12px;
/* 직선으로 깎인 모서리 */
}
MPA View Transitions
/* 페이지 간 전환 애니메이션 (Multi-Page Application) */
@view-transition {
navigation: auto;
}
/* 기본 페이지 전환 */
::view-transition-old(root) {
animation: fade-out 0.3s ease;
}
::view-transition-new(root) {
animation: fade-in 0.3s ease;
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
}
/* 특정 요소에 view-transition 이름 부여 */
.product-image {
view-transition-name: product-hero;
}
/* 상품 이미지 공유 요소 전환 */
::view-transition-old(product-hero) {
animation: scale-down 0.4s ease;
}
::view-transition-new(product-hero) {
animation: scale-up 0.4s ease;
}
브라우저 지원 현황 (2026년 4월 기준)
- @mixin/@apply - Chrome 128+, Firefox 131+, Safari 19.1+
- contrast-color() - Chrome 127+, Firefox 130+, Safari 19+
- base-select - Chrome 129+, Firefox 132+ (Safari 예정)
- sibling-index/count - Chrome 128+, Firefox 131+, Safari 19+
- corner-shape - Chrome 130+, Firefox 133+ (Safari 예정)
- MPA View Transitions - Chrome 126+, Firefox 130+ (Safari 예정)