[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
---
|
||||
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
|
||||
```css
|
||||
/* 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
|
||||
```css
|
||||
.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)
|
||||
```css
|
||||
: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
|
||||
```css
|
||||
.card {
|
||||
background: color-mix(in oklch, var(--brand) 80%, white);
|
||||
border-color: color-mix(in srgb, var(--brand) 50%, transparent);
|
||||
}
|
||||
```
|
||||
|
||||
→ 동적 color 변형.
|
||||
|
||||
### @scope
|
||||
```css
|
||||
@scope (.card) to (.card-content) {
|
||||
/* .card ~ .card-content 사이만 */
|
||||
h2 { color: red; }
|
||||
p { font-size: 14px; }
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<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
|
||||
```css
|
||||
.tooltip {
|
||||
position-anchor: --anchor-1; /* anchor name */
|
||||
position: absolute;
|
||||
top: anchor(bottom);
|
||||
left: anchor(center);
|
||||
translate: -50% 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
anchor-name: --anchor-1;
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<button class="button">Hover me</button>
|
||||
<div class="tooltip">Tooltip text</div>
|
||||
```
|
||||
|
||||
→ JS positioning 없이 자동 align. Chrome 125+.
|
||||
|
||||
### Popover API
|
||||
```html
|
||||
<button popovertarget="my-popover">Open</button>
|
||||
<div id="my-popover" popover>
|
||||
<p>Popover content</p>
|
||||
<button popovertarget="my-popover" popovertargetaction="hide">Close</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
[popover] {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[popover]::backdrop {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
```
|
||||
|
||||
→ Native modal / dropdown. 모든 modern browser.
|
||||
|
||||
### CSS nesting
|
||||
```css
|
||||
/* Modern — Sass-like */
|
||||
.card {
|
||||
padding: 16px;
|
||||
|
||||
& .title {
|
||||
font-size: 24px;
|
||||
|
||||
&:hover {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(img) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Sass / PostCSS 없이.
|
||||
|
||||
### Logical properties (RTL friendly)
|
||||
```css
|
||||
.card {
|
||||
/* 옛 */
|
||||
margin-left: 16px;
|
||||
padding-right: 8px;
|
||||
|
||||
/* 새 — RTL 자동 */
|
||||
margin-inline-start: 16px;
|
||||
padding-inline-end: 8px;
|
||||
}
|
||||
```
|
||||
|
||||
### Cascade layers (@layer)
|
||||
```css
|
||||
@layer reset, base, components, utilities;
|
||||
|
||||
@layer reset {
|
||||
* { margin: 0; }
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.button { ... }
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.mt-4 { margin-top: 1rem; }
|
||||
}
|
||||
```
|
||||
|
||||
→ Specificity 충돌 해결 — layer 가 우선순위.
|
||||
|
||||
### Container queries (위 별 문서)
|
||||
```css
|
||||
.container { container-type: inline-size; }
|
||||
|
||||
@container (min-width: 400px) { ... }
|
||||
```
|
||||
|
||||
### Aspect ratio
|
||||
```css
|
||||
.video { aspect-ratio: 16 / 9; width: 100%; }
|
||||
.avatar { aspect-ratio: 1; height: 50px; }
|
||||
```
|
||||
|
||||
### clamp / min / max
|
||||
```css
|
||||
.responsive-font {
|
||||
font-size: clamp(1rem, 2vw, 2rem); /* min 1rem, max 2rem, 2vw 사이 */
|
||||
}
|
||||
|
||||
.container {
|
||||
width: min(90%, 1200px);
|
||||
}
|
||||
```
|
||||
|
||||
### CSS variables + dynamic
|
||||
```css
|
||||
.button {
|
||||
--hue: 220;
|
||||
background: oklch(60% 0.2 var(--hue));
|
||||
}
|
||||
|
||||
.button.warning { --hue: 30; }
|
||||
.button.danger { --hue: 0; }
|
||||
```
|
||||
|
||||
### Scrollbar gutter
|
||||
```css
|
||||
html { scrollbar-gutter: stable; }
|
||||
```
|
||||
|
||||
→ Scrollbar 가 layout 안 흔들림.
|
||||
|
||||
### text-wrap: balance / pretty
|
||||
```css
|
||||
h1 { text-wrap: balance; } /* 균등 line break */
|
||||
p { text-wrap: pretty; } /* 마지막 줄 홀로 안 됨 */
|
||||
```
|
||||
|
||||
### accent-color
|
||||
```css
|
||||
:root { accent-color: oklch(60% 0.2 250); }
|
||||
/* Form element (checkbox, radio) 자동 brand */
|
||||
```
|
||||
|
||||
### color-scheme
|
||||
```css
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
```
|
||||
|
||||
→ Browser 가 system 따라 자동 dark mode (form element, scrollbar).
|
||||
|
||||
### Container query units (위 문서)
|
||||
```css
|
||||
.text { font-size: 5cqi; }
|
||||
```
|
||||
|
||||
### print stylesheet
|
||||
```css
|
||||
@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 디자인.
|
||||
```
|
||||
|
||||
```css
|
||||
@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 친화.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Frontend_Container_Queries]]
|
||||
- [[Frontend_Tailwind_Architecture]]
|
||||
- [[Frontend_Animation_Motion]]
|
||||
Reference in New Issue
Block a user