Files
2nd/10_Wiki/Topics/Coding/Web_Anchor_Positioning_CSS.md
T
2026-05-10 22:08:15 +09:00

7.0 KiB


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

<button id='trigger'>Open</button>
<div id='tooltip' class='tooltip'>Hello</div>
#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 (단순)

.tooltip {
  position-anchor: --trigger;
  position-area: bottom;
}

→ "anchor 의 bottom" 식 declarative.

Try alternatives (fallback)

.tooltip {
  position-anchor: --trigger;
  position-area: bottom;
  
  position-try-fallbacks:
    bottom right,
    top left,
    top right;
}

→ Viewport edge 시 다른 position.

Popover + anchor

<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)

<div class='card'>
  <h2>Title</h2>
  <p>Body</p>
</div>
@scope (.card) {
  h2 { color: blue; }
  p { font-size: 14px; }
}

.card 안 만 적용. CSS-in-JS 없이.

@scope to (limit)

@scope (.card) to (.card-children) {
  h2 { color: blue; }
}

.card-children 의 descendant 가 안 받음.

.card {
  padding: 16px;
  
  & h2 {
    font-size: 1.5rem;
  }
}

Speculation Rules (prerender)

<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)

<script type='speculationrules'>
{
  "prefetch": [{
    "where": { "href_matches": "/articles/*" }
  }]
}
</script>

→ HTML / resource preload (no execute).

Container queries

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

@container (min-width: 400px) {
  .card-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

→ Component 의 size 따라 layout.

Container query units

.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

@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

.progress {
  animation: progress-bar linear;
  animation-timeline: scroll();
}

@keyframes progress-bar {
  from { width: 0%; }
  to { width: 100%; }
}

→ Page scroll 따라 progress bar.

Color modern

: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()

.button {
  background: color-mix(in oklch, var(--primary) 70%, white);
}

→ Mix 2 color.

relative colors

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

→ 기존 color 의 lighter / darker 자동.

:has() selector

.card:has(img) {
  padding-top: 0;
}

form:has(input:invalid) {
  border-color: red;
}

→ Parent 가 child 따라 style. Game-changing.

subgrid

.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

.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.

🔗 관련 문서