Modern CSS
Pure native CSS for building interfaces — no preprocessors, no frameworks.
When to Use (and When NOT to)
Support statuses below are as of mid-2026. "Newly Baseline (date)" means all three engines ship it, but users on older browsers won't have it — keep graceful degradation. Verify anything critical on MDN or webstatus.dev.
| Use Freely (Baseline) |
Feature-Detect / Progressive Enhancement |
| CSS Grid, Subgrid, Flexbox |
Scroll-driven animations (Firefox: behind flag) |
| Container queries — size; style (newly Baseline May 2026) |
Scroll-state queries (Chromium-only) |
:has(), :is(), :where() |
::scroll-button(), ::scroll-marker (Chromium-only) |
CSS Nesting, @layer |
Customizable <select> (Chromium; Safari 27 announced) |
@scope (newly Baseline Dec 2025) |
@function, if() (Chromium-only) |
@property (typed custom props) |
sibling-index(), sibling-count() (no Firefox) |
oklch(), color-mix(), light-dark(), relative color |
Typed attr() beyond content (Chromium-only) |
@starting-style, transition-behavior: allow-discrete |
Cross-document view transitions (no Firefox) |
| View transitions — same-document (newly Baseline Oct 2025) |
interpolate-size, calc-size() (Chromium-only) |
| Anchor positioning (newly Baseline Jan 2026) |
popover="hint", interestfor (Chromium-only) |
Popover API, <dialog>, invoker commands (newly Baseline Dec 2025) |
text-wrap: pretty, text-box (no Firefox) |
field-sizing: content (newly Baseline Jun 2026) |
reading-flow (Chromium-only) |
text-wrap: balance, linear() easing |
closedby on <dialog> (no Safari) |
Logical properties, ::details-content |
Grid Lanes / masonry (Safari 26.4 only; flags elsewhere) |
|
random() (Safari 26.2+ only), @mixin (no browser yet) |
CRITICAL: The Modern Cascade
Understanding how styles resolve is the single most important concept in CSS. The additions of @layer and @scope fundamentally changed the cascade algorithm.
Style Resolution Order (highest priority wins):
┌─────────────────────────────────────────────────┐
│ 1. Transitions (active transition wins) │
│ 2. !important (user-agent > user > author; │
│ layer order INVERTS under !important) │
│ 3. Unlayered author styles (beat ALL layers) │
│ 4. @layer order (later layer > earlier layer) │
│ 5. Specificity (ID > class > element) │
│ 6. @scope proximity (closer root wins) │
│ 7. Source order (later > earlier) │
└─────────────────────────────────────────────────┘
Unlayered > Last layer > ... > First layer
(utilities) (reset)
Cascade layers (@layer) and scope proximity (@scope) are now more powerful than selector specificity. Define your layer order once (@layer reset, base, components, utilities;) and specificity wars disappear. Unlayered styles always beat layered styles — use this for overrides.
Quick Decision Trees
"How do I lay this out?"
Layout approach?
├─ 2D grid (rows + columns) → CSS Grid
│ ├─ Children must align across → Grid + Subgrid
│ └─ Waterfall / masonry → display: grid-lanes (not yet cross-browser)
├─ 1D row OR column → Flexbox
├─ Component adapts to container → Container Query + Grid/Flex
├─ Viewport-based responsiveness → @media range syntax
└─ Element sized to content → fit-content / min-content / stretch
"How do I style this state?"
Style based on what?
├─ Child/descendant presence → :has()
├─ Container size → @container (inline-size)
├─ Container custom property → @container style()
├─ Scroll position (stuck/snapped) → scroll-state() query
├─ Element's own custom property → if(style(...))
├─ Browser feature support → @supports
├─ User preference (motion/color) → @media (prefers-*)
└─ Multiple selectors efficiently → :is() / :where()
"How do I animate this?"
Animation type?
├─ Enter/appear on DOM → @starting-style + transition
├─ Exit/disappear (display:none) → transition-behavior: allow-discrete
├─ Animate to/from auto height → interpolate-size: allow-keywords
├─ Scroll-linked (parallax/reveal) → animation-timeline: scroll()/view()
├─ Page/view navigation → View Transitions API
├─ Custom easing (bounce/spring) → linear() function
└─ Always: respect user preference → @media (prefers-reduced-motion)
What CSS Replaced JavaScript For
| JavaScript Pattern |
CSS Replacement |
| Scroll position listeners |
Scroll-driven animations |
| IntersectionObserver for reveal |
animation-timeline: view() |
| Sticky header shadow toggle |
scroll-state(stuck: top) |
| Floating UI / Popper.js |
Anchor positioning |
| Carousel prev/next/dots |
::scroll-button(), ::scroll-marker |
| Auto-expanding textarea |
field-sizing: content |
| Staggered animation delays |
sibling-index() |
max-height: 9999px hack |
interpolate-size: allow-keywords |
| Parent element selection |
:has() |
| Theme toggle logic |
light-dark() + color-scheme |
| Tooltip/popover show/hide |
Popover API + invoker commands |
| Color manipulation functions |
color-mix(), relative color syntax |
For non-Baseline features, always feature-detect with @supports or use progressive enhancement. Check MDN or Baseline for current browser support.
Anti-Patterns (CRITICAL)
| Anti-Pattern |
Problem |
Fix |
Overusing !important |
Specificity arms race |
Use @layer for cascade control |
Deep nesting (.a .b .c .d) |
Fragile, DOM-coupled |
Flat selectors, @scope |
IDs for styling (#header) |
Too specific to override |
Classes (.header) |
@media for component layout |
Viewport-coupled, not reusable |
Container queries |
| JS scroll listeners for effects |
Janky, expensive |
Scroll-driven animations |
| JS for tooltip positioning |
Floating UI dependency |
Anchor positioning |
| JS for carousel controls |
Fragile, a11y issues |
::scroll-button, ::scroll-marker |
| JS for auto-expanding textarea |
Unnecessary complexity |
field-sizing: content |
max-height: 9999px for animation |
Wrong duration, janky |
interpolate-size: allow-keywords |
margin-left / padding-right |
Breaks in RTL/vertical |
Logical properties (margin-inline-start) |
rgba() with commas |
Legacy syntax |
rgb(r g b / a) space-separated |
appearance: none on selects |
Removes ALL functionality |
appearance: base-select |
| Preprocessor-only variables |
Can't change at runtime |
CSS custom properties |
| Preprocessor-only nesting |
Extra build step dependency |
Native CSS nesting |
| Preprocessor color functions |
Can't respond to context |
color-mix(), relative colors |
text-wrap: balance on paragraphs |
Performance-heavy |
Only headings/short text |
content-visibility above fold |
Delays LCP rendering |
Only off-screen sections |
Overusing will-change |
Wastes GPU memory |
Apply only to animating elements |
Reference Documentation
Open the reference file BEFORE writing code in its area — each contains the sharp edges and support caveats that are easy to get wrong.
| Read this |
Before doing |
| references/CASCADE.md |
Setting up stylesheet architecture, @layer ordering, @scope, nesting, resolving specificity conflicts |
| references/LAYOUT.md |
Any Grid/Subgrid/Flexbox layout, container queries, masonry, intrinsic sizing |
| references/SELECTORS.md |
Using :has(), :is()/:where(), :focus-visible, or modern pseudo-elements |
| references/COLOR.md |
Defining palettes, dark mode with light-dark(), color-mix(), relative color, OKLCH |
| references/TOKENS.md |
Building design tokens, animating custom properties (@property), @function, if(), math functions |
| references/ANIMATION.md |
Entry/exit animations, @starting-style, animating to auto, view transitions, custom easing |
| references/SCROLL.md |
Scroll-linked effects, sticky-header states, carousels — anything replacing scroll listeners |
| references/COMPONENTS.md |
Tooltips, dropdowns, dialogs, popovers, anchor positioning, styled <select>, form field sizing |
| references/PERFORMANCE.md |
content-visibility, typography tuning, logical properties, accessibility media queries, viewport units |
| references/CHEATSHEET.md |
Quick lookups: legacy→modern upgrades, @supports tests, units, syntax tables |
Sources
Official Specifications
Browser Vendor Blogs
Support Status (check these — support claims go stale fast)
1---2name: modern-css3description: Proactively apply when creating design systems, component libraries, or any frontend application. Triggers on CSS Grid, Subgrid, Flexbox, Container Queries, :has(), @layer, @scope, CSS nesting, @property, @function, if(), oklch, color-mix, light-dark, relative color, @starting-style, scroll-driven animations, view transitions, anchor positioning, popover, customizable select, content-visibility, logical properties, text-wrap, interpolate-size, clamp, field-sizing, modern CSS, CSS architecture, responsive design, dark mode, theming, design tokens, cascade layers. Use when writing CSS for any web project, choosing layout approaches, building responsive components, implementing dark mode or theming, creating animations or transitions, styling form elements, or modernizing legacy stylesheets. Modern CSS features and best practices for building interfaces with pure native CSS.4---5
6# Modern CSS
7
8Pure native CSS for building interfaces — no preprocessors, no frameworks.
9
10## When to Use (and When NOT to)
11
12Support statuses below are as of mid-2026. "Newly Baseline (date)" means all three engines ship it, but users on older browsers won't have it — keep graceful degradation. Verify anything critical on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) or [webstatus.dev](https://webstatus.dev/).
13
14| Use Freely (Baseline) | Feature-Detect / Progressive Enhancement |
15|---|---|
16| CSS Grid, Subgrid, Flexbox | Scroll-driven animations (Firefox: behind flag) |
17| Container queries — size; style (newly Baseline May 2026) | Scroll-state queries (Chromium-only) |
18| `:has()`, `:is()`, `:where()` | `::scroll-button()`, `::scroll-marker` (Chromium-only) |
19| CSS Nesting, `@layer` | Customizable `<select>` (Chromium; Safari 27 announced) |
20| `@scope` (newly Baseline Dec 2025) | `@function`, `if()` (Chromium-only) |
21| `@property` (typed custom props) | `sibling-index()`, `sibling-count()` (no Firefox) |
22| `oklch()`, `color-mix()`, `light-dark()`, relative color | Typed `attr()` beyond `content` (Chromium-only) |
23| `@starting-style`, `transition-behavior: allow-discrete` | Cross-document view transitions (no Firefox) |
24| View transitions — same-document (newly Baseline Oct 2025) | `interpolate-size`, `calc-size()` (Chromium-only) |
25| Anchor positioning (newly Baseline Jan 2026) | `popover="hint"`, `interestfor` (Chromium-only) |
26| Popover API, `<dialog>`, invoker commands (newly Baseline Dec 2025) | `text-wrap: pretty`, `text-box` (no Firefox) |
27| `field-sizing: content` (newly Baseline Jun 2026) | `reading-flow` (Chromium-only) |
28| `text-wrap: balance`, `linear()` easing | `closedby` on `<dialog>` (no Safari) |
29| Logical properties, `::details-content` | Grid Lanes / masonry (Safari 26.4 only; flags elsewhere) |
30| | `random()` (Safari 26.2+ only), `@mixin` (no browser yet) |
31
32## CRITICAL: The Modern Cascade
33
34Understanding how styles resolve is the single most important concept in CSS. The additions of `@layer` and `@scope` fundamentally changed the cascade algorithm.
35
36```
37Style Resolution Order (highest priority wins):
38┌─────────────────────────────────────────────────┐
39│ 1. Transitions (active transition wins) │
40│ 2. !important (user-agent > user > author; │
41│ layer order INVERTS under !important) │
42│ 3. Unlayered author styles (beat ALL layers) │
43│ 4. @layer order (later layer > earlier layer) │
44│ 5. Specificity (ID > class > element) │
45│ 6. @scope proximity (closer root wins) │
46│ 7. Source order (later > earlier) │
47└─────────────────────────────────────────────────┘
48
49Unlayered > Last layer > ... > First layer
50 (utilities) (reset)
51```
52
53Cascade layers (`@layer`) and scope proximity (`@scope`) are now more powerful than selector specificity. Define your layer order once (`@layer reset, base, components, utilities;`) and specificity wars disappear. Unlayered styles always beat layered styles — use this for overrides.
54
55## Quick Decision Trees
56
57### "How do I lay this out?"
58
59```
60Layout approach?
61├─ 2D grid (rows + columns) → CSS Grid
62│ ├─ Children must align across → Grid + Subgrid
63│ └─ Waterfall / masonry → display: grid-lanes (not yet cross-browser)
64├─ 1D row OR column → Flexbox
65├─ Component adapts to container → Container Query + Grid/Flex
66├─ Viewport-based responsiveness → @media range syntax
67└─ Element sized to content → fit-content / min-content / stretch
68```
69
70### "How do I style this state?"
71
72```
73Style based on what?
74├─ Child/descendant presence → :has()
75├─ Container size → @container (inline-size)
76├─ Container custom property → @container style()
77├─ Scroll position (stuck/snapped) → scroll-state() query
78├─ Element's own custom property → if(style(...))
79├─ Browser feature support → @supports
80├─ User preference (motion/color) → @media (prefers-*)
81└─ Multiple selectors efficiently → :is() / :where()
82```
83
84### "How do I animate this?"
85
86```
87Animation type?
88├─ Enter/appear on DOM → @starting-style + transition
89├─ Exit/disappear (display:none) → transition-behavior: allow-discrete
90├─ Animate to/from auto height → interpolate-size: allow-keywords
91├─ Scroll-linked (parallax/reveal) → animation-timeline: scroll()/view()
92├─ Page/view navigation → View Transitions API
93├─ Custom easing (bounce/spring) → linear() function
94└─ Always: respect user preference → @media (prefers-reduced-motion)
95```
96
97## What CSS Replaced JavaScript For
98
99| JavaScript Pattern | CSS Replacement |
100|---|---|
101| Scroll position listeners | Scroll-driven animations |
102| IntersectionObserver for reveal | `animation-timeline: view()` |
103| Sticky header shadow toggle | `scroll-state(stuck: top)` |
104| Floating UI / Popper.js | Anchor positioning |
105| Carousel prev/next/dots | `::scroll-button()`, `::scroll-marker` |
106| Auto-expanding textarea | `field-sizing: content` |
107| Staggered animation delays | `sibling-index()` |
108| `max-height: 9999px` hack | `interpolate-size: allow-keywords` |
109| Parent element selection | `:has()` |
110| Theme toggle logic | `light-dark()` + `color-scheme` |
111| Tooltip/popover show/hide | Popover API + invoker commands |
112| Color manipulation functions | `color-mix()`, relative color syntax |
113
114> For non-Baseline features, always feature-detect with `@supports` or use progressive enhancement. Check [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) or [Baseline](https://web.dev/baseline) for current browser support.
115
116## Anti-Patterns (CRITICAL)
117
118| Anti-Pattern | Problem | Fix |
119|---|---|---|
120| Overusing `!important` | Specificity arms race | Use `@layer` for cascade control |
121| Deep nesting (`.a .b .c .d`) | Fragile, DOM-coupled | Flat selectors, `@scope` |
122| IDs for styling (`#header`) | Too specific to override | Classes (`.header`) |
123| `@media` for component layout | Viewport-coupled, not reusable | Container queries |
124| JS scroll listeners for effects | Janky, expensive | Scroll-driven animations |
125| JS for tooltip positioning | Floating UI dependency | Anchor positioning |
126| JS for carousel controls | Fragile, a11y issues | `::scroll-button`, `::scroll-marker` |
127| JS for auto-expanding textarea | Unnecessary complexity | `field-sizing: content` |
128| `max-height: 9999px` for animation | Wrong duration, janky | `interpolate-size: allow-keywords` |
129| `margin-left` / `padding-right` | Breaks in RTL/vertical | Logical properties (`margin-inline-start`) |
130| `rgba()` with commas | Legacy syntax | `rgb(r g b / a)` space-separated |
131| `appearance: none` on selects | Removes ALL functionality | `appearance: base-select` |
132| Preprocessor-only variables | Can't change at runtime | CSS custom properties |
133| Preprocessor-only nesting | Extra build step dependency | Native CSS nesting |
134| Preprocessor color functions | Can't respond to context | `color-mix()`, relative colors |
135| `text-wrap: balance` on paragraphs | Performance-heavy | Only headings/short text |
136| `content-visibility` above fold | Delays LCP rendering | Only off-screen sections |
137| Overusing `will-change` | Wastes GPU memory | Apply only to animating elements |
138
139## Reference Documentation
140
141Open the reference file BEFORE writing code in its area — each contains the sharp edges and support caveats that are easy to get wrong.
142
143| Read this | Before doing |
144|------|---------|
145| [references/CASCADE.md](references/CASCADE.md) | Setting up stylesheet architecture, `@layer` ordering, `@scope`, nesting, resolving specificity conflicts |
146| [references/LAYOUT.md](references/LAYOUT.md) | Any Grid/Subgrid/Flexbox layout, container queries, masonry, intrinsic sizing |
147| [references/SELECTORS.md](references/SELECTORS.md) | Using `:has()`, `:is()`/`:where()`, `:focus-visible`, or modern pseudo-elements |
148| [references/COLOR.md](references/COLOR.md) | Defining palettes, dark mode with `light-dark()`, `color-mix()`, relative color, OKLCH |
149| [references/TOKENS.md](references/TOKENS.md) | Building design tokens, animating custom properties (`@property`), `@function`, `if()`, math functions |
150| [references/ANIMATION.md](references/ANIMATION.md) | Entry/exit animations, `@starting-style`, animating to `auto`, view transitions, custom easing |
151| [references/SCROLL.md](references/SCROLL.md) | Scroll-linked effects, sticky-header states, carousels — anything replacing scroll listeners |
152| [references/COMPONENTS.md](references/COMPONENTS.md) | Tooltips, dropdowns, dialogs, popovers, anchor positioning, styled `<select>`, form field sizing |
153| [references/PERFORMANCE.md](references/PERFORMANCE.md) | `content-visibility`, typography tuning, logical properties, accessibility media queries, viewport units |
154| [references/CHEATSHEET.md](references/CHEATSHEET.md) | Quick lookups: legacy→modern upgrades, `@supports` tests, units, syntax tables |
155
156## Sources
157
158### Official Specifications
159- [CSS Snapshot 2025](https://www.w3.org/TR/css-2025/) — W3C
160- [CSS Values and Units Level 5](https://www.w3.org/TR/css-values-5/) — `if()`, `random()`, `sibling-index/count()`
161- [CSS Functions and Mixins Level 1](https://www.w3.org/TR/css-mixins-1/) — `@function`, `@mixin`
162- [CSS Conditional Rules Level 5](https://www.w3.org/TR/css-conditional-5/) — Scroll-state queries
163- [CSS Anchor Positioning](https://www.w3.org/TR/css-anchor-position-1/)
164- [CSS Overflow Level 5](https://www.w3.org/TR/css-overflow-5/) — Scroll markers/buttons
165
166### Browser Vendor Blogs
167- [CSS Wrapped 2025](https://chrome.dev/css-wrapped-2025/) — Chrome DevRel
168- [Interop 2025 Review](https://webkit.org/blog/17808/interop-2025-review/) — WebKit
169- [What's New in Web UI (I/O 2025)](https://developer.chrome.com/blog/new-in-web-ui-io-2025-recap)
170
171### Support Status (check these — support claims go stale fast)
172- [MDN Web Docs: CSS](https://developer.mozilla.org/en-US/docs/Web/CSS)
173- [Baseline / webstatus.dev](https://webstatus.dev/)
174- [Can I use](https://caniuse.com/)