필사 모드: Web Standards & CSS 2026 — Container Queries / View Transitions / Popover / Anchor Positioning / Scroll-driven Animations Deep Dive
English1. The 2026 Web Platform — Standards That Finally Arrived
A decade ago we said "this will soon be standard" while installing polyfills. In 2026 we live in the era where **standards are now everyday.**
Right now, Chrome 130+, Safari 18+, and Firefox 130+ all stably support:
- **Container Queries** — media queries for the component era
- **View Transitions API** — single-page version (2023) plus cross-document (2024)
- **Popover API** — native modals (stabilized across all browsers in 2024)
- **Anchor Positioning** — Chrome 125 (April 2024), Safari/Firefox in 2025
- **Scroll-driven Animations** — Chrome 115 (July 2023), Safari/Firefox in 2025
- **CSS Nesting** — stable since 2023
- **`:has()` selector** — universal support
- **`color-mix()` / `light-dark()`** — universal support
- **`@scope` / `@layer`** — supported in all three engines
- **Subgrid** — Chrome 117, Safari 16, Firefox 71 (universal)
- **Speculation Rules API** — prerender future pages
- **WebGPU / WebTransport** — stable
The answer to "what should I use if I start a new project today?" has finally become simple. **Just use the standards.** This article walks through the new baseline one chapter at a time.
> Not a single line in this article requires a polyfill. Everything targets the latest stable releases of 2026 evergreen browsers.
2. Container Queries — Media Queries for the Component Era
Why Media Queries Are Not Enough
Media queries are **viewport-based**. But components live inside sidebars, modals, card grids, side panels. The same card shrinks in a sidebar and stretches in main content. The viewport is identical. Only the container width differs.
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1.5rem;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}
Three container-type values
| Value | Meaning |
| --- | --- |
| `inline-size` | Query inline (horizontal) size only — most common |
| `size` | Query both axes — performance cost is higher |
| `normal` | Not a container (default) |
`size` must prevent children from influencing parent height, which has cost. In 99% of cases, `inline-size` is enough.
Container Query Units
.hero-title {
font-size: clamp(1.5rem, 5cqi, 3rem);
}
`cqi` (container query inline-size), `cqb` (block-size), `cqw`, `cqh`, `cqmin`, `cqmax`. They are the container-scoped versions of `vw` / `vh`.
Real-world Pattern
.product-grid {
container-type: inline-size;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
gap: 1rem;
}
@container (min-width: 900px) {
.product-card .image {
aspect-ratio: 4 / 3;
}
}
@container (max-width: 600px) {
.product-card .price {
font-size: 0.875rem;
}
}
The same card adapts whether it sits in a modal, sidebar, or main column. **Components finally become real components.**
3. View Transitions API — Single-page to Cross-document (2024)
Same-document version (2023)
When state changes, the browser produces a smooth transition automatically.
function updateList(newItems) {
if (!document.startViewTransition) {
renderList(newItems)
return
}
document.startViewTransition(() => renderList(newItems))
}
::view-transition-old(root) {
animation: fade-out 200ms ease-out;
}
::view-transition-new(root) {
animation: fade-in 200ms ease-in;
}
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }
Named transitions
Animate specific elements independently:
.product-card .image {
view-transition-name: product-image;
}
::view-transition-old(product-image),
::view-transition-new(product-image) {
animation-duration: 400ms;
}
Clicking a product card to navigate to the detail page can produce a **native-mobile-app-style hero zoom** automatically.
Cross-document version (2024)
This is the real revolution. **MPAs (multi-page apps) get smooth page transitions too.**
@view-transition {
navigation: auto;
}
.hero-image {
view-transition-name: hero;
}
If the previous page's `.hero-image` and the new page's `.hero-image` share the same name, the browser transitions between them automatically. No SPA router. No Next.js Link. **A plain anchor click is enough.**
Why MPA Transitions Matter
One of the biggest reasons we adopted SPAs for the last decade was "smooth page transitions". The cross-document version of View Transitions removes that reason. Next.js, Astro, and SvelteKit all leverage it automatically.
Caveats
- User input can be momentarily blocked during the transition — keep it short (200~300ms)
- Provide a fallback for users with `prefers-reduced-motion: reduce`
- `view-transition-name` must be **unique on the page**
4. Popover API (2024) — Native Modals
Why We Always Failed at Modals
- Focus trap
- Close on ESC
- Close on outside click
- Backdrop
- z-index hell
- Accessibility (role, aria, focus management)
The **browser does all of it** for you. Since 2024.
With just this:
- Closes on ESC
- Closes on outside click (auto mode)
- Focus moves automatically
- Always rendered in the top layer (z-index does not matter)
Two popover modes
| Value | Behavior |
| --- | --- |
| `auto` (default) | Closes on ESC, outside click, or when another popover opens |
| `manual` | Only closes explicitly — toasts, notifications |
Backdrop styling
[popover]::backdrop {
background: oklch(0% 0 0 / 0.6);
backdrop-filter: blur(8px);
}
Difference from `<dialog>`
`<dialog>` has form integration and a modal/non-modal distinction. Popover is lighter and applies to any element. Generally:
- **Full modal (form submission etc.)** → use `<dialog>`
- **Menus, tooltips, card hover** → use popover
5. Anchor Positioning (Chrome 125, April 2024) — Tooltip/Popover Placement
The Old Nightmare
To use a single tooltip library you installed Floating UI (formerly Popper.js), detected viewport collisions, listened to scroll events, attached a ResizeObserver. It was insane.
Now:
Tooltip content
#tooltip {
position-anchor: --my-button;
top: anchor(bottom);
left: anchor(center);
translate: -50% 0;
}
The browser tracks the trigger element's position automatically. Whether you scroll or resize, the tooltip follows.
position-try-fallbacks — Automatic viewport collision avoidance
#tooltip {
position-anchor: --my-button;
position-area: bottom;
position-try-fallbacks: top, right, left;
}
Default: show below. If space is tight, try top, then right, then left automatically. **Without a single line of Floating UI.**
position-area — 9-cell shorthand
#tooltip {
position-area: top span-all;
}
Use `top`, `bottom`, `left`, `right`, `center`, `span-all`, `start`, `end`. Keep the 9-cell grid in mind and it becomes intuitive.
What Does Not Work
- One element referencing multiple anchors simultaneously (under review in CSS Anchor Positioning Level 2)
- Only works for `position: fixed` or `absolute`
The Fate of Floating UI
Still valid — advanced collision detection and arrow auto-positioning remain library territory. But 80% of simple tooltips and popovers are now solvable with CSS alone.
6. Scroll-driven Animations (Chrome 115) — Scroll Effects Without JS
Two scroll timelines
| Timeline | Meaning |
| --- | --- |
| `scroll()` | Overall scroll progress of the container |
| `view()` | Progress of an element crossing the viewport |
scroll() — Progress indicator
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4px;
background: oklch(70% 0.2 250);
transform-origin: left;
animation: grow-progress linear;
animation-timeline: scroll();
}
Scrolling fills it in automatically. Not a single line of JavaScript.
view() — Fade-in on viewport entry
@keyframes fade-in {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
.card {
animation: fade-in linear;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
Start when the element enters the bottom of the viewport, end when 40% visible. No Intersection Observer required.
prefers-reduced-motion
@media (prefers-reduced-motion: no-preference) {
.card {
animation: fade-in linear;
animation-timeline: view();
}
}
Do not forget accessibility.
Performance
Scroll-driven animations run on the **compositor thread**, unlike JavaScript scroll handlers that block the main thread. 60fps is the baseline; 120fps is achievable.
7. CSS Nesting (2023) — Nest Without Sass
Standard syntax
.card {
padding: 1rem;
border-radius: 0.5rem;
& .title {
font-size: 1.5rem;
font-weight: 600;
}
& .description {
color: oklch(50% 0 0);
}
&:hover {
transform: translateY(-2px);
}
@media (min-width: 768px) {
padding: 1.5rem;
}
}
Difference from Sass is minor. You need `&` more often (mandatory in early 2023, relaxed later).
Differences from Sass
- **Variables** are not standard → use CSS Custom Properties (`--var`)
- **Mixins** still under review
- **`@extend`** unlikely to be standardized
Migration
/* Before (Sass) */
.btn {
&.primary { background: blue; }
&.danger { background: red; }
}
/* After (native CSS nesting) */
.btn {
&.primary { background: oklch(60% 0.2 250); }
&.danger { background: oklch(60% 0.25 25); }
}
Works almost as-is.
8. :has() — The Parent Selector
"CSS Has No Parent Selector" Is Over
/* Different layout when a card has an image */
.card:has(img) {
display: grid;
grid-template-columns: 1fr 2fr;
}
/* Disable submit button when any input is invalid */
form:has(input:invalid) button[type="submit"] {
opacity: 0.5;
pointer-events: none;
}
/* Dark mode unless the html has a "light" class */
html:not(.light) {
color-scheme: dark;
}
Real-world patterns
**1) Parent-child joint condition**
.dropdown:has(:focus-visible) {
outline: 2px solid currentColor;
}
**2) Sibling-based styling**
label:has(+ input:required)::after {
content: ' *';
color: oklch(60% 0.25 25);
}
**3) Empty state handling**
.list:not(:has(.list-item)) {
display: none;
}
Performance
`:has()` is not brute force — it has invalidation tracking. Practical even on very large DOMs.
9. color-mix() / light-dark() — Color Arithmetic
color-mix() — Mix two colors
.btn {
background: color-mix(in oklch, blue, white 20%);
}
.btn:hover {
background: color-mix(in oklch, blue, white 30%);
}
Specify a color space with `in oklch` / `in lab` / `in srgb` / `in hsl`. Generally `oklch` is the most natural (a perceptually uniform space).
Combining with Design Tokens
:root {
--brand: oklch(60% 0.2 250);
}
.btn {
background: var(--brand);
}
.btn:hover {
background: color-mix(in oklch, var(--brand), white 15%);
}
.btn:active {
background: color-mix(in oklch, var(--brand), black 15%);
}
No need to predefine every color variant — **derive everything from one brand color**.
light-dark() — Dark mode in one line
:root {
color-scheme: light dark;
--bg: light-dark(white, oklch(15% 0 0));
--fg: light-dark(oklch(15% 0 0), white);
}
body {
background: var(--bg);
color: var(--fg);
}
No need for two `prefers-color-scheme` blocks. **But you must declare `color-scheme: light dark`** for it to work.
Why OKLCH Is Becoming Standard
`#hex` or `rgb()` lives in the RGB space, which does not match human perception. `oklch(60% 0.2 250)` gives:
- L = lightness (0~100%)
- C = chroma (0~0.4)
- H = hue (0~360)
Color interpolation is natural, lightness changes are intuitive. In 2026, design systems are de-facto OKLCH-first.
10. @scope / @layer — Encapsulation and Cascade
@scope — Real CSS scoping
@scope (.card) to (.card-content) {
h2 {
font-size: 1.25rem;
}
}
Applies inside `.card` but **not** below `.card-content`. A scope with a **lower boundary**.
@scope (.dark-section) {
a {
color: white;
}
}
Only anchors inside `.dark-section` become white.
@layer — Cascade layers
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; }
}
@layer base {
body { font-family: system-ui; }
}
@layer components {
.btn { padding: 0.5rem 1rem; }
}
@layer utilities {
.text-center { text-align: center; }
}
The later the layer, the higher the priority. **Independent of specificity.**
**Why it matters**: Tailwind/UnoCSS utility-first triggered specificity wars. By placing utilities in `@layer utilities` last, they always win without specificity hacks.
Cascade Layers + Tailwind 4
Tailwind 4 uses `@layer` internally. Place your CSS in a separate layer and priority resolves cleanly.
11. Subgrid — Finally Universal Support
The problem
We want titles, descriptions, and buttons across cards to **align horizontally**. Children need to inherit the parent grid's grid lines.
Subgrid solution
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
`grid-template-rows: subgrid` inherits the parent's row structure. Titles align to the same height automatically.
What Subgrid Solved
- Title/image/button alignment across card grids
- Form label/input alignment
- Cell alignment in table-like components
- Consistent spacing across design systems
12. Speculation Rules — Prerender Future Pages
What it is
Pre-render the links the user is likely to click. Stronger than prefetch — DOM, JavaScript execution, all of it.
{
"prerender": [
{
"where": { "href_matches": "/articles/*" },
"eagerness": "moderate"
}
]
}
For every link starting with `/articles/`, when the user shows intent signals (hover, touch start), the page is prerendered in advance.
eagerness levels
| Value | Meaning |
| --- | --- |
| `immediate` | Right away — use with care |
| `eager` | When the link enters the viewport |
| `moderate` | 200ms hover or mousedown |
| `conservative` | Mousedown only |
Impact
Next-page LCP drops to **a few milliseconds**. From the user's perspective: "the next page is already there when I click."
Relationship to Next.js
Next.js App Router prefetches automatically (code and data). Speculation Rules prerender goes further — it pre-renders the **DOM** as well. Opt-in support exists in Next.js 15+.
13. WebGPU / WebTransport — Stable
WebGPU
- Stable in Chrome 113 (May 2023), Safari 18, Firefox 121+
- Shader language: WGSL
- Machine learning (transformers.js, MediaPipe, ONNX Runtime Web), advanced graphics, GPU compute
- Unlike WebGL, exposes compute shaders and modern GPU features
You can now run LLM inference (WebLLM), image processing, and simulations on GPU acceleration inside the browser. The era of running ChatGPT-like local models in a browser tab has truly arrived.
WebTransport
- HTTP/3-based bidirectional communication
- Successor to WebSocket (UDP-based reliability options)
- Multiple streams, no head-of-line blocking
- Real-time gaming, alternative to WebRTC data channels
const transport = new WebTransport('https://example.com/wt')
await transport.ready
const stream = await transport.createBidirectionalStream()
const writer = stream.writable.getWriter()
await writer.write(new TextEncoder().encode('hello'))
Other stabilized APIs
- **Custom Highlight API** — user-defined highlights beyond browser selection
- **`content-visibility: auto`** — skip rendering offscreen content
- **`Performance.measureUserAgentSpecificMemory`** — accurate memory measurement
- **Storage Access API**, **CHIPS** — third-party cookie era handling
14. Korean / Japanese Web Standards Content — Naver D2, html5.jp, mizchi
Korean
- **Naver D2** (d2.naver.com) — steady articles on web standards, browser internals, performance. The 2024-2025 analyses of View Transitions and Container Queries are excellent.
- **TOAST UI blog** — standards plus their own library implementation know-how
- **Frontender Kakao open chat / Frontier conference** — community catching up on standards in Korean
- **MDN Korean translation** — still partial, but newer features land in Korean faster than before
Japanese
- **html5.jp** — old site, but the fastest and most accurate spec translations
- **mizchi blog** — frontend trends and spec analysis. Critical voice that exposes the limits of standards
- **Web Developer Conference (Tokyo)** — annual standards-rich talks
- **CodeGrid** (Pixel Grid) — paid but in-depth CSS standards explanations
- **MDN Japanese** — near-simultaneous updates with English
English-speaking world
- **web.dev** — Google official, the canon for new feature guides
- **CSS Tricks** — still good (though the new article pace slowed from 2025)
- **Smashing Magazine** — many deep articles
- **State of CSS / State of JS** — annual trend surveys
- Blogs by **Una Kravets, Adam Argyle, Bramus** — first-hand info from Chrome DevRel
Japanese standards quirk
W3C Japan node activity is very active. Unlike Korea, Japan has a strong spec-translation culture, with many articles chewing over the spec sentence by sentence. If you want to understand the "why" of a standard, html5.jp is recommended reading.
15. Migration Strategy — Where to Start
Priorities
**Tier 1 — Adopt now (universal support)**
- Container Queries
- CSS Nesting
- `:has()`
- `color-mix()`, `light-dark()`
- `@layer`
- Subgrid
- Popover API
- View Transitions (same-document)
**Tier 2 — Gradual adoption (mostly supported, graceful fallback)**
- View Transitions (cross-document)
- Anchor Positioning
- Scroll-driven Animations
- `@scope`
**Tier 3 — Opt-in (specific scenarios)**
- Speculation Rules API
- WebGPU (ML/graphics)
- WebTransport (real-time)
Progressive enhancement pattern
.tooltip {
/* fallback: basic absolute positioning */
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
}
@supports (position-anchor: --x) {
.tooltip {
position-anchor: --trigger;
top: anchor(bottom);
left: anchor(center);
transform: translateX(-50%);
}
}
Branch via `@supports`. Enhance if the new feature is there, fall back otherwise.
Design system migration checklist
1. **OKLCH color migration** — convert design tokens to `color-mix(in oklch, ...)` first
2. **Modal library to Popover API** — dialog for forms, popover otherwise
3. **Floating UI to Anchor Positioning** — start with simple tooltips
4. **Intersection Observer to Scroll-driven Animations** — fade-ins first
5. **Tailwind/Sass variables to `@layer` organization** — end of priority conflicts
6. **Responsive components to Container Queries** — replace media queries
7. **SPA router transitions to View Transitions (cross-document)** — gradual adoption
Common pitfalls
- `view-transition-name` must be unique on the page — use ID-based names for dynamic lists
- Anchor Positioning only works for `position: absolute/fixed`
- Always branch on `prefers-reduced-motion` for Scroll-driven Animations
- Popover's `popovertarget` is ID-based — be careful with dynamic IDs in React
- Container Queries enforce layout containment via `container-type` — some layouts may break
Conclusion — The 2026 baseline
Every feature in this article is a **2026 evergreen-browser baseline**. Not "advanced CSS" — just CSS. Before installing one more library, ask: "Isn't this in the standard?" The answer is usually yes.
> **In 2026, betting on standards is the winning bet.**
References
- MDN Web Docs — [CSS Container Queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries)
- MDN — [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
- MDN — [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API)
- MDN — [CSS Anchor Positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)
- MDN — [CSS Scroll-driven Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll-driven_animations)
- MDN — [CSS Nesting](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting)
- MDN — [`:has()` selector](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)
- MDN — [`color-mix()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix)
- MDN — [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark)
- MDN — [CSS `@scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope)
- MDN — [CSS Cascade Layers (`@layer`)](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer)
- MDN — [CSS Subgrid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid)
- MDN — [Speculation Rules API](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API)
- MDN — [WebGPU API](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)
- MDN — [WebTransport API](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API)
- MDN — [CSS Custom Highlight API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API)
- MDN — [`content-visibility`](https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility)
- web.dev — [Container queries land in stable browsers](https://web.dev/blog/cq-stable)
- web.dev — [Cross-document view transitions](https://developer.chrome.com/docs/web-platform/view-transitions/cross-document)
- web.dev — [Introducing the popover API](https://developer.chrome.com/blog/introducing-popover-api)
- web.dev — [CSS anchor positioning API](https://developer.chrome.com/blog/anchor-positioning-api)
- web.dev — [Scroll-driven animations](https://developer.chrome.com/docs/css-ui/scroll-driven-animations)
- web.dev — [Speculation Rules API](https://developer.chrome.com/docs/web-platform/prerender-pages)
- Bramus van Damme blog — [scroll-driven-animations.style](https://scroll-driven-animations.style/)
- Una Kravets blog — [una.im](https://una.im/)
- Adam Argyle — [nerdy.dev](https://nerdy.dev/)
- Naver D2 — [d2.naver.com](https://d2.naver.com/)
- TOAST UI blog — [ui.toast.com/weekly-pick](https://ui.toast.com/weekly-pick)
- html5.jp — [html5.jp](https://www.html5.jp/)
- mizchi blog — [zenn.dev/mizchi](https://zenn.dev/mizchi)
- CodeGrid — [www.codegrid.net](https://www.codegrid.net/)
- State of CSS — [stateofcss.com](https://stateofcss.com/)
- W3C CSS Working Group — [drafts.csswg.org](https://drafts.csswg.org/)
- Baseline (web platform feature status) — [web.dev/baseline](https://web.dev/baseline)
- Can I Use — [caniuse.com](https://caniuse.com/)
현재 단락 (1/450)
A decade ago we said "this will soon be standard" while installing polyfills. In 2026 we live in the...