Files
2nd/10_Wiki/Topics/Coding/Frontend_CSS_Modern_Features.md
T
2026-05-09 21:08:02 +09:00

6.8 KiB


id: frontend-css-modern-features title: Modern CSS — :has() / Subgrid / Color spaces / @scope category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [frontend, css, vibe-coding] tech_stack: { language: "CSS", applicable_to: ["Frontend"] } applied_in: [] aliases: [:has, subgrid, OKLCH, color-mix, @scope, anchor positioning, popover]

Modern CSS

2024-2026 = CSS 황금기. :has() (parent selector), subgrid, OKLCH, @scope, anchor positioning, popover, container queries. JS 없이 가능한 게 늘어남.

📖 핵심 개념

  • :has(): parent selector (드디어).
  • Subgrid: nested grid 가 parent grid 따름.
  • OKLCH: perceptually uniform color.
  • @scope: CSS scope (CSS-in-JS 의 native).
  • Anchor positioning: tooltip / popover 자동 align.

💻 코드 패턴

:has() — parent selector

/* Card 안 image 가 있으면 padding 변경 */
.card:has(img) {
  padding: 0;
}

/* Form 안 :invalid input 가 있으면 button disable */
form:has(input:invalid) button {
  opacity: 0.5;
  pointer-events: none;
}

/* Sibling */
.card + .card:has(.featured) {
  border-color: gold;
}

/* :has + :not */
.list:has(:not(.read)) {
  background: yellow;
}

→ JS 없이 parent / sibling 반응. Chrome 105+ / Safari 15.4+ / FF 121+.

Subgrid

.parent {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 16px;
}

.parent > .nested {
  display: grid;
  grid-template-columns: subgrid;       /* parent 의 column 따름 */
  grid-column: 1 / -1;                   /* 모든 column */
}

→ Nested grid 가 parent column align.

OKLCH (modern color)

:root {
  --primary: oklch(60% 0.2 250);          /* L=60% C=0.2 H=250 */
  --primary-hover: oklch(55% 0.2 250);    /* darker */
  --primary-bg: oklch(95% 0.05 250);      /* lighter */
}

.button {
  background: var(--primary);
}
.button:hover {
  background: oklch(from var(--primary) calc(l - 5%) c h);
}

→ HSL 보다 perceptually uniform. Tailwind 4 가 default.

color-mix

.card {
  background: color-mix(in oklch, var(--brand) 80%, white);
  border-color: color-mix(in srgb, var(--brand) 50%, transparent);
}

→ 동적 color 변형.

@scope

@scope (.card) to (.card-content) {
  /* .card ~ .card-content 사이만 */
  h2 { color: red; }
  p { font-size: 14px; }
}
<div class="card">
  <h2>...</h2>           <!-- styled -->
  <div class="card-content">
    <h2>...</h2>          <!-- not styled (out of scope) -->
  </div>
</div>

→ CSS module / CSS-in-JS 의 native 대안.

Anchor positioning

.tooltip {
  position-anchor: --anchor-1;  /* anchor name */
  position: absolute;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 0;
}

.button {
  anchor-name: --anchor-1;
}
<button class="button">Hover me</button>
<div class="tooltip">Tooltip text</div>

→ JS positioning 없이 자동 align. Chrome 125+.

Popover API

<button popovertarget="my-popover">Open</button>
<div id="my-popover" popover>
  <p>Popover content</p>
  <button popovertarget="my-popover" popovertargetaction="hide">Close</button>
</div>
[popover] {
  border: none;
  border-radius: 8px;
  padding: 1rem;
}

[popover]::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

→ Native modal / dropdown. 모든 modern browser.

CSS nesting

/* Modern — Sass-like */
.card {
  padding: 16px;
  
  & .title {
    font-size: 24px;
    
    &:hover {
      color: blue;
    }
  }
  
  &:has(img) {
    padding: 0;
  }
}

→ Sass / PostCSS 없이.

Logical properties (RTL friendly)

.card {
  /* 옛 */
  margin-left: 16px;
  padding-right: 8px;
  
  /* 새 — RTL 자동 */
  margin-inline-start: 16px;
  padding-inline-end: 8px;
}

Cascade layers (@layer)

@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; }
}

@layer components {
  .button { ... }
}

@layer utilities {
  .mt-4 { margin-top: 1rem; }
}

→ Specificity 충돌 해결 — layer 가 우선순위.

Container queries (위 별 문서)

.container { container-type: inline-size; }

@container (min-width: 400px) { ... }

Aspect ratio

.video { aspect-ratio: 16 / 9; width: 100%; }
.avatar { aspect-ratio: 1; height: 50px; }

clamp / min / max

.responsive-font {
  font-size: clamp(1rem, 2vw, 2rem);  /* min 1rem, max 2rem, 2vw 사이 */
}

.container {
  width: min(90%, 1200px);
}

CSS variables + dynamic

.button {
  --hue: 220;
  background: oklch(60% 0.2 var(--hue));
}

.button.warning { --hue: 30; }
.button.danger { --hue: 0; }

Scrollbar gutter

html { scrollbar-gutter: stable; }

→ Scrollbar 가 layout 안 흔들림.

text-wrap: balance / pretty

h1 { text-wrap: balance; }   /* 균등 line break */
p { text-wrap: pretty; }      /* 마지막 줄 홀로 안 됨 */

accent-color

:root { accent-color: oklch(60% 0.2 250); }
/* Form element (checkbox, radio) 자동 brand */

color-scheme

:root {
  color-scheme: light dark;
}

→ Browser 가 system 따라 자동 dark mode (form element, scrollbar).

Container query units (위 문서)

.text { font-size: 5cqi; }

print stylesheet

@media print {
  .no-print { display: none; }
  body { font-size: 12pt; }
  
  a::after { content: " (" attr(href) ")"; }  /* URL 표시 */
  
  .page-break { page-break-after: always; }
}

Browser support 검사

caniuse.com
mdn.dev/learn 

새 기능 = polyfill / fallback 디자인.
@supports (color: oklch(60% 0.2 250)) {
  .button { background: oklch(...); }
}

@supports not (color: oklch(60% 0.2 250)) {
  .button { background: hsl(...); }  /* fallback */
}

Tailwind 4

- OKLCH default
- Container query @container
- :has() variants
- @scope
- 모든 modern feature 친화

🤔 의사결정 기준

기능 사용
Parent selector :has()
Nested grid align Subgrid
색 계산 OKLCH + color-mix
Component scope @scope
Tooltip / popover Anchor + Popover API
CSS-in-JS 대안 @scope + cascade layers

안티패턴

  • JS 가 모든 거 — CSS 가능한데: :has + container 가 더 빠름.
  • CSS-in-JS 무 reason: @scope 가 native.
  • HSL 만 + brand 변형: OKLCH 가 perceptual.
  • !important 남발: cascade layers.
  • Browser support 무 fallback: @supports.
  • Tooltip JS positioning + 옛 ancho-positioning 가능: native.

🤖 LLM 활용 힌트

  • :has() / Subgrid / Container query / @scope = 새 standards.
  • OKLCH > HSL.
  • Native popover + anchor = JS 줄임.
  • Tailwind 4 가 modern features 친화.

🔗 관련 문서