[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
---
|
||||
id: web-anchor-positioning-css
|
||||
title: CSS Anchor Positioning / @scope / Speculation Rules
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [web, css, vibe-coding]
|
||||
tech_stack: { language: "CSS", applicable_to: ["Frontend"] }
|
||||
applied_in: []
|
||||
aliases: [CSS anchor, anchor positioning, @scope, speculation rules, prerender, modern CSS]
|
||||
---
|
||||
|
||||
# Modern CSS Features
|
||||
|
||||
> Floating UI / scoped CSS / prerender 의 native. **Anchor (Chrome 125+), @scope (Chrome 118+), Speculation Rules (Chrome 110+)**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- Floating element 의 native (anchor).
|
||||
- CSS-in-JS 의 scoped CSS native.
|
||||
- Speculative prerender / preload.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Anchor positioning
|
||||
```html
|
||||
<button id='trigger'>Open</button>
|
||||
<div id='tooltip' class='tooltip'>Hello</div>
|
||||
```
|
||||
|
||||
```css
|
||||
#trigger {
|
||||
anchor-name: --my-trigger;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
position-anchor: --my-trigger;
|
||||
|
||||
top: anchor(bottom); /* 매 anchor 의 bottom */
|
||||
left: anchor(center);
|
||||
|
||||
/* 또는 */
|
||||
position-area: bottom;
|
||||
}
|
||||
```
|
||||
|
||||
→ JS / Floating UI 없이 popover position.
|
||||
|
||||
### position-area (단순)
|
||||
```css
|
||||
.tooltip {
|
||||
position-anchor: --trigger;
|
||||
position-area: bottom;
|
||||
}
|
||||
```
|
||||
|
||||
→ "anchor 의 bottom" 식 declarative.
|
||||
|
||||
### Try alternatives (fallback)
|
||||
```css
|
||||
.tooltip {
|
||||
position-anchor: --trigger;
|
||||
position-area: bottom;
|
||||
|
||||
position-try-fallbacks:
|
||||
bottom right,
|
||||
top left,
|
||||
top right;
|
||||
}
|
||||
```
|
||||
|
||||
→ Viewport edge 시 다른 position.
|
||||
|
||||
### Popover + anchor
|
||||
```html
|
||||
<button popovertarget='my-pop' style='anchor-name: --btn'>Open</button>
|
||||
<div id='my-pop' popover='auto' style='position-anchor: --btn; position-area: bottom'>
|
||||
Content
|
||||
</div>
|
||||
```
|
||||
|
||||
→ Popover + anchor = native modal.
|
||||
|
||||
### @scope (CSS scoping)
|
||||
```html
|
||||
<div class='card'>
|
||||
<h2>Title</h2>
|
||||
<p>Body</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
@scope (.card) {
|
||||
h2 { color: blue; }
|
||||
p { font-size: 14px; }
|
||||
}
|
||||
```
|
||||
|
||||
→ `.card` 안 만 적용. CSS-in-JS 없이.
|
||||
|
||||
### @scope to (limit)
|
||||
```css
|
||||
@scope (.card) to (.card-children) {
|
||||
h2 { color: blue; }
|
||||
}
|
||||
```
|
||||
|
||||
→ `.card-children` 의 descendant 가 안 받음.
|
||||
|
||||
### CSS nesting (related)
|
||||
```css
|
||||
.card {
|
||||
padding: 16px;
|
||||
|
||||
& h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Speculation Rules (prerender)
|
||||
```html
|
||||
<script type='speculationrules'>
|
||||
{
|
||||
"prerender": [{
|
||||
"where": { "href_matches": "/products/*" },
|
||||
"eagerness": "moderate"
|
||||
}]
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
→ Hover 시 prerender. Click = instant.
|
||||
|
||||
### Eagerness
|
||||
```
|
||||
- conservative: explicit only (anchor click).
|
||||
- moderate: hover.
|
||||
- eager: visible link.
|
||||
- immediate: 매 link.
|
||||
```
|
||||
|
||||
→ Cost vs UX trade-off.
|
||||
|
||||
### Prefetch (lighter)
|
||||
```html
|
||||
<script type='speculationrules'>
|
||||
{
|
||||
"prefetch": [{
|
||||
"where": { "href_matches": "/articles/*" }
|
||||
}]
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
→ HTML / resource preload (no execute).
|
||||
|
||||
### Container queries
|
||||
```css
|
||||
.card {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
@container (min-width: 400px) {
|
||||
.card-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Component 의 size 따라 layout.
|
||||
|
||||
### Container query units
|
||||
```css
|
||||
.card {
|
||||
container-type: inline-size;
|
||||
container-name: card;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
font-size: 5cqw; /* 5% of card width */
|
||||
}
|
||||
```
|
||||
|
||||
→ `cqw, cqh, cqi, cqb` 가 container 의 width / height.
|
||||
|
||||
### View timeline
|
||||
```css
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.fade {
|
||||
animation: fade-in linear;
|
||||
animation-timeline: view();
|
||||
animation-range: entry 0% cover 30%;
|
||||
}
|
||||
```
|
||||
|
||||
→ Scroll 위치 따라 animation. JS 없이.
|
||||
|
||||
### Scroll timeline
|
||||
```css
|
||||
.progress {
|
||||
animation: progress-bar linear;
|
||||
animation-timeline: scroll();
|
||||
}
|
||||
|
||||
@keyframes progress-bar {
|
||||
from { width: 0%; }
|
||||
to { width: 100%; }
|
||||
}
|
||||
```
|
||||
|
||||
→ Page scroll 따라 progress bar.
|
||||
|
||||
### Color modern
|
||||
```css
|
||||
:root {
|
||||
--primary: oklch(0.7 0.15 270);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: oklch(from var(--primary) 0.6 c h); /* darker */
|
||||
}
|
||||
```
|
||||
|
||||
→ OKLCH = perceptual uniform color.
|
||||
|
||||
### color-mix()
|
||||
```css
|
||||
.button {
|
||||
background: color-mix(in oklch, var(--primary) 70%, white);
|
||||
}
|
||||
```
|
||||
|
||||
→ Mix 2 color.
|
||||
|
||||
### relative colors
|
||||
```css
|
||||
.button:hover {
|
||||
background: oklch(from var(--primary) calc(l * 0.8) c h);
|
||||
}
|
||||
```
|
||||
|
||||
→ 기존 color 의 lighter / darker 자동.
|
||||
|
||||
### :has() selector
|
||||
```css
|
||||
.card:has(img) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
form:has(input:invalid) {
|
||||
border-color: red;
|
||||
}
|
||||
```
|
||||
|
||||
→ Parent 가 child 따라 style. Game-changing.
|
||||
|
||||
### subgrid
|
||||
```css
|
||||
.parent {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
.child {
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
}
|
||||
```
|
||||
|
||||
→ Child 가 parent 의 grid 사용.
|
||||
|
||||
### Browser support
|
||||
```
|
||||
- Anchor: Chrome 125+, FF flag.
|
||||
- @scope: Chrome 118+.
|
||||
- Speculation Rules: Chrome 110+.
|
||||
- Container queries: 모든 modern.
|
||||
- :has(): 모든 modern.
|
||||
- View Timeline: Chrome 115+.
|
||||
- OKLCH: 모든 modern.
|
||||
- Subgrid: 모든 modern (Safari 16+).
|
||||
```
|
||||
|
||||
→ Caniuse 가 reality.
|
||||
|
||||
### Polyfill / fallback
|
||||
```css
|
||||
.tooltip {
|
||||
/* Fallback */
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
|
||||
/* Modern */
|
||||
position-anchor: --trigger;
|
||||
position-area: bottom;
|
||||
}
|
||||
```
|
||||
|
||||
→ Progressive enhancement.
|
||||
|
||||
### Use case
|
||||
```
|
||||
Anchor: tooltip, popover, dropdown.
|
||||
@scope: component-scoped CSS.
|
||||
Speculation: navigation 빠름.
|
||||
Container: responsive component.
|
||||
:has(): conditional layout.
|
||||
View Timeline: scroll-based animation.
|
||||
```
|
||||
|
||||
### vs Floating UI library
|
||||
```
|
||||
Floating UI:
|
||||
- JavaScript (3 KB).
|
||||
- 모든 browser.
|
||||
- 정밀 control.
|
||||
|
||||
Native anchor:
|
||||
- 0 JS.
|
||||
- Modern browser only.
|
||||
- CSS declarative.
|
||||
|
||||
→ Modern only = native.
|
||||
Cross-browser = library.
|
||||
```
|
||||
|
||||
### vs CSS-in-JS
|
||||
```
|
||||
CSS-in-JS:
|
||||
- JavaScript runtime.
|
||||
- Dynamic style.
|
||||
- 큰 bundle.
|
||||
|
||||
@scope:
|
||||
- Native CSS.
|
||||
- Build-time.
|
||||
- 작은.
|
||||
|
||||
→ Modern = @scope.
|
||||
```
|
||||
|
||||
### Production examples
|
||||
- **Astro / Next**: View Transitions.
|
||||
- **Tailwind 4**: Lightning CSS + container.
|
||||
- **Vercel docs**: Speculation Rules.
|
||||
- **Apple**: scroll animation.
|
||||
|
||||
### LLM / Cursor 도움
|
||||
```
|
||||
Modern CSS 가 LLM 의 baked.
|
||||
- Generate anchor positioning.
|
||||
- @scope 작성.
|
||||
- :has() pattern.
|
||||
|
||||
→ 작은 bundle + clean code.
|
||||
```
|
||||
|
||||
### 함정
|
||||
```
|
||||
- Anchor 가 Chrome 만: fallback 필수.
|
||||
- Container 의 layout 가 ambiguous: type 명시.
|
||||
- :has() 의 performance: 매우 큰 tree 가 slow.
|
||||
- View Timeline 의 timing 정밀 X.
|
||||
- Speculation Rules cost: prerender 가 page render.
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 작업 | 추천 |
|
||||
|---|---|
|
||||
| Floating UI | Anchor (modern) / Floating UI |
|
||||
| Component CSS | @scope |
|
||||
| Page navigation | Speculation Rules |
|
||||
| Responsive component | Container queries |
|
||||
| Conditional layout | :has() |
|
||||
| Scroll animation | View Timeline |
|
||||
| Color | OKLCH |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Anchor + no fallback**: Safari 깨짐.
|
||||
- **모든 페이지 prerender**: cost.
|
||||
- **Container 가 type 안**: ignore.
|
||||
- **:has() 큰 tree**: performance.
|
||||
- **CSS-in-JS + @scope 둘 다**: redundant.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Anchor positioning 가 Chrome 125+.
|
||||
- @scope 가 native CSS-in-JS.
|
||||
- Speculation Rules 가 prerender.
|
||||
- :has() 가 game-changing parent selector.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Frontend_CSS_Modern_Features]]
|
||||
- [[Frontend_Container_Queries]]
|
||||
- [[Web_Origin_Trial_Platform]]
|
||||
Reference in New Issue
Block a user